2010-12-15 36 views

回答

10

马克赎金是正确
的简单,干净和简单的解决方案是通过调用者需要COM初始化。

丑陋的黑客
你可以试试你的第一个电话 - 可能CoCreateInstance,如果返回CO_E_NOTINITIALIZED,自己运行CoInitialize(不要忘记在这种情况下,以UNINIT)

然而,它将CoInitialize“注入”DLL中的调用者线程仍然存在问题。所以这是一个

清理解决方案
让DLL创建工作线程(这意味着该DLL需要初始化和拆卸电话)的CoInitializeEx在此线程自己,把所有的COM调用到单独的线程。

+2

谢谢,我结束了使用工作线程。初始化和拆除都在同一个DLL函数中完成。 'WaitForSingleObject'用于等待工作线程完成。 – 2010-12-16 22:19:36

7

最简单的方法是不要打扰,只是让它成为任何人使用你的DLL的要求,他们首先初始化COM。否则,如果他们在之后执行之后就会冒着搞乱自己的初始化的风险。

另一方面,如果您的标记为CoInitializeEx与应用程序的标记相匹配,则应该没问题。从CoInitializeEx documentation:由 同一个线程

多次调用的CoInitializeEx被允许,只要 他们通过相同的并发标志, 但后续的有效来电返回 S_FALSE。

+0

这真的是没有帮助的。不幸的是,大部分应用程序已经写好了。我需要知道如何确定COM是否已经初始化。我意识到这不是最佳实践,但它是必须与之合作的。 – 2010-12-15 22:54:24

+0

@Jim:这是对的。在你的代码中,只需要一次CoInitializeEx()和CoUninitialize()一次。它不是也不应该在你的线程中调用它。除此之外,您不控制何时您控制的代码将取消初始化COM,因此启动时依赖初始化状态是危险的。 – 2010-12-15 23:11:48

+0

+1,另请参阅:http://stackoverflow.com/q/2154151/57428 - 看起来像调用'CoInitialize()'更多,一旦可以导致一些奇怪的问题。 – sharptooth 2010-12-16 04:43:00

0

CoInitializeEx \ CoUninitialize只能由线程调用(不能由Dll调用)。

顺便说一句,你不应该在DllMain中使用CoInitializeEx \ CoUninitialize!

1

它遵循我编码它线程安全的COM组件记录器,我想换peterchen干净的解决方案:

IComLoggerPtr _logger; 
_bstr_t _name; 
HANDLE _thread; 
HANDLE _completed; 

Logger::Logger(_bstr_t name) 
{ 
    _name = name; 

    _completed = ::CreateEvent(NULL, false, false, NULL); 
    if (_completed == NULL) 
     ::AtlThrowLastWin32(); 

    // Launch the thread for COM interation 
    DWORD threadId; 
    _thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(this->threadRun), 
     (LPVOID)this, 0, &threadId); 

    // Wait object initialization 
    HRESULT hr = ::WaitForSingleObject(_completed, INFINITE); 
    if (FAILED(hr)) 
     AtlThrow(hr); 
} 

Logger::~Logger() 
{ 
    ::SetEvent(_completed); 
    CloseHandle(_thread); 
    CloseHandle(_completed); 
} 

DWORD WINAPI Logger::threadRun(LPVOID opaque) 
{ 
    Logger *obj = (Logger *)opaque; 

    // Init Free-Threaded COM subsystem 
    HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); 
    if (FAILED(hr)) 
     ::AtlThrow(hr); 

    hr = obj->_logger.CreateInstance(__uuidof(ComLogger)); 
    if (FAILED(hr)) 
     ::AtlThrow(hr); 

    obj->_logger->Init(obj->_name); 

    // Initialization completed 
    bool success = ::SetEvent(obj->_completed); 
    if (!success) 
     ::AtlThrowLastWin32(); 

    // Wait release event 
    hr = ::WaitForSingleObject(obj->_completed, INFINITE); 
    if (FAILED(hr)) 
     AtlThrow(hr); 

    obj->_logger.Release(); 

    // Release COM subsystem 
    ::CoUninitialize(); 
} 

HRESULT Logger::Log(_bstr_t description) 
{ 
    return _logger->Log(description); 
}