基于MFC的ActiveX控件的退出(卸载)

来源:互联网 发布:做电力行业的软件 编辑:程序博客网 时间:2024/05/18 02:33

一直以来,写了控件后用起来很正常,最近,客户要把这控件用在web页面上,那么问题它就来了。

大多数时候的运行环境:
os: win7、win10
浏览器:ie8、ie11,这里不考虑ie之外的其它浏览器

问题表现:

打开A.htm页面,然后通过其中的链接打开B.htm,其中B.htm页面中加载本控件。然后关闭B.htm,再点击A.htm中的链接打开B.htm,结果ie挂掉!

开发IDE:vs2010中文版
开发语言:VC++

控件的基本结构:
在CxxCtrl中创建一个对话框CMainDs m_mds,并且设置m_mds的父窗口为CxxCtrl,大致的过程如下:

int CxxCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct){    if (COleControl::OnCreate(lpCreateStruct) == -1)        return -1;    m_mds.Create(CMainDs::IDD);    m_mds.SetParent(this);    return 0;}

问题跟踪:
注:环境为win7+ie11
因为是在页面加载的时候挂掉的,所以这个OnCreate()入口便是第一个要找的地方,通过添加打印发现,在挂掉的时候,是因为m_mds.Create(CMainDs::IDD);的接口调用失败,即对话框没有创建成功,则下面的一行自然报错!
在创建对话框时,不管是否成功,都会有下面的打印:

Warning: Creating dialog from within a COleControlModule application is not a supported scenario.

这个打印,应该是只在debug版本下才会有,在release下是被屏蔽掉的。其实既然创建成功的时候也会有这一行的打印,那说明这一行所说的问题并不是核心问题。
通过进一步跟踪发现,在关闭页面的时候,其进程并没有退出,在经过大约60秒~70秒后,进程才从任务管理器中消失,所以初步怀疑是因为控件退出有问题,控件退出太慢导致进程退出慢,在进程退出之前又打开页面的时候,ie却又使用了之前的那个进程,但奇怪的是,即使是同一个进程,那只是创建一个对话框,第一次成功,那第二次就会失败?
做为测试,在关闭页面后,一直等待进程退出。结果在进程退出时候有很多输出的内容,但都是表示内存泄露:

00000118    75.85210419 [7180] Detected memory leaks!   00000119    75.85218811 [7180] Dumping objects ->   00000120    75.85226440 [7180] {334}    00000121    75.85233307 [7180] normal block at 0x0FEFA8A8, 24 bytes long.   00000122    75.85242462 [7180]  Data: <h               > 68 D3 E4 0B 03 00 00 00 03 00 00 00 01 00 00 00    00000123    75.85247040 [7180] {331}    00000124    75.85254669 [7180] normal block at 0x0FEFA860, 26 bytes long.   00000125    75.85263062 [7180]  Data: <h               > 68 D3 E4 0B 04 00 00 00 04 00 00 00 01 00 00 00    00000126    75.85269165 [7180] {325}    00000127    75.85276031 [7180] normal block at 0x0FBDEB28, 44 bytes long.   00000128    75.85284424 [7180]  Data: <h               > 68 D3 E4 0B 0D 00 00 00 0D 00 00 00 01 00 00 00    00000129    75.85291290 [7180] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(4553) :    00000130    75.85298157 [7180] {297}    00000131    75.85305023 [7180] client block at 0x0FBDEEC0, subtype c0, 56 bytes long.   00000132    75.85317230 [7180] a CObject object at $0FBDEEC0, 56 bytes long    00000133    75.85324860 [7180] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp(4553) :    00000134    75.85330963 [7180] {285}    00000135    75.85337830 [7180] client block at 0x0FBDEC90, subtype c0, 56 bytes long.   00000136    75.85347748 [7180] a CObject object at $0FBDEC90, 56 bytes long    00000137    75.85354614 [7180] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\ctlcore.cpp(574) :     00000138    75.85361481 [7180] {277}    00000139    75.85367584 [7180] client block at 0x0FBDE410, subtype c0, 120 bytes long.  00000140    75.85377502 [7180] a CWnd object at $0FBDE410, 120 bytes long  00000141    75.85385132 [7180] f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\ctlview.cpp(239) :     00000142    75.85391235 [7180] {271}    00000143    75.85397339 [7180] normal block at 0x0FBDE9D0, 12 bytes long.   00000144    75.85405731 [7180]  Data: <         h  > 01 00 00 00 00 00 00 00 E4 68 19 07    00000145    75.85412598 [7180] {266}    00000146    75.85419464 [7180] normal block at 0x0FBDEA90, 102 bytes long.  00000147    75.85427856 [7180]  Data: <h   &   *       > 68 D3 E4 0B 26 00 00 00 2A 00 00 00 02 00 00 00    00000148    75.85433960 [7180] {262}    00000149    75.85440826 [7180] normal block at 0x0FBDE998, 8 bytes long.    00000150    75.85448456 [7180]  Data: <        > A0 C4 EF 0F 00 00 00 00    00000151    75.85454559 [7180] {261}    00000152    75.85461426 [7180] normal block at 0x0FEFB0B0, 32 bytes long.   00000153    75.85469818 [7180]  Data: <                > B0 B0 EF 0F B0 B0 EF 0F B0 B0 EF 0F CD CD CD CD    00000154    75.85475922 [7180] {260}    00000155    75.85482788 [7180] normal block at 0x0FBDE960, 8 bytes long.    00000156    75.85490417 [7180]  Data: <p       > 70 C4 EF 0F 00 00 00 00    00000157    75.85496521 [7180] {259}    00000158    75.85503387 [7180] normal block at 0x0FBDE900, 52 bytes long.   00000159    75.85511780 [7180]  Data: <                > 00 E9 BD 0F 00 E9 BD 0F 00 E9 BD 0F CD CD CD CD    00000160    75.85518646 [7180] {258}    00000161    75.85525513 [7180] normal block at 0x0FBDE8C8, 8 bytes long.    00000162    75.85532379 [7180]  Data: <        > 14 C3 EF 0F 00 00 00 00    00000163    75.85539246 [7180] {257}    00000164    75.85546112 [7180] normal block at 0x0FBDE890, 8 bytes long.    00000165    75.85553741 [7180]  Data: <        > DC C2 EF 0F 00 00 00 00    00000166    75.85560608 [7180] e:\work\plat\xxctrl.cpp(14) :    00000167    75.85566711 [7180] {256}    00000168    75.85572815 [7180] client block at 0x0FEFBFF8, subtype c0, 2048 bytes long. 00000169    75.85584259 [7180] a CsilvideocliCtrl object at $0FEFBFF8, 2048 bytes long     00000170    75.85590363 [7180] Object dump complete.    

这个打印中,
1. 我电脑上没有 f:\dd\vctools\vc7libs\ 这个路径,不知道为什么会有这个打印出来
2. xxctrl.cpp(14) 这一行非常重要,这一行的内容是:

IMPLEMENT_DYNCREATE(CxxCtrl, COleControl)

这是创建控件类的代码,是建立工程时向导自己加上去的,退出时它自己不删除,关我何事?事实证明,关系很大,它会让客户不厌其烦地找你,给你压力!

因为这控件依赖了很多其它的动态库,难道是因为某些库退出慢才导致了控件的退出慢?
把控件依赖的动态库逐个地屏蔽掉再测试,结果依旧,直到把所有添加上去的代码全部删除掉,接口什么的也全部删除掉,还是一样!难道是哪里没有恢复原状导致的?立马建立一个全新的控件,自己不写一行代码直接编译,然后加载到web页面中,结果仍然无法及时退出!
费了九牛二虎之力查找是哪个库的问题,结果不是我的问题!话说,这向导、这ie不可能会有这么严重的bug吧?我用的少,那其他开发人员还不早就疯了?这说明还得自己找问题去。
再进行测试。不启动A.htm页面,而是直接双击B.htm页面启动ie,然后再关闭,结果这个退出很快,进程也立马消失,但是仍然还是一堆的内存泄露打印。
在CxxCtrl的析构函数中添加打印,发现web页面关闭直到其进程退出,仍然没有走到析构函数中,但是我启动使用的exe后关闭,它就可以。意思是说,exe没问题,而web就有问题,客户说,早点就不应该承诺支持web,这下麻烦大了,出力不讨好,我说,这确实是个好主意,不过机会已经错过,后悔咋办?冰办。

通过以上的多种测试,这要等使进程快速退出,估计是很困难了,因为ie对控件的加载和卸载,完全不受控件的控制。只好调整方向。
对比两次创建对话框的不同,似乎是只有CxxCtrl类是否存在的差异,所以如果在关闭页面时,能够把CxxCtrl的实例删除掉,岂不美哉?这个办法其实之前也有试过,但是由于方向没有定下来,加的位置导致退出时程序挂掉而不了了之,现在只好重新考虑这个办法了。
对比ie的退出和exe退出的打印可以发现,exe的退出,会调用CxxCtrl::OnFinalRelease()函数,通过单步跟踪发现,在调用完这个函数后,有如下一行代码:

delete this;

也就是说,还是有删除自己的这一动作存在。但是在web退的时候,不会调用这个接口,也没有调用delete this,那就只好自己添加了。
好在web退出时,会调用CxxCtrl::OnClose()函数,添加delete this应该就和它有关了,但是之前在这个函数中直接添加delete this就是导致在页面关闭时ie挂掉,这硬的不行,只能来软的了,变通一下方法,在这里向自己pose一个消息。
因为PostMessage()的优先级很低,它发的消息会在最后才处理,所以这里不用SendMessage()。

#define UM_CHGFUN       (WM_USER+101)void CxxCtrl::OnClose(DWORD dwSaveOption){    // TODO: Add your message handler code here and/or call default    EndCtrol(); // 反初始化,关闭所有任务,清空所有变量及内存        PostMessage(UM_CHGFUN, 11);    COleControl::OnClose(dwSaveOption);    //delete this;  // 这里删除自己会导致ie挂掉!   }LRESULT CxxCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam){    if (message == UM_CHGFUN && wParam == 11)    {        delete this;        return 0;    }    return COleControl::WindowProc(message, wParam, lParam);}

通过以上的修改,再测试已经可以了。这是初步成功,是否还有其它问题,目前还没有进行更多的测试,所以还不能确定,但至少发现的这个退出和加载的问题是暂时解决了。

开始的时候,是通过发送销毁消息:

void CxxCtrl::OnClose(DWORD dwSaveOption){    // TODO: Add your message handler code here and/or call default    EndCtrol(); // 反初始化,关闭所有任务,清空所有变量及内存        PostMessage(WM_DESTROY);    COleControl::OnClose(dwSaveOption);    //delete this;  // 这里删除自己会导致ie挂掉!   }LRESULT CxxCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam){    if (message == WM_DESTROY)    {        delete this;        return 0;    }    return COleControl::WindowProc(message, wParam, lParam);}

这样,在web上退出是没有问题了,但在exe上退出时,会在CxxCtrl::OnFinalRelease()中挂掉,所以发送WM_DESTROY的消息是不行的,当然也可能是我处理的不对,这就没有深究了

0 0
原创粉丝点击