问题描述
我正在使用 StackOverflow (How can I get HTML source code from TWebBrowser) 中的此代码来获取网页的完整响应:
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;
在对一些大型网页的例程进行数百次调用后,我的内存不足。
显然组件的 Document
属性存在一个已知问题,但将 Webbrowser.Document
替换为 Webbrowser.DefaultInterface.Document
的建议无济于事。我真的不想尝试修复 VCL,如果我知道在哪里以及如何去做,那么调用 Release
的另一个建议可能会奏效。泄漏可能完全是另一回事。此代码高于我的工资等级。
我无法使用 TIdHTTP
,因为必须编写一些脚本,但无论如何我都需要视觉效果。
另见:TWebbrowser massive memory leaks : no solution so far
解决方法
显然组件的 Document 属性存在已知问题
供看到此内容的任何人参考:
RSP-32393: Reference leak in TOleControl.GetIDispatchProp and TOleControl.GetIUnknownProp
更新:据报道此问题已在 10.0 Seattle 中修复,因此不应 在 10.3 中不再发生。
我真的不想尝试修复 VCL,如果我知道在哪里以及如何去做,另一个调用 Release
的建议可能会奏效。
你可以这样称呼它:
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;
因此:
-
TWebBrowser.Document
属性返回一个IDispatch
,由于TOleControl
中的错误,该Disp
的引用计数错误地增加了 +2 而不是 +1。 - 对
LPersistStreamInit
的赋值使引用计数增加 +1 - 对
_Release()
的强制转换+赋值使引用计数增加 +1。
函数退出时:
- 显式
_Release()
递减引用计数 -1 以解决该错误 - 当
LPersistStreamInit
超出范围时的隐式_Release()
会减少引用计数 -1 - 当
Disp
超出范围时的隐式_Release()
会减少引用计数 -1 -
Document
属性的返回值上的隐式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;
使引用计数递减 -1。
引用计数已正确平衡。
或者,您也可以这样做:
_Release()
这样,您就不再需要显式的 TWebBrowser.Document
:
-
IDispatch
属性仍然返回一个Disp
,其引用计数错误地增加了 +2 而不是 +1 - 对
LPersistStreamInit
的赋值不会使引用计数增加 +1 - 对
_Release()
的强制转换+赋值使引用计数增加 +1。
函数退出时:
- 当
LPersistStreamInit
超出范围时的隐式_Release()
会减少引用计数 -1 - 当
Disp
超出范围时的隐式_Release()
会减少引用计数 -1 -
Document
属性的返回值上的隐式 {{1}} 使引用计数递减 -1。
引用计数已正确平衡。