2011-07-20 13 views
7

Necesito alguna aclaración con respecto a los problemas de tiempo de ejecución/montón al eliminar un objeto que se creó en una DLL. Necesita alguna introducción antes de llegar a mis preguntas ...Eliminación de un objeto que se creó en una DLL

En mi proyecto, una DLL (que se especifica por su nombre) devuelve un nuevo objeto de Grabber. En una versión anterior de mi código, el archivo DLL exporta una función como esta:

extern "C" 
__declspec(dllexport) Grabber* CreateGrabber(string settings) 
{ 
    return new SomeSpecificGrabber(settings); 
} 

en el EXE He utilizado una función estática como este para crear un nuevo objeto en forma de mano:

static Grabber* createGrabberObject(const std::string& grabberType, const std::string& grabberSettings) 
{ 
    FARPROC hProc = 0; 

    // load dll with the name of grabberType 
    HMODULE hDLL = LoadLibrary(grabberType.c_str()); 

    // get address for CreateGrabber function 
    hProc = GetProcAddress(hDLL, "CreateGrabber"); 

    // instantiate a function pointer of our type and typecast the address 
    // of the CreateGrabber function to this type 
    CreateGrabberFunctionType CreateGrabberFunction = (CreateGrabberFunctionType)hProc; 

    // call CreateGrabber in DLL to get a Grabber object 
    return CreateGrabberFunction(grabberSettings); 
} 

en el EXE el tiempo de vida de un objeto en forma de mano es administrado por un puntero inteligente:

shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings")); 

Esta todo funcionó bien siempre y cuando compilé el EXE y DLL con el ajuste /MDd (VC++ 2010), WHI ch significa que EXE y DLL usaron el mismo montón.

Ahora quiero compilar mi solución con la configuración /MTd. Con esto obtuve una afirmación en tiempo de ejecución del tipo _CrtIsValidHeapPointer para el objeto de cadena de configuración que pasé a la DLL. Esto tiene sentido porque el DLL intenta eliminar un objeto de cadena que se creó en el EXE. Y ya no usan el mismo montón.

que tiene alrededor de este problema cambiando la función DLL exportado un poco (en lugar de const char*string):

extern "C" 
__declspec(dllexport) Grabber* CreateGrabber(const char* settings) 
{ 
    return new SomeSpecificGrabber(settings); 
} 

Y en createGrabberObject que pase grabberSettings.c_str() en lugar de grabberSettings a la función DLL.

Ahora todo vuelve a funcionar bien. Pero ahora viene mi primera pregunta: ¿Por qué no obtengo la aserción _CrtIsValidHeapPointer cuando se borra myGrabberObj? El objeto se creó desde el DLL, pero se elimina desde el EXE (mediante el puntero inteligente). ¿Por qué no tengo el mismo problema aquí que con el objeto de cadena anterior?

supongo una solución limpia sería que la DLL exporta también una función como esta:

extern "C" 
__declspec(dllexport) void DeleteGrabber(Grabber* grabber) 
{ 
    delete grabber; 
} 

Entonces tendría también una función estática en mi EXE que llama a DeleteGrabber en un archivo DLL:

static void deleteGrabberObject(const std::string& grabberType, Grabber* grabber) 
{ 
    FARPROC hProc = 0; 

    // load dll with the name of grabberType 
    HMODULE hDLL = LoadLibrary(grabberType.c_str()); 

    // get address for DeleteGrabber function 
    hProc = GetProcAddress(hDLL, "DeleteGrabber"); 

    // instantiate a function pointer of our type and typecast the address 
    // of the DeleteGrabber function to this type 
    DeleteGrabberFunctionType DeleteGrabberFunction = (DeleteGrabberFunctionType)hProc; 

    // call DeleteGrabber in DLL 
    DeleteGrabberFunction(grabber); 
} 

Esta función estática podría entonces ser llamado automáticamente por el puntero inteligente:

shared_ptr<Grabber> myGrabberObj = shared_ptr<Grabber>(createGrabberObject("SomeGrabber.DLL", "Settings"), 
boost::bind(deleteGrabberObject, "SomeGrabber.DLL", _1)); 

Esto también funciona. Pero aquí viene mi segunda pregunta: Las funciones estáticas createGrabberObject y deleteGrabberObject ambas cargan la DLL. ¿Significa que se crean dos montones diferentes porque se cargan dos instancias de la DLL (entonces esta solución no resolvería mi problema en absoluto)? ¿O estas dos funciones estáticas usan el mismo montón?

Espero que alguien pueda explicar lo que está pasando aquí ...

Respuesta

5

La DLL se cuenta de referencia, no se carga dos veces, y cuando se usa LoadLibrary, solo se carga una vez y usan el mismo montón. La función estática es la solución normal a este problema.

+0

Gracias! ¡Esto fue realmente rápido! –

3

Para su segunda pregunta, solo porque cargue la DLL dos veces no significa que hay dos instancias. El sistema operativo es lo suficientemente inteligente como para cargarlo solo una vez.

EDITAR: Para la primera pregunta, probablemente sea porque el puntero compartido nunca sale realmente del alcance O porque el tiempo de ejecución de VC no puede detectar el caso correctamente (no falló fatalmente pero la memoria no era t liberado).

+0

¡Gracias por la respuesta rápida! En realidad, el puntero compartido sale del alcance (se golpea un punto de interrupción en '~ SomeSpecificGrabber' en la DLL). Parece que el tiempo de ejecución de VC simplemente no lo detectó. –

3

Bueno, es porque (en su caso), dos montones están funcionando. La DLL está teniendo otro heap-manager, y EXE está teniendo diferente. Esto puede ser debido a: contradicción

  • de depuración/Release
  • El VC-tiempo de ejecución utilizado (uno es VC8, uno es VC9 por ejemplo). ¡Compuesto por arriba también!
  • Diferentes modelos utilizados para compilar DLL/EXE (/MT[d] bandera, vincular como static-lib, etc.).
  • Has mostrado delete contra new que es válido. Pero eso también puede ser el caso donde new es en realidad malloc/HeapAlloc.

En resumen, la memoria asignada por X heap-gerente no será encontrado por Y heap-gerente, y por lo tanto la afirmación!

Cuestiones relacionadas