Webbrowser内存泄漏解决方案(修改OleCtrls.pas)
2014-07-25 07:50
218 查看
根据对IE、Maxtron、TT、世界之窗等浏览器的观察,可以肯定目前在所有的语言中如果调用Webbrowser这个ActiveX组件普遍存在内存泄漏问题。Delphi使用Webbrowser引起内存泄漏的原因是在OleCtrls.pas单元GetIDispatchProp和GetIntegerProp函数于对COM对象引用计数的错误处理上。
参见OleCtrls.pas源代码:
1
function TOleControl.GetIDispatchProp(Index: Integer): IDispatch;
2
var
3
Temp: TVarData;
4
begin
5
GetProperty(Index, Temp);
6
Result := IDispatch(Temp.VDispatch); //错误,COM对象赋值操作会增加对象的引用计数
7
end;
8
9
function TOleControl.GetIUnknownProp(Index: Integer): IUnknown;
10
var
11
Temp: TVarData;
12
begin
13
GetProperty(Index, Temp);
14
Result := IUnknown(Temp.VUnknown); //错误,COM对象赋值操作会增加对象的引用计数
15
end;
由于该错误代码会影响到所有继承TOleControl的组件,使其内存泄漏,所以我建议修改TOleControl的代码并重新编译OleCtrls.pas单元。该成如下的样子,即可解决问题。
1
function TOleControl.GetIDispatchProp(Index: Integer): IDispatch;
2
var
3
Temp: TVarData;
4
begin
5
GetProperty(Index, Temp);
6
//Result := IDispatch(Temp.VDispatch);
7
Pointer(Result) := Temp.VDispatch; //强制转换成Pointer再赋值就不会增加引用计数了
8
end;
9
10
function TOleControl.GetIUnknownProp(Index: Integer): IUnknown;
11
var
12
Temp: TVarData;
13
begin
14
GetProperty(Index, Temp);
15
//Result := IUnknown(Temp.VUnknown);
16
Pointer(Result) := Temp.VUnknown; //强制转换成Pointer再赋值就不会增加引用计数了
17
end;
18
19
问题分析:
该问题由来已久,自Delphi6(我用的第一个Delphi版本,之前版本是否存在不确定)到Delphi2009,OleControl.pas始终存在该问题。修改之后可以解决内存泄漏问题,但Borland为何迟迟不进行更正呢?该内存泄漏对于一个需要频繁创建、操作和释放Ole对象的程序来说,是致命的。例如Doc:=Browser.Document as IHTMLDocument2这个语句就会引发GetDispachProp的执行,由于引用计数被错误的增加,对象不被使用时引用计数仍为1,所以该对象(Doc)不会被释放,进而引发内存泄漏。
由于Borland迟迟未能修复此问题,一些第三方组件例如:TEmbeddedWB,被动的去掉用_Release方法额外减少引用计数,已达到引用计数的平衡,我不太喜欢这样的做法,因为一旦该Bug被Borland或用户自行修复(我就自行修改了OleControl.pas),_Release将会导致引用计数向另一个更危险的方向偏移,Ole对象仍被使用的时候,引用计数已经归零,于是Ole对象被释放,接着是一个AV错误(Access violation错误)被引发。
下篇文章会详细介绍TEmbeddedWB如何被动的修复此漏洞。
参见OleCtrls.pas源代码:
1
function TOleControl.GetIDispatchProp(Index: Integer): IDispatch;
2
var
3
Temp: TVarData;
4
begin
5
GetProperty(Index, Temp);
6
Result := IDispatch(Temp.VDispatch); //错误,COM对象赋值操作会增加对象的引用计数
7
end;
8
9
function TOleControl.GetIUnknownProp(Index: Integer): IUnknown;
10
var
11
Temp: TVarData;
12
begin
13
GetProperty(Index, Temp);
14
Result := IUnknown(Temp.VUnknown); //错误,COM对象赋值操作会增加对象的引用计数
15
end;
由于该错误代码会影响到所有继承TOleControl的组件,使其内存泄漏,所以我建议修改TOleControl的代码并重新编译OleCtrls.pas单元。该成如下的样子,即可解决问题。
1
function TOleControl.GetIDispatchProp(Index: Integer): IDispatch;
2
var
3
Temp: TVarData;
4
begin
5
GetProperty(Index, Temp);
6
//Result := IDispatch(Temp.VDispatch);
7
Pointer(Result) := Temp.VDispatch; //强制转换成Pointer再赋值就不会增加引用计数了
8
end;
9
10
function TOleControl.GetIUnknownProp(Index: Integer): IUnknown;
11
var
12
Temp: TVarData;
13
begin
14
GetProperty(Index, Temp);
15
//Result := IUnknown(Temp.VUnknown);
16
Pointer(Result) := Temp.VUnknown; //强制转换成Pointer再赋值就不会增加引用计数了
17
end;
18
19
问题分析:
该问题由来已久,自Delphi6(我用的第一个Delphi版本,之前版本是否存在不确定)到Delphi2009,OleControl.pas始终存在该问题。修改之后可以解决内存泄漏问题,但Borland为何迟迟不进行更正呢?该内存泄漏对于一个需要频繁创建、操作和释放Ole对象的程序来说,是致命的。例如Doc:=Browser.Document as IHTMLDocument2这个语句就会引发GetDispachProp的执行,由于引用计数被错误的增加,对象不被使用时引用计数仍为1,所以该对象(Doc)不会被释放,进而引发内存泄漏。
由于Borland迟迟未能修复此问题,一些第三方组件例如:TEmbeddedWB,被动的去掉用_Release方法额外减少引用计数,已达到引用计数的平衡,我不太喜欢这样的做法,因为一旦该Bug被Borland或用户自行修复(我就自行修改了OleControl.pas),_Release将会导致引用计数向另一个更危险的方向偏移,Ole对象仍被使用的时候,引用计数已经归零,于是Ole对象被释放,接着是一个AV错误(Access violation错误)被引发。
下篇文章会详细介绍TEmbeddedWB如何被动的修复此漏洞。
相关文章推荐
- Webbrowser内存泄漏解决方案(修改OleCtrls.pas)
- 使用TWebBrowser时存在内存泄漏问题的解决方案(使用SetProcessWorkingSetSize函数,或者修改OleCtrls.pas源码解决问题)
- 修改WebBrowser控件的内核解决方案(x86和x64有不同)
- 【.Net码农】修改WebBrowser控件的内核解决方案
- 修改WebBrowser控件的内核解决方案
- 修改WebBrowser控件的内核解决方案
- 修改WebBrowser控件的内核解决方案
- 根据sql脚本修改数据库表结构的几种解决方案
- VS中修改解决方案/项目/类的名字
- Oracle修改表列名与顺序的解决方案 (sql 修改列名)
- JS动态修改页面EasyUI datebox不生效、EasyUI动态添加Class、EasyUI动态渲染解析解决方案
- 关于IE测试,网页打开速度慢的解决方案(通过修改hosts文件)。
- Delphi中WebBrowser控件的3个bug及解决方案
- 清除webBrowser 缓存和Cookie的解决方案
- Java修改JVM内存大小整理。(java heap space 解决方案)
- sql2008中已存在已有数据表修改主键为自增不让更改的解决方案
- 内存溢出和内存泄漏的区别、产生原因以及解决方案
- 跨线程访问及修改控件属性的解决方案
- 清除webBrowser 缓存和Cookie的解决方案
- Delphi7中WebBrowser控件的bug及解决方案