简体   繁体   中英

COM Initialization and Use in Win32 C++ DLL

I am writing a Win32 C++ DLL that uses the COM to query WMI. How can I programmatically determine if COM has already been initialized? Thanks.

Mark Ransom is right
the straightforward, clean and simple solution is to require COM initialization by the caller.

Ugly hack
You can try your first call - likely CoCreateInstance , and if it returns CO_E_NOTINITIALIZED, run CoInitialize yourself (and don't forget to uninit in that case)

However , it is still problematic to "inject" a CoInitialize into a caller thread from a DLL. So there's a

Clean Solution
Let the DLL create a worker thread (which means the DLL needs Init and Teardown calls), CoInitializeEx in this thread yourself, and move all the COM calls to that separate thread.

The easiest way is not to bother, just make it a requirement of anybody using your DLL that they initialize COM first. Otherwise you run the risk of messing up their own initialization if they perform it after yours.

On the other hand if your flags to CoInitializeEx match those of the application, you should be fine. From the CoInitializeEx documentation :

Multiple calls to CoInitializeEx by the same thread are allowed as long as they pass the same concurrency flag, but subsequent valid calls return S_FALSE.

It follows peterchen clean solution as I coded it for a thread-safe COM logger component that I wanted to wrap:

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 should only be called by threads (not by Dll-calls).

BTW ,you should not Use CoInitializeEx\\CoUninitialize in DllMain !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM