我正在编写一个使用COM查询WMI的Win32 C++ DLL。如何在程序中确定COM是否已经初始化?谢谢。
我正在编写一个使用COM查询WMI的Win32 C++ DLL。如何在程序中确定COM是否已经初始化?谢谢。
Mark Ransom 是正确的
直截了当、干净简单的解决办法是要求调用者进行COM初始化。
丑陋的hack
您可以尝试第一次调用——很可能是CoCreateInstance
,如果它返回CO_E_NOTINITIALIZED,则自己运行CoInitialize
(并在这种情况下不要忘记取消初始化)。
然而,从DLL向调用线程“注入”CoInitialize仍然存在问题。所以有一个
清洁的解决方案
让DLL创建一个工作线程(这意味着DLL需要Init和Teardown调用),在这个线程中自己进行CoInitializeEx,并将所有的COM调用移动到该独立线程中。
CoInitializeEx
标志与应用程序的匹配,那么您应该没问题。从CoInitializeEx
文档中可以得知:
同一线程对 CoInitializeEx 的多个调用允许,只要它们传递相同的并发标志,但后续有效的调用将返回 S_FALSE。
CoInitialize()
可能会导致一些奇怪的问题。 - sharptooth以下是我为了封装一个线程安全的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);
}
CoInitializeEx\CoUninitialize 应该只被线程调用(而非Dll调用)。
顺便提一下,在 DllMain 中不应使用 CoInitializeEx\CoUninitialize !
WaitForSingleObject
等待工作线程完成。 - Jim Fell