2010-12-15 2 views

Antwort

10

Mark Ransom ist richtig
die einfache, saubere und einfache Lösung ist der COM-Initialisierung durch den Anrufer zu verlangen.

hässliche Hack
Sie können Ihren ersten Anruf versuchen - wahrscheinlich CoCreateInstance, und wenn es CO_E_NOTINITIALIZED zurückkehrt, laufen CoInitialize selbst (und vergessen Sie nicht, in diesem Fall UNINIT)

jedoch, es ist immer noch problematisch, ein CoInitialize in einen Aufrufer Thread von einer DLL "injizieren". So gibt es eine

saubere Lösung
Lassen Sie die DLL einen Worker-Thread erstellen (was bedeutet, dass die DLL muss Init und Teardown Anrufe), CoInitializeEx in diesem Thread selbst, und verschieben Sie alle COM-Aufrufe in diesem separaten Thread.

+2

Danke, ich habe einen Worker Thread verwendet. Initialisierung und Abbau erfolgen alle in der gleichen DLL-Funktion. 'WaitForSingleObject' wird verwendet, um auf den Worker-Thread zu warten. –

7

Der einfachste Weg ist, nicht zu stören, machen Sie es nur eine Anforderung von jedermann mit Ihrer DLL, dass sie zuerst COM initialisieren. Sonst laufen Sie Gefahr, Ihre eigene Initialisierung zu verpfuschen, wenn sie es nach Ihres durchführen.

Auf der anderen Seite, wenn Ihre Flaggen zu CoInitializeEx denen der Anwendung entsprechen, sollten Sie in Ordnung sein. Von den CoInitializeEx documentation:

Mehrere Anrufe zu CoInitializeEx von das gleiche Gewinde erlaubt sind, solange sie die gleiche Parallelität Flag übergeben, aber nachfolgende gültige Anrufe zurückkehren S_FALSE.

+0

Das ist wirklich nicht hilfreich. Leider ist der Großteil der Anwendung bereits geschrieben. Ich muss wissen, wie man bestimmt, ob das COM bereits initialisiert worden ist. Mir ist klar, dass dies keine Best Practice ist, aber es muss damit arbeiten. –

+0

@ Jim: Das ist richtig. CoInitializeEx() immer genau einmal und CoUninitialize() genau einmal in deinem Code. Es ist nicht und sollte nicht wichtig sein, wenn es bereits in Ihrem Thread aufgerufen wurde. Darüber hinaus steuern Sie nicht, wann der Code, den Sie für die Abwärtssteuerung verwenden, COM aufheben wird. Daher ist es gefährlich, sich beim Start auf den Initialisierungsstatus zu verlassen. –

+0

+1, Siehe auch: http://Stackoverflow.com/q/2154151/57428 - sieht so aus, als würde man 'CoInitialize()' mehr aufrufen, was zu einigen seltsamen Problemen führen kann. – sharptooth

0

CoInitializeEx \ CoUninitialize sollte nur von Threads (nicht von DLL-Aufrufe) aufgerufen werden.

BTW, sollten Sie nicht CoInitializeEx \ CoUninitialize in DllMain verwenden!

1

Es folgt peterchen saubere Lösung, wie ich es für einen Thread-sichere COM-Logger-Komponente codiert, die ich wickeln wollte:

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); 
}