在 Delphi 10.3.3 中访问 TWebBrowser.Document 时如何克服内存泄漏

Kev*_*son 3 delphi memory-leaks twebbrowser delphi-10.3-rio

我正在使用 StackOverflow 中的此代码(如何从 TWebBrowser 获取 HTML 源代码)来获取网页的完整响应:

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;
Run Code Online (Sandbox Code Playgroud)

在使用一些大型网页对例程进行数百次调用后,我的内存不足。

显然,组件的Document属性存在一个已知问题,但替换为WebBrowser.Documentwith的建议WebBrowser.DefaultInterface.Document无济于事。我真的不想尝试修复 VCL,Release如果我知道在哪里以及如何做的话,另一个调用的建议可能会奏效。泄漏可能完全是另一回事。此代码高于我的工资等级。

TIdHTTP由于必须编写一些脚本,我无法使用,无论如何我都需要视觉效果。

另请参阅:TWebbrowser 大量内存泄漏:目前尚无解决方案

Rem*_*eau 7

显然组件的 Document 属性存在已知问题

供看到此内容的任何人参考:

RSP-32393:TOleControl.GetIDispatchProp 和 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;
Run Code Online (Sandbox Code Playgroud)

因此:

  • TWebBrowser.Document属性返回一个IDispatchrefcount 由于错误而错误地增加了 +2 而不是 +1TOleControl
  • 分配给Disp增加引用计数 +1
  • cast+assignment toLPersistStreamInit增加 refcount +1。

当函数退出时:

  • 显式_Release()减少引用计数 -1 以解决该错误
  • 超出范围_Release()时的隐式LPersistStreamInit递减引用计数 -1
  • 超出范围_Release()时的隐式Disp递减引用计数 -1
  • 一个隐含的_Release()Document财产的返回值递减引用计数-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;
Run Code Online (Sandbox Code Playgroud)

这样,您不再需要显式_Release()

  • TWebBrowser.Document属性仍然返回IDispatch其引用计数错误地增加了 +2 而不是 +1
  • 分配给Disp不会增加引用计数 +1
  • cast+assignment toLPersistStreamInit增加 refcount +1。

当函数退出时:

  • 超出范围_Release()时的隐式LPersistStreamInit递减引用计数 -1
  • 超出范围_Release()时的隐式Disp递减引用计数 -1
  • 一个隐含的_Release()Document财产的返回值递减引用计数-1。

引用计数正确平衡。