Llamar a C++ DLL desde C++ funciona, pero no desde C#
loadlibrary (2)
Tengo una DLL llamada tccdvc.dll que es parte de un SDK disponible aquí:
La DLL se escribió en C ++ y al examinar la DLL se muestra que estaba enlazada con la versión 6.0 del enlazador, así que supongo que fue escrita con VC ++ 6.0. La DLL no viene con el código fuente, solo un archivo .lib y un archivo .h. Todas las funciones exportadas se declaran como extern "C" (por lo que no hay que alterar el nombre de C ++) y con APIENTRY (así que __stdcall).
He escrito un programa C ++ (no .NET) en Visual Studio 2010 en Windows XP SP3 (32 bits) para acceder a este tccdvc.dll. Esto funciona bien, tanto al usar el archivo .lib proporcionado como al usar LoadLibrary / GetProcAddress. También escribí una DLL C ++ (llamémosla mywrapper.dll) que usa tccdvc.dll y, nuevamente, en dos versiones, una que usa el archivo .lib y la otra usando LoadLibrary / GetProcAddress. Nuevamente, esto funciona bien. Este mywrapper.dll usa la convención de llamadas __cdecl. Contiene una función llamada InitMyWrapperDLL () que carga el tccdvc.dll. La versión de mywrapper.dll que utiliza LoadLibrary / GetProcAddress tiene un código como este:
typedef int (APIENTRY *TCCPROCTYPE01)();
HMODULE TCCmodule;
TCCPROCTYPE01 Proc_TCC_DVCOpen;
extern "C" __declspec(dllexport) void InitMyWrapperDLL ()
{ TCCmodule = LoadLibrary("tccdvc.dll");
Proc_TCC_DVCOpen = (TCCPROCTYPE01)GetProcAddress(TCCmodule, "TCC_DVCOpen");
...
}
De nuevo, usar un front-end de C ++ funciona bien. Sin embargo, al llamarlo desde C # (en la misma máquina), la llamada LoadLibrary ("tccdvc.dll") devuelve NULL. En C #, estoy usando:
[DllImport("mywrapper.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="InitMyWrapperDLL")]
private static extern void InitMyWrapperDLL ();
...
InitMyWrapperDLL();
Al compilar mywrapper.dll utilizando el archivo tccdvc.lib proporcionado, también falla, con el código de error 0x8007045a (también conocido como 1114), lo que significa que la inicialización de la DLL falló, y le da a mywrapper.dll el nombre de la DLL. Resulta que la falla se debe a tccdvc.dll, que se carga a través de mywrapper.dll.
El uso de lo siguiente en C # también falla:
[DllImport("tcc.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="TCC_DVCOpen")]
private static extern Int32 TCC_DVCOpen ();
...
TCC_DVCOpen();
También utilicé "inseguro" en la declaración, pero eso no hizo ninguna diferencia. Predecible, porque LoadLibrary () falla, por lo que ni siquiera llega a TCC_DVCOpen ().
Para identificar el problema, utilicé la versión LoadLibrary / GetProcAddress de mywrapper.dll nuevamente y puse el siguiente código en mi programa C #:
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary (string lpLibFileName);
[DllImport("kernel32.dll")]
private static extern Int32 GetLastError ();
...
IntPtr hdll1 = LoadLibrary("mywrapper.dll");
IntPtr hdll2 = LoadLibrary("tccdvc.dll");
Int32 errcode = GetLastError();
Después de esto, hdll1 tiene un valor válido, pero hdll2 es 0. Cuando usa .NET Framework 3.5, GetLastError () devuelve 0x8007045a nuevamente, pero cuando usa .NET 4.0, GetLastError () devuelve 0 (ERROR_SUCCESS).
Usé Process Monitor de Sysinternals para obtener más información y puedo ver que tccdvc.dll se está leyendo y mapeando con éxito. Nada de lo que muestra el Monitor de procesos me da ninguna pista de por qué falla al usar C #, pero no cuando se usa C ++.
¿Algunas ideas? ¡Gracias!
Entonces, su código C funciona cuando se lo llama desde una aplicación C ++, pero el código idéntico falla cuando se lo llama desde un binario .NET, donde falla en LoadLibrary
...
Esto es solo una corazonada, así que no estoy seguro si describe sus circunstancias, pero LoadLibrary
tiene algunos criterios complicados para resolver las rutas de los nombres de DLL relativos, que pueden variar de acuerdo con ciertos parámetros. No los conozco a todos fuera de mi cabeza. Podría ser que algo en el manifiesto EXE del binario .NET le impida encontrar la DLL de acuerdo con esas reglas. Podría intentar ajustar la PATH
entorno PATH
o cargar la DLL con una ruta absoluta. (Puede usar GetModuleFileName
etc. para encontrar la ruta absoluta de su propio código ...)
Tengo algunas sugerencias para ti:
- Puede crear un proyecto de biblioteca de clase C ++ / CLI, luego hacer referencia a él en su proyecto de C #.
- En mi caso descubrí que UnmanagedFunctionPointerAttribute era esencial para la llamada,
- Hubo un caso de que, haga lo que haga, la llamada a .DLL nunca funcionó desde C #, solo el .LIB funcionó para mí (lo que significa implementar mi primera sugerencia). La solución de problemas me llevó a que el ''espacio DLL'' no era apropiado para esa biblioteca en particular.
(Respecto a la última frase: de ninguna manera soy un experto en C ++, de hecho, ese es el único proyecto que hice hasta ahora. Esto ciertamente merece más detalles, pero nunca supe ni tuve el conocimiento para encontrar la fuente del problema, pero se solucionó porque Simplemente lo necesitaba . Gracias por señalar cualquier error / mejores explicaciones.)
Aquí hay algunas soluciones que se aplicaron a mi problema:
¿Cómo usar una devolución de llamada C desde C #? Hay un enlace al .DLL, si quieres ver todo mi código solo pregunta.
Además, algunas herramientas que me han sido útiles en este dominio:
- PE Explorer para ver las exportaciones de DLL
- PInvoke Interop Assistant ayuda a crear declaraciones de PInvoke
Que la fuerza esté con usted :-)