De hecho, obtuve una DLL de C ++ (en funcionamiento) que quiero importar a mi proyecto C # para llamar a sus funciones.
Funciona cuando especifico la ruta completa a la DLL, así:
string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
El problema es que será un proyecto instalable, por lo que la carpeta del usuario no será la misma (por ejemplo, Pierre, Paul, Jack, Mum, Dad, ...) dependiendo de la computadora / sesión donde se ejecute.
Entonces me gustaría que mi código sea un poco más genérico, como este:
/*
goes right to the temp folder of the user
"C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
"C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
"C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/
string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);
El gran problema es que "DllImport" desea un parámetro de "cadena constante" para el directorio de la DLL.
Entonces mi pregunta es: ¿Qué podría hacerse en este caso?
Respuestas:
Contrariamente a las sugerencias de algunas de las otras respuestas, el uso del
DllImport
atributo sigue siendo el enfoque correcto.Sinceramente, no entiendo por qué no puedes hacer lo mismo que todos los demás en el mundo y especificar una ruta relativa a tu DLL. Sí, la ruta en la que se instalará su aplicación difiere en las computadoras de diferentes personas, pero esa es básicamente una regla universal cuando se trata de la implementación. El
DllImport
mecanismo está diseñado con esto en mente.De hecho, ni siquiera
DllImport
eso lo maneja. Son las reglas de carga de DLL de Win32 nativas las que gobiernan las cosas, independientemente de si está utilizando los prácticos contenedores administrados (el encargado de invocación P / Invoke solo llamaLoadLibrary
). Estas reglas se enumeran con gran detalle aquí , pero las importantes se extraen aquí:Entonces, a menos que esté nombrando su DLL de la misma manera que una DLL del sistema (lo que obviamente no debería hacer, nunca, bajo ninguna circunstancia), el orden de búsqueda predeterminado comenzará a buscar en el directorio desde el que se cargó su aplicación. Si coloca la DLL allí durante la instalación, se encontrará. Todos los problemas complicados desaparecen si solo usa rutas relativas.
Solo escribe:
Pero si eso no funciona por alguna razón, y necesita forzar a la aplicación a buscar en la DLL un directorio diferente, puede modificar la ruta de búsqueda predeterminada utilizando la
SetDllDirectory
función .Tenga en cuenta que, según la documentación:
Por lo tanto, siempre que llame a esta función antes de llamar a la función importada de la DLL por primera vez, puede modificar la ruta de búsqueda predeterminada utilizada para ubicar las DLL. El beneficio, por supuesto, es que puede pasar un valor dinámico a esta función que se calcula en tiempo de ejecución. Eso no es posible con el
DllImport
atributo, por lo que aún usará una ruta relativa (solo el nombre de la DLL) allí, y dependerá del nuevo orden de búsqueda para encontrarlo.Tendrás que P / Invocar esta función. La declaración se ve así:
fuente
.dll
y otros sistemas agregarán la extensión apropiada en Mono (por ejemplo,.so
en Linux). Esto puede ayudar si la portabilidad es una preocupación.SetDllDirectory
. ¡También puede cambiarEnvironment.CurrentDirectory
y todas las rutas relativas se evaluarán desde esa ruta!AddDllDirectory
por otro lado ...DllImport
es más que solo un contenedorLoadLibrary
. También considera el directorio del ensamblaje en elextern
que se define el método . LasDllImport
rutas de búsqueda se pueden limitar adicionalmente usandoDefaultDllImportSearchPath
.Incluso mejor que la sugerencia de uso de Ran
GetProcAddress
, simplemente llameLoadLibrary
antes de cualquier llamada a lasDllImport
funciones (con solo un nombre de archivo sin ruta) y usarán el módulo cargado automáticamente.He usado este método para elegir en tiempo de ejecución si cargar una DLL nativa de 32 o 64 bits sin tener que modificar un montón de funciones P / Invoke-d. Pegue el código de carga en un constructor estático para el tipo que tiene las funciones importadas y todo funcionará bien.
fuente
FunctionLoader
código.Si necesita un archivo .dll que no está en la ruta o en la ubicación de la aplicación, entonces no creo que pueda hacer eso, porque
DllImport
es un atributo, y los atributos son solo metadatos que se establecen en tipos, miembros y otros elementos del lenguajeUna alternativa que puede ayudarlo a lograr lo que creo que está intentando es usar el nativo a
LoadLibrary
través de P / Invoke, para cargar un .dll de la ruta que necesita, y luego usarloGetProcAddress
para obtener una referencia de la función que necesita de ese .dll. Luego, utilícelos para crear un delegado que pueda invocar.Para que sea más fácil de usar, puede configurar este delegado en un campo en su clase, de modo que usarlo parezca llamar a un método miembro.
EDITAR
Aquí hay un fragmento de código que funciona y muestra lo que quise decir.
Nota: No me molesté en usarlo
FreeLibrary
, por lo que este código no está completo. En una aplicación real, debe tener cuidado al liberar los módulos cargados para evitar una pérdida de memoria.fuente
Siempre que conozca el directorio donde se pueden encontrar sus bibliotecas de C ++ en tiempo de ejecución, esto debería ser simple. Puedo ver claramente que este es el caso en su código. Su
myDll.dll
estaría presente en el interiormyLibFolder
del directorio dentro de la carpeta temporal del usuario actual.Ahora puede continuar usando la instrucción DllImport usando una cadena constante como se muestra a continuación:
Justo en tiempo de ejecución antes de llamar a la
DLLFunction
función (presente en la biblioteca de C ++) agregue esta línea de código en el código C #:Esto simplemente le indica al CLR que busque las bibliotecas C ++ no administradas en la ruta del directorio que obtuvo en el tiempo de ejecución de su programa.
Directory.SetCurrentDirectory
La llamada establece el directorio de trabajo actual de la aplicación en el directorio especificado. Si sumyDLL.dll
está presente en la ruta representada por laassemblyProbeDirectory
ruta, entonces se cargará y la función deseada se llamará a través de p / invoke.fuente
establecer la ruta dll en el archivo de configuración
antes de llamar al dll en tu aplicación, haz lo siguiente
luego llame al dll y puede usar como a continuación
fuente
DllImport funcionará bien sin la ruta completa especificada siempre que la dll esté ubicada en algún lugar de la ruta del sistema. Es posible que pueda agregar temporalmente la carpeta del usuario a la ruta.
fuente
Si todo falla, simplemente coloque la DLL en la
windows\system32
carpeta. El compilador lo encontrará. Especifique la DLL desde la cual cargar:DllImport("user32.dll"...
configureEntryPoint = "my_unmanaged_function"
para importar la función no administrada que desee a su aplicación C #:Fuente e incluso más
DllImport
ejemplos: http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspxfuente