Hallo Zusammen,
Wenn, Ihr mit Eurem merkwürdigen Hin- und Her-Geplänkel hinsichtlich des angemessenen Tons in technischen Foren fertig seid, dann können wir uns ja mal wieder mit dem eigentlichen Problem beschäftigen (wer Lesen kann ist übrigens immer klar im Vorteil).
Derjenige, der das 3 Jahre alte Thema wieder ausgegraben hatte und nach einer Lösung sucht bin ich. Ich hatte eine entsprechende Anfrage in der
FB Delphi Developer Group gestellt:
Memory leak with Delphi 10 Seattle generated Soap from PayPal WSDL.
Leider kam da bislang nicht mehr rüber, als wilde Spekulationen, wie die Speicherverwaltung unter Delphi funktioniert.
Embarcadero scheint das Problem ja offenbar nicht zu interessieren, sonst hätte man wohl einen Fix, oder wenigstens Vorschläge für einen Workaround mit einer fundierten Recherche finden können.
Ich habe das exakt gleiche Problem (mit einer wesentlich aktuelleren Delphi Version), und Source Code der Memory-Leaks produziert pflege ich nicht in Produktion zu geben; soviel dazu und genug zum Vorwort.
--------------------------------------------------------
Also zunächst mal liegt das Problem nicht an dem RequesterCredentials Objekt, das für den
SOAP Request erzeugt wurde (der
Soap.SOAPHeaders.SetOwnsSentHeaders(True); Mechanismus funktioniert wie er soll), sondern das für den
SOAP Response vom PayPal Server ebenfalls ein RequesterCredentials
SOAP Header (mit leeren Feldern) zurückgesandt wird und vom Parser ein entsprechendes Objekt instanziiert wird.
Das kann man eigentlich auch schön in TiGü's FastMM Stack Trace sehen:
[Optosoapdomconv.TOPToSoapDomConvert.ProcessResponse$qqrxp22System.Classes.TStreamrx27Soap.Intfinfo.TIntfMetaDatarx28Soap.Intfinfo.TIntfMethEntryp31Soap.Invokeregistry.TInvContextp31Soap.Invokeregistry.THea
ProcessResponse ist diesbezüglich ziemlich unmissverständlich.
Die
SOAP Header werden nun leider nicht automatisch mit dem decodierten
SOAP Body Object freigegeben, soweit ich den Source Code analysiert habe.
Die Response Header werden in einer generischen TObjectList<T> von TSOAPHeaders.FInHeaders (oder so) gespeichert, für die die OwnsObjects Eigenschaft auf true gesetzt ist, soweit ich das heute debugged habe.
D.h. im Klartext, dass die Response Header Objekte eigentlich spätestens beim Zerstören des TSOAPHeaders Objekts abgeräumt werden sollten(!) was aber aus irgendeinem obskuren Grund nicht passiert (TObjectList<T> funktioniert eigentlich ausnahmsweise einwandfrei, soweit mir bekannt ist). Daher das finale Memory-Leak.
Was mir beim Debuggen heute auch noch aufgefallen ist, ist dass das in der Liste gespeicherte TSOAPHeader Objekt von der
RTTI nicht als RequesterCredentials Typ erkannt wird, sondern als dessen (generierte) Basis Klasse CustomSecurityHeaderType.
Der vom
WSDL Component Generator erzeugte Code sieht so aus:
Delphi-Quellcode:
RequesterCredentials = class(CustomSecurityHeaderType)
private
published
end;
Ich habe versucht mittels
Delphi-Quellcode:
procedure MyProcedure();
var
headerAccess : ISOAPHeaders;
respCredentials : RequesterCredentials;
begin
// ...
respCredentials := headerAccess.Get(RequesterCredentials) as RequesterCredentials;
// ...
respCredentials.Free();
end;
das Header Objekt explizit zu holen und freizugeben, was allerdings scheitert, da das in der Liste gespeicherte Objekt tatsächlich vom Typ CustomSecurityHeaderType ist.
Weiter bin ich heute nicht mehr gekommen, aber ich glaube ich bin der Lösung (dem Workaround) schon ziemlich nahe.
Der ganze generierte Kram aus dem
WSDL basiert übrigens als Super-Super Klasse auf TObject, und leider nicht auf TInterfacedObject und entsprechenden Reference Counted Interface Objekten (TRemotable ist leider nur ein TObject ohne Interface), was die ganze Angelegenheit (Speicherverwaltung) wohl erheblich vereinfachen würde.
Sobald ich in der Lage bin Weiteres zur Lösung des Problems beizutragen, werde ich dies hier tun (morgen hoffentlich).
Gruss,
Günther
P.S.:
Wenn irgendjemand weitere konstruktive und fundierte Beiträge zur Lösung des Problems beitragen möchte, ist dies ausdrücklich erwünscht!