Инициализация и использование COM в Win32 C ++ DLL - PullRequest
5 голосов
/ 16 декабря 2010

Я пишу Win32 C ++ DLL, которая использует COM для запроса WMI. Как я могу программно определить, был ли COM уже инициализирован? Спасибо.

Ответы [ 4 ]

10 голосов
/ 16 декабря 2010

Марк Рэнсом прав
простое, чистое и простое решение - инициализация COM вызывающей стороной.

Гадкий хак
Вы можете попробовать свой первый вызов - скорее всего, CoCreateInstance, и, если он вернет CO_E_NOTINITIALIZED, запустите CoInitialize самостоятельно (и в этом случае не забудьте удалить)

Однако , все еще проблематично «внедрить» CoInitialize в поток вызывающей стороны из DLL. Так что есть

Чистый раствор
Позвольте DLL создать рабочий поток (что означает, что DLL нужны вызовы Init и Teardown), CoInitializeEx в этом потоке самостоятельно, и переместите все вызовы COM в этот отдельный поток.

7 голосов
/ 16 декабря 2010

Самый простой способ - не беспокоиться, а просто потребовать от любого, кто использует вашу DLL, сначала инициализировать COM.В противном случае вы рискуете испортить их собственную инициализацию, если они выполнят ее после вашей.

С другой стороны, если ваши флаги CoInitializeEx совпадают с флагами приложения, вы должныхорошо.Из документации CoInitializeEx :

Разрешены множественные вызовы CoInitializeEx одним и тем же потоком, если они проходят один и тот же флаг параллелизма, но последующие действительные вызовы возвращают S_FALSE.

1 голос
/ 02 августа 2013

Это соответствует чистому решению peterchen, как я его кодировал для многопоточного компонента 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);
}
0 голосов
/ 16 декабря 2010

CoInitializeEx \ CoUninitialize должен вызываться только потоками (не Dll-вызовами).

Кстати, вы не должны использовать CoInitializeEx \ CoUninitialize в DllMain!

...