Win32 C++ DLL中的COM初始化和使用

7

我正在编写一个使用COM查询WMI的Win32 C++ DLL。如何在程序中确定COM是否已经初始化?谢谢。

4个回答

10

Mark Ransom 是正确的
直截了当、干净简单的解决办法是要求调用者进行COM初始化。

丑陋的hack
您可以尝试第一次调用——很可能是CoCreateInstance,如果它返回CO_E_NOTINITIALIZED,则自己运行CoInitialize(并在这种情况下不要忘记取消初始化)。

然而,从DLL向调用线程“注入”CoInitialize仍然存在问题。所以有一个

清洁的解决方案
让DLL创建一个工作线程(这意味着DLL需要Init和Teardown调用),在这个线程中自己进行CoInitializeEx,并将所有的COM调用移动到该独立线程中。


2
谢谢,最终我使用了一个工作线程。初始化和拆除都在同一个DLL函数中完成。使用WaitForSingleObject等待工作线程完成。 - Jim Fell

7
最简单的方法是不要麻烦自己,只需让使用您的DLL的任何人都先初始化COM。否则,如果他们在您之后执行初始化,就会有破坏他们自己初始化的风险。
另一方面,如果您的CoInitializeEx标志与应用程序的匹配,那么您应该没问题。从CoInitializeEx文档中可以得知:

同一线程对 CoInitializeEx 的多个调用允许,只要它们传递相同的并发标志,但后续有效的调用将返回 S_FALSE。


那真的没有帮助。不幸的是,大部分应用程序已经编写完成了。我需要知道如何确定 COM 是否已经被初始化。我知道这不是最佳实践,但这是我必须处理的。 - Jim Fell
@Jim:没错。在你的代码中,只需确保恰好调用一次CoInitializeEx()和一次CoUninitialize()即可。无论在你的线程中是否已经调用过这些函数都没有关系,也不应该有影响。此外,你无法控制你所依赖的代码何时会取消初始化COM,因此依赖启动时的初始化状态是很危险的。 - John Dibling
+1,同时请看这个链接:http://stackoverflow.com/q/2154151/57428 - 看起来多次调用CoInitialize()可能会导致一些奇怪的问题。 - sharptooth

2

以下是我为了封装一个线程安全的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);
}

0

CoInitializeEx\CoUninitialize 应该只被线程调用(而非Dll调用)。

顺便提一下,在 DllMain 中不应使用 CoInitializeEx\CoUninitialize !


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接