如何在 Delphi 10.3.3 中访问 TWebBrowser.Document 时克服内存泄漏
Posted
技术标签:
【中文标题】如何在 Delphi 10.3.3 中访问 TWebBrowser.Document 时克服内存泄漏【英文标题】:How can I overcome memory leak in accessing TWebBrowser.Document in Delphi 10.3.3 【发布时间】:2021-05-14 08:07:37 【问题描述】:我正在使用 *** (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
【问题讨论】:
你知道究竟是什么泄漏吗?如果是,那是什么?如果没有,请使用 madExcept 之类的工具,它会告诉您已分配的内容和位置,但未释放。这有助于了解这些信息。 有趣的是,这些年来我与TWebBrowser
合作过很多次,但从未意识到这个泄漏。 “用 WebBrowser.DefaultInterface.Document
替换 WebBrowser.Document
的建议没有帮助” - 为什么不呢?
@DelphiCoder TStreamAdapter
实现了IStream
接口,并被分配给IStream
变量。所以普通的接口引用计数会处理释放它。
@KevinDavidson 您是否为 10.3 安装了所有更新和补丁?与此问题相关的RSP-19473 在 10.3.3 中已作为“已修复”关闭。但我刚刚为原始问题提交了RSP-32393,以防万一。
@KevinDavidson 您声称遇到的TOleControl
问题实际上已在西雅图 10.0 中修复,因此您不应该在 10.3 中看到它,除非这是回归或全新泄漏。
【参考方案1】:
显然组件的 Document 属性存在已知问题
供看到此内容的任何人参考:
RSP-32393: Reference leak in TOleControl.GetIDispatchProp and TOleControl.GetIUnknownProp
更新:据报道此问题已在西雅图 10.0 中修复,因此不应 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
中的错误,它的引用计数错误地增加了+2 而不是+1
对Disp
的赋值增加了引用计数+1
对LPersistStreamInit
的强制转换+赋值增加了引用计数+1。
函数退出时:
显式_Release()
减少 refcount -1 以解决该错误
当LPersistStreamInit
超出范围时,隐式_Release()
会减少引用计数-1
当Disp
超出范围时,隐式_Release()
会减少引用计数-1
Document
属性返回值上的隐式 _Release()
会减少引用计数 -1。
引用计数已正确平衡。
或者,您也可以这样做:
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;
这样,您不再需要显式的_Release()
:
TWebBrowser.Document
属性仍然返回一个 IDispatch
,其引用计数错误地增加了 +2 而不是 +1
分配给Disp
不会增加引用计数+1
对LPersistStreamInit
的强制转换+赋值增加了引用计数+1。
函数退出时:
当LPersistStreamInit
超出范围时,隐式_Release()
会减少引用计数-1
当Disp
超出范围时,隐式_Release()
会减少引用计数-1
Document
属性返回值上的隐式 _Release()
会减少引用计数 -1。
引用计数已正确平衡。
【讨论】:
在 10.4.1 中签入,支持票证中提到的两个函数中的临时变量为 nil 添加了赋值。 是的,我刚刚确认nil
分配是在西雅图 10.0 中引入的。所以这段代码不应该在 10.3 中泄漏以上是关于如何在 Delphi 10.3.3 中访问 TWebBrowser.Document 时克服内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章