在COM对象内调用时返回S_OK的CoInitializeX 前一段时间,我不得不修改一个旧的COM DLL(Visual C++ 2010,ATL),将它从“公寓”线程模型迁移到“两者”,即现在可以从STA和MTA线程调用而不串行调用(当然,我必须为共享数据添加内部同步)。当从.NET应用程序通过互操作调用我的DLL时(即使在.NET应用程序中,我也必须同时支持STA和MTA),这反过来会导致将COM事件(连接点)转换为.NET事件时出现问题。 为了解决这些问题,我改变了触发事件的方式

在COM对象内调用时返回S_OK的CoInitializeX 前一段时间,我不得不修改一个旧的COM DLL(Visual C++ 2010,ATL),将它从“公寓”线程模型迁移到“两者”,即现在可以从STA和MTA线程调用而不串行调用(当然,我必须为共享数据添加内部同步)。当从.NET应用程序通过互操作调用我的DLL时(即使在.NET应用程序中,我也必须同时支持STA和MTA),这反过来会导致将COM事件(连接点)转换为.NET事件时出现问题。 为了解决这些问题,我改变了触发事件的方式,c++,com,com-interop,atl,apartments,C++,Com,Com Interop,Atl,Apartments,1) 如果在STA上下文中调用DLL,它的工作方式与以前一样,即它创建一个不可见的窗口,然后,当必须引发事件时,它调用该窗口的PostMessage,然后主STA线程调用实际的事件触发代码,即CProxy_IMyEventFiringInterface成员函数(CProxy_IMyEventFiringInterface源自IConnectionPointImpl) 2) 如果在MTA上下文中调用DLL,则我没有主COM线程,也无法执行PostMessage,因此我使用我创建的自定义线程,并让该

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

2) 如果在MTA上下文中调用DLL,则我没有主COM线程,也无法执行PostMessage,因此我使用我创建的自定义线程,并让该线程调用IConnectionPointImpl函数

但是目前还没有检测调用线程是STA还是MTA的Windows API。许多网站建议像这样使用CoInitializeX:

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!!!!
}
BOOL IsMultiThreadedApartment() throw()
{
    HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if(SUCCEEDED(nResult))
        CoUninitialize();
    return SUCCEEDED(nResult);
}
我决定在CMyComComponent::FinalConstruct中调用CoInitializeEx。 一切都很顺利。。。直到今天。在一个客户场景中,我从我的跟踪工具中看到,对于某个.NET EXE应用程序(我没有源代码),上面的代码以默认分支结束,因为CoInitializeX返回了S_OK。 这怎么可能呢?Microsoft文档说S_OK意味着“COM库在此线程上已成功初始化”,但我在COM对象中,COM库必须已初始化! 顺便说一句,默认分支不会关闭应用程序,但是,由于返回了S_OK,它调用了ConInitialize(每次成功调用CoInitialize或CoInitializeEx,包括任何返回S_FALSE的调用,都必须通过相应的调用ConInitialize来平衡),然后DLL继续假设STA(事后看来是错误的)。但这不是STA:事实上,稍后的PostMessage返回FALSE

如果coinitializex(NULL,COINIT_多线程)返回S_OK,我可以简单地将代码更改为使用MTA作为默认值,我应该从一开始就这样做。 但我也希望确保这是正确的做法,以避免未来出现更多问题。 非常感谢你
Demetrio

当您在线程上时,这是可能的。正确的功能如下所示:

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!!!!
}
BOOL IsMultiThreadedApartment() throw()
{
    HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if(SUCCEEDED(nResult))
        CoUninitialize();
    return SUCCEEDED(nResult);
}

您遇到这种异常情况是因为调用方应用程序未能在其实例化类的线程上初始化COM。但是请注意,如果调用方稍后将线程初始化为STA,则在STA线程上以MTA模式运行类可能会遇到棘手的情况。另一方面,自己执行
CoInitializeEx
可能会导致调用方错误地延迟COM初始化。

非常有趣,非常感谢,我将使用此原理,希望不会发生“延迟STA初始化”…我怀疑调用方在一个(主)中初始化了您的对象ApplicationOn线程然后在worker线程中调用您的对象方法,而worker线程并没有调用CoInitializeEx。您可以始终创建消息传递线程并从此线程调用所有方法如果您的目标是Windows 7及更高版本,则可以使用
CoGetPartmentType
()。