![]() |
AW: PayPal Soap Anbindung
Leider ist es so, dass tatsächlich Embarcadero den Fehler (das Memory-Leak) verursacht.
Das Problem ist der folgende Code in der Funktion TSOAPDomConv.ConvertSOAPToObject aus Soap.OPToSOAPDomConv.pas ab Zeile 4768:
Delphi-Quellcode:
Ist auch in der aktuellen Delphi Version (Tokyo) noch vorhanden. Vermutlich gibt es auch noch weitere Stellen die ein ähnliches Problem haben.
function TSOAPDomConv.ConvertSOAPToObject(RootNode, Node: IXMLNode; AClass: TClass;
const URI, TypeName: InvString; ObjP: Pointer): TObject; // ... if Assigned(Obj) and LegalRef then begin if (NodeClass <> nil) and (NodeClass <> Obj.ClassType) then Obj.Free(); // Dieses Free hat gefehlt, die alte Instanz von Obj dangled nach dem Überschreiben Obj := NodeClass.Create; end // ... TObject ist nun mal nicht ref-counted :( Ich werde ein Ticket bei Embarcadero aufmachen. Gruss, Günther |
AW: PayPal Soap Anbindung
Dann sollte man die beiden Zeilen aber auch mit begin..end umschliessen.
|
AW: PayPal Soap Anbindung
Zitat:
Wenn man sich in der Methode den XML-Text vom RootNode anschaut sieht man folgendes:
Code:
Aufgrund von
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cc="urn:ebay:apis:CoreComponentTypes" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:ed="urn:ebay:apis:EnhancedDataTypes" xmlns:ebl="urn:ebay:apis:eBLBaseComponents" xmlns:ns="urn:ebay:api:PayPalAPI">
<SOAP-ENV:Header> <Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext" xsi:type="wsse:SecurityType"/> <RequesterCredentials xmlns="urn:ebay:api:PayPalAPI" xsi:type="ebl:CustomSecurityHeaderType"> <<<--------- Hier ist der Übertäter <Credentials xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:UserIdPasswordType"/> </RequesterCredentials> </SOAP-ENV:Header> <SOAP-ENV:Body id="_0"> <RefundTransactionResponse xmlns="urn:ebay:api:PayPalAPI"> <Timestamp xmlns="urn:ebay:apis:eBLBaseComponents">2018-06-14T11:41:34Z</Timestamp> <Ack xmlns="urn:ebay:apis:eBLBaseComponents">Failure</Ack> <CorrelationID xmlns="urn:ebay:apis:eBLBaseComponents">ac2341fd2df09</CorrelationID> <Errors xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:ErrorType"> <ShortMessage xsi:type="xs:string">Authentication/Authorization Failed</ShortMessage> <LongMessage xsi:type="xs:string">You do not have permissions to make this API call</LongMessage> <ErrorCode xsi:type="xs:token">10002</ErrorCode> <SeverityCode xmlns="urn:ebay:apis:eBLBaseComponents">Error</SeverityCode> </Errors> <Version xmlns="urn:ebay:apis:eBLBaseComponents">0.000000</Version> <Build xmlns="urn:ebay:apis:eBLBaseComponents">000000</Build> <RefundTransactionID/> </RefundTransactionResponse> </SOAP-ENV:Body></SOAP-ENV:Envelope>
Code:
wird ja in den Zeilen
xsi:type="ebl:CustomSecurityHeaderType"
Delphi-Quellcode:
die NodeClass auf CustomSecurityHeaderType gesetzt. Mit den bekannten Folgen.
GetElementType(ObjNode, NodeURI, NodeTypeName);
NodeClass := RemTypeRegistry.URIToClass(NodeURI, NodeTypeName, IsScalar); Also Workaround - welche negativen Auswirkungen das hat, muss gesondert untersucht werden - kannst du in der PayPalSvc-Unit ca. bei Zeile 28049 das folgende auskommentieren:
Delphi-Quellcode:
...
RemClassRegistry.RegisterXSClass(CustomSecurityHeaderType, 'urn:ebay:apis:eBLBaseComponents', 'CustomSecurityHeaderType'); ... Dadurch wird RemTypeRegistry.URIToClass nichts finden und NodeClass wird zu nil. Damit bleibt das Obj (=> RequesterCredentials) erhalten und das Speicherleck ist weg. |
AW: PayPal Soap Anbindung
Zitat:
Es hat glücklicherweise trotzdem funktioniert (ein sehr spezieller Fall.) |
AW: PayPal Soap Anbindung
Zitat:
Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben. |
AW: PayPal Soap Anbindung
Zitat:
Das Problem, fehlerhafte oder schlecht programmierten Code aus dem Framework ersetzen zu müssen, hatte ich auch schon öfters. Am Einfachsten ist es noch, wenn man von der betreffenden Klasse ableiten kann und die zu überschreibende Methode protected/public und virtual/dynamic ist. |
AW: PayPal Soap Anbindung
Zitat:
|
AW: PayPal Soap Anbindung
So hatte ich das auch verstanden.
|
AW: PayPal Soap Anbindung
Zitat:
Leider erweist sich diese Hoffnung als Trugschluss :( Die Superklasse TRIO von THTTPRIO hat zwar die definierte Eigenschaft Converter vom (Interface) Typ IOPConverter, THTTPRIO will aber dennoch den Typ Soap.TOPToSoapDomConvert. Was ist denn das für ein #&$**!!@ Design bitteschön??? Also kein "sauberer" Workaround möglich, es bleibt bei dem Patch (den ich jezt auch noch 2x pflegen muss, für Seattle und Tokyo, da andere Änderungen an Soap.OPToSoapDomConvert.pas vorgenommen wurden). So ein Saftladen!! :| P.S.: Für jeden den's interessiert, hier ist das Ticket bei Embarcadero: ![]() |
AW: PayPal Soap Anbindung
Zitat:
Der entscheidende Punkt ist das Freigeben der betreffenden Klasse in der überschriebenen ConvertSoapToNativeData-Methode.
Delphi-Quellcode:
unit Paypal.View;
interface uses System.SysUtils, System.Variants, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, Winapi.ActiveX, PayPalSvc, Soap.SOAPHTTPClient, Soap.InvokeRegistry, System.TypInfo, Soap.Rio, Soap.OPToSOAPDomConv, Xml.XMLIntf, Soap.SOAPConst, Soap.TypeTrans; type TfrmPayPal = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private public end; var frmPayPal: TfrmPayPal; implementation {$R *.dfm} type TMyConverter = class(Soap.OPToSOAPDomConv.TOPToSoapDomConvert) public procedure ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo; RootNode, Node: IXMLNode; Translate: Boolean); override; end; type PTObject = ^TObject; procedure TMyConverter.ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo; RootNode, Node: IXMLNode; Translate: Boolean); var TypeUri, TypeName: InvString; IsNull: Boolean; Obj, DataObj: TObject; P: Pointer; ID: InvString; begin Node := GetDataNode(RootNode, Node, ID); IsNull := NodeIsNull(Node); if TypeInfo.Kind = tkVariant then begin if IsNull then begin Variant(PVarData(DataP)^) := NULL; end else ConvertSoapToVariant(Node, DataP); end else if TypeInfo.Kind = tkDynArray then begin P := DataP; P := ConvertSoapToNativeArray(P, TypeInfo, RootNode, Node); Pointer(DataP^) := P end else if TypeInfo.Kind = tkClass then begin Obj := ConvertSOAPToObject(RootNode, Node, GetTypeData(TypeInfo).ClassType, TypeUri, TypeName, DataP); DataObj := PTObject(DataP)^; if Assigned(Obj) and Assigned(DataObj) and (Obj.ClassType <> DataObj.ClassType) then begin DataObj.Free; // <--- hier geben wir das Objekt frei, wenn die Class Types verschieden sind! end; PTObject(DataP)^ := Obj end else begin if Translate then begin if not TypeTranslator.CastSoapToNative(TypeInfo, GetNodeAsText(Node), DataP, IsNull) then raise ESOAPDomConvertError.CreateFmt(STypeMismatchInParam, [Node.nodeName]); end; end; end; procedure TfrmPayPal.Button1Click(Sender: TObject); var Rio: THTTPRIO; PaypalInterface: PayPalAPIInterface; Sec: RequesterCredentials; Request: RefundTransactionReq; Response: RefundTransactionResponse; begin CoInitializeEx(nil, 0); try Rio := THTTPRIO.Create(nil); Rio.Converter := TMyConverter.Create(Rio); PaypalInterface := GetPayPalAPIInterface(False, 'https://api-3t.sandbox.paypal.com/2.0/', Rio); Sec := RequesterCredentials.Create; Sec.Credentials := UserIdPasswordType.Create; Sec.Credentials.Username := 'hello world'; Sec.Credentials.Password := 'hello world'; Sec.Credentials.Signature := 'hello world'; Rio.SOAPHeaders.Send(Sec); Rio.SOAPHeaders.SetOwnsSentHeaders(True); Request := RefundTransactionReq.Create; try Request.RefundTransactionRequest := RefundTransactionRequest.Create; Response := PaypalInterface.RefundTransaction(Request); if Assigned(Response) then begin try { Hier machen wir was Kluges mit dem Response-Objekt } finally Response.Free; end; end; finally Request.Free; end; finally PaypalInterface := nil; // nur um zu verdeutlichen, dass hier Schluss ist! CoUnInitialize; end; end; end. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:57 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz