【发布时间】:2011-05-26 05:47:05
【问题描述】:
我正在编写一个使用 COM 查询 WMI 的 Win32 C++ DLL。如何以编程方式确定 COM 是否已经初始化?谢谢。
【问题讨论】:
标签: c++ visual-studio-2008 dll com
我正在编写一个使用 COM 查询 WMI 的 Win32 C++ DLL。如何以编程方式确定 COM 是否已经初始化?谢谢。
【问题讨论】:
标签: c++ visual-studio-2008 dll com
Mark Ransom 是对的
直接、干净和简单的解决方案是要求调用者进行 COM 初始化。
丑陋的黑客攻击
你可以尝试你的第一个电话——可能是CoCreateInstance,如果它返回CO_E_NOTINITIALIZED,你自己运行CoInitialize(在这种情况下不要忘记取消初始化)
然而,将 CoInitialize 从 DLL “注入”到调用者线程中仍然存在问题。所以有一个
清洁解决方案
让 DLL 创建一个工作线程(这意味着 DLL 需要 Init 和 Teardown 调用),在这个线程中自己创建 CoInitializeEx,并将所有 COM 调用移动到那个单独的线程。
【讨论】:
WaitForSingleObject 用于等待工作线程完成。
最简单的方法是不要打扰,只要让任何使用您的 DLL 的人都要求他们首先初始化 COM。否则,如果他们在你之后执行它,你就有可能搞乱他们自己的初始化。
另一方面,如果CoInitializeEx 的标志与应用程序的标志匹配,则应该没问题。来自CoInitializeEx documentation:
多次调用 CoInitializeEx 只要允许相同的线程 它们传递相同的并发标志, 但随后的有效调用返回 S_FALSE。
【讨论】:
CoInitialize() 一次会导致一些奇怪的问题。
它遵循 peterchen clean 解决方案,因为我将它编码为我想要包装的线程安全 COM 记录器组件:
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 !
【讨论】: