I'm using this code from StackOverflow (How can I get HTML source code from TWebBrowser) to get the full response from a webpage:
function TMain.GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
var
LStream: TStringStream;
Stream: IStream;
LPersistStreamInit: IPersistStreamInit;
begin
if not Assigned(WebBrowser.Document) then
Exit;
LStream := TStringStream.Create('');
try
LPersistStreamInit := WebBrowser.Document as
IPersistStreamInit;
Stream := TStreamAdapter.Create(LStream, soReference);
LPersistStreamInit.Save(Stream, True);
Result := LStream.DataString;
finally
LStream.Free();
end;
end;
After a couple of hundred calls to the routine with some large web pages, I'm out of memory.
Apparently there is a known problem with the component's Document
property, but the suggestion of replacing WebBrowser.Document
with WebBrowser.DefaultInterface.Document
doesn't help. I really don't want to try to fix the VCL, and the other suggestion of calling Release
might work if I knew where and how to do it. And the leak could be something else entirely. This code is above my pay grade.
I can't use TIdHTTP
because of some scripting that has to occur, and I need the visual anyway.
See also: TWebbrowser massive memory leaks : no solution so far
Apparently there is a known problem with the component's Document property
For reference to anyone seeing this:
RSP-32393: Reference leak in TOleControl.GetIDispatchProp and TOleControl.GetIUnknownProp
UPDATE: this issue was reportedly fixed in 10.0 Seattle, so it should not be happening anymore in 10.3.
I really don't want to try to fix the VCL, and the other suggestion of calling
Release
might work if I knew where and how to do it.
You would call it like this:
function TMain.GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
var
Disp: IDispatch;
LStream: TStringStream;
Stream: IStream;
LPersistStreamInit: IPersistStreamInit;
begin
Disp := WebBrowser.Document;
if not Assigned(Disp) then
Exit;
try
LStream := TStringStream.Create('');
try
LPersistStreamInit := Disp as IPersistStreamInit;
Stream := TStreamAdapter.Create(LStream, soReference);
LPersistStreamInit.Save(Stream, True);
Result := LStream.DataString;
finally
LStream.Free;
end;
finally
Disp._Release;
end;
end;
Thus:
TWebBrowser.Document
property returns an IDispatch
whose refcount has been erroneously incremented +2 instead of +1 due to a bug in TOleControl
Disp
increments the refcount +1LPersistStreamInit
increments the refcount +1.When the function exits:
_Release()
decrements the refcount -1 to workaround the bug_Release()
when LPersistStreamInit
goes out of scope decrements the refcount -1_Release()
when Disp
goes out of scope decrements the refcount -1_Release()
on the Document
property's return value decrements the refcount -1.The refcount is balanced properly.
Alternatively, you can do this instead:
function TMain.GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
var
Disp: IDispatch;
LStream: TStringStream;
Stream: IStream;
LPersistStreamInit: IPersistStreamInit;
begin
Pointer(Disp) := WebBrowser.Document;
if not Assigned(Disp) then
Exit;
LStream := TStringStream.Create('');
try
LPersistStreamInit := Disp as IPersistStreamInit;
Stream := TStreamAdapter.Create(LStream, soReference);
LPersistStreamInit.Save(Stream, True);
Result := LStream.DataString;
finally
LStream.Free;
end;
end;
This way, you don't need the explicit _Release()
anymore:
TWebBrowser.Document
property still returns an IDispatch
whose refcount has been erroneously incremented +2 instead of +1Disp
WON'T increment the refcount +1LPersistStreamInit
increments the refcount +1.When the function exits:
_Release()
when LPersistStreamInit
goes out of scope decrements the refcount -1_Release()
when Disp
goes out of scope decrements the refcount -1_Release()
on the Document
property's return value decrements the refcount -1.The refcount is balanced properly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With