2010-12-15 18 views

Respuesta

10

Marcos Ransom es correcto
la solución fácil, limpio y sencillo es exigir inicialización COM por la persona que llama.

Rutina fea
Puede probar su primera llamada - CoCreateInstance probable, y si devuelve CO_E_NOTINITIALIZED, ejecute CoInitialize a sí mismo (y no se olvide de UNINIT en ese caso)

Sin embargo, se sigue siendo problemático para "inyectar" una Coinicialización en un hilo de llamada de una DLL. Así que hay una

Limpiar solución
Vamos a la DLL crear un subproceso de trabajo (lo que significa que el DLL necesita Init y llamadas tearDown), CoInitializeEx en este hilo del mismo, y mover todos los COM llama a ese hilo separado.

+2

Gracias, terminé usando un hilo de trabajo. La inicialización y el desmontaje se realizan en la misma función DLL. 'WaitForSingleObject' se usa para esperar a que se complete el hilo de trabajo. –

7

La manera más fácil es no molestarse, simplemente haga que todo el que use su DLL inicialice primero COM. De lo contrario, se corre el riesgo de arruinar su propia inicialización si lo realizan después de.

Por otro lado, si sus banderas a CoInitializeEx coinciden con las de la aplicación, debería estar bien. De los CoInitializeEx documentation:

múltiples llamadas a CoInitializeEx por el mismo hilo están permitidos siempre y cuando pasan la misma bandera de concurrencia, pero llamadas válidas posteriores volver S_FALSE.

+0

Eso no es realmente útil. Desafortunadamente, la mayoría de la aplicación ya está escrita. Necesito saber cómo determinar si el COM ya se ha inicializado. Me doy cuenta de que esto no es una buena práctica, pero es con lo que tengo que trabajar. –

+0

@Jim: esto es correcto. Simplemente siempre CoInitializeEx() exactamente una vez y CoUninitialize() exactamente una vez en su código. No importa ni debería importar si ya ha sido invocado en su hilo. Además de eso, usted no controla cuando el código que bajó el control desactivará COM, por lo que depender del estado de inicialización cuando arranque es peligroso. –

+0

+1, También vea esto: http://stackoverflow.com/q/2154151/57428 - Parece que se llama a 'CoInitialize()' más de lo que una vez puede ocasionar algunos problemas extraños. – sharptooth

0

CoInitializeEx \ CoUninitialize solo debe invocarse mediante subprocesos (no mediante llamadas Dll).

Por cierto, no debe utilizar CoInitializeEx \ CoUninitialize en DllMain!

1

De ello se deduce solución Peterchen limpio como codifiqué por un componente COM registrador de flujos seguros que quería envolver:

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