从主机应用程序

时间:2017-08-04 09:20:55

标签: c++ mfc activex ole windowless

继承了一个基于MFC的应用程序,该应用程序托管窗口和无窗口ActiveX控件。在此应用程序中,用户可以设计他/她能够创建的SCADA页面并动态删除这些AX控件。控件大多是简单的小部件,如按钮,读数,线条,圆圈等。

自从升级到较新版本的Visual Studio(2017)后,程序现在在删除AX控件时崩溃。最初是VC6。

更具体地说,当您尝试删除无窗口AX控件时,会发生大多数崩溃。 如果这些AX控件托管在另一个主机应用程序(例如Internet Explorer)中,则不会发生崩溃。

程序退出时也会崩溃,因为页面上的所有AX控件也会逐个删除。

更具体地说,创建对象的顺序很重要。如果你删除一个无窗口对象,并且焦点中的下一个对象将成为一个窗口对象,那么你就是安全的:没有崩溃。我认为“路径事件在这里”(焦点)框架代码然后选择窗口对象作为窗口消息的有效目标,从那时起一切都会好起来的。但是,如果新对象也没有窗口,那你就不走运了。

请注意,我们使用WS_DISABLED标志创建所有控件,否则当用户调整小部件并移动小部件时,按钮小部件将开始对鼠标单击做出反应。

在删除无窗口AX控件后发生GPF时,调试器会在OLE容器文件occcont.cpp,函数COleControlContainer::HandleWindowlessMessage中进入并停止。此时,它想要致电m_pSiteFocus->m_pWindowlessObject->OnWindowMessage

这是托管所有AX控件的“容器”窗口的上下文。窗口消息即将被路由到当前聚焦的AX控件。

但是这里选择了错误的目的地,因为m_pWindowlessObject0xdddddddd(这意味着释放了内存)。

我认为应该不再选择该站点焦点对象,因为它的所有内容都已与已删除的AX控件一起被销毁!

有趣的是控件本身的删除操作不会导致任何(直接)问题。间接地,“删除后”窗口消息现在希望被传递到无效的OLE控件容器。即使程序关闭,程序仍然会因早期删除AX控制操作而崩溃。

BTW:使用CWnd::CreateControl创建AX控件,并通过调用CWnd::DestroyWindow()再次删除。

作为一种解决方法,在销毁窗口调用之后,我强制将违规指针指向空指针:

COleControlContainer * pContainer = m_wndContainer.GetControlContainer();
const COleControlContainer * pFreedMemory(reinterpret_cast<COleControlContainer*>(0xdddddddd));
if (pContainer != nullptr && pContainer != pFreedMemory) {
    // After we delete the control, the library wants to focus another control.
    // But if there is no more control or the next control is also windowless/disabled
    // it will bump into a stale pointer (0xdddddddd). The code does check for a null pointer,
    // so we force a null pointer.
    pContainer->m_pSiteFocus = 0;
}

现在程序不再崩溃了,但这太丑了!

所以问题是:我做错了什么,我是否面临框架中的错误,甚至是无窗口AX控件中的错误?

更新#1

在MFC的occsite.cpp

中找到此代码片段
BOOL COleControlSite::DestroyControl()
...
    //VBBUG: VB controls will crash if IOleObject::Close is called on them
    //  when they have focus (and unfortunately, deactivating them does not
    //  always move the focus).  To work around this problem, we always hide
    //  the control before closing it.
    ShowWindow(SW_HIDE);
...

显然,这是针对类似(如果不是相同)问题的解决方法。

更新#2

我发现对于AX控件,他们的析构函数甚至都没有被调用。然后确实找到了一个未正确处理的引用计数接口指针。正如iinspectable在评论中所预测的那样!

0 个答案:

没有答案