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

Dem*_*o78 6 c++ com atl apartments com-interop

前段时间,我不得不修改一个旧的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!!!!
}
Run Code Online (Sandbox Code Playgroud)

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

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

Rom*_* R. 5

当您使用隐式MTA线程时,这是可能的.正确的功能是这样的:

BOOL IsMultiThreadedApartment() throw()
{
    HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if(SUCCEEDED(nResult))
        CoUninitialize();
    return SUCCEEDED(nResult);
}
Run Code Online (Sandbox Code Playgroud)

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