首页 文章

CoInitializeEx在COM对象内部调用时返回S_OK

提问于
浏览
6

前段时间,我不得不修改旧的COM DLL(Visual C 2010,ATL),将其从“Apartment”线程模型迁移到“Both”,即现在可以从STA和MTA线程调用它而无需序列化调用(当然,我不得不为共享数据添加内部同步 . 当从.NET应用程序通过Interop调用我的DLL时,这会在将COM事件(连接点)转换为.NET事件时导致问题(即使在.NET应用程序中我也必须支持STA和MTA) . 为了解决这些问题,我改变了触发事件的方式 .

1)如果在STA上下文中调用DLL,它就像以前一样工作,即它创建一个不可见的窗口,然后,当必须引发事件时,它调用PostMessage到该窗口,然后主STA线程调用实际的事件 - 触发代码,即CProxy_IMyEventFiringInterface成员函数(CProxy_IMyEventFiringInterface派生自IConnectionPointImpl) .

2)如果在MTA上下文中调用DLL,我没有主COM线程,我不能做PostMessage,所以我使用我创建的自定义线程,让该线程调用IConnectionPointImpl函数 .

但AFAIK没有Windows API可以检测调用线程是STA还是MTA . 许多网站都建议像这样使用CoInitializeEx:

HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch(hr)
{
    case S_FALSE:  // we are in a Multi-Threaded Apartment (MTA)
    CoUninitialize(); // each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize
    break;
    case RPC_E_CHANGED_MODE:  // we are in a Single-Threaded Apartment (STA)
    break;
    default:  // IMPOSSIBLE!!!!
}

我决定将此调用放在CMyComComponent :: FinalConstruct中的CoInitializeEx中 . 一切都很好......直到今天 . 在客户场景中,我从我的跟踪工具中看到,对于某个.NET EXE应用程序(我没有源代码),上面的代码最终会出现在默认分支中,因为CoInitializeEx返回了S_OK . 这怎么可能? Microsoft文档说S_OK意味着“COM库已在此线程上成功初始化”,但我是INSIDE COM对象,COM库必须已经初始化!顺便说一句,默认分支不会关闭应用程序,但是,由于返回了S_OK,它会调用CoUninitialize(每次成功调用CoInitialize或CoInitializeEx,包括任何返回S_FALSE的调用,都必须通过对CoUninitialize的相应调用进行 balancer )然后DLL继续假设STA(后见之明的错误移动) . 但它不是STA:事实上,后来的PostMessage返回FALSE .

我可以简单地更改代码以使用MTA作为默认值,如果CoInitializeEx(NULL,COINIT_MULTITHREADED)返回S_OK,我应该从一开始就做到了 . 但我也希望确保这是正确的做法,以避免将来出现进一步的问题 . 非常感谢Demetrio

1 回答

  • 5

    当你在implicit MTA线程上时,这是可能的 . 正确的功能是这样的:

    BOOL IsMultiThreadedApartment() throw()
    {
        HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
        if(SUCCEEDED(nResult))
            CoUninitialize();
        return SUCCEEDED(nResult);
    }
    

    您正在遇到这种不寻常的情况,因为调用者应用程序无法在其实例化您的类的线程上初始化COM . 但请注意,如果稍后调用者将线程初始化为STA,则可能会遇到棘手的情况,即您的类在STA线程上以MTA模式运行 . 另一方面,自己做 CoInitializeEx 可能会导致调用者无法错误地进行COM初始化 .

相关问题