Estoy trabajando en la migración de una aplicación .Net Core 3.1 de Windows a Linux. Tengo un ourdevice.dll en Windows, y el equivalente ourdevice.so creado para Linux. Este dll es un dll nativo que estamos usando en Windows con un contenedor pinvoke.
Cargamos las funciones desde la dll nativa usando DllImports desde kernel32.dll
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError = true)] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule);Creamos delegados para cada una de las funciones a importar:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate short AdcOpen([MarshalAs(UnmanagedType.LPStr)] string adcName, [MarshalAs(UnmanagedType.LPStr)] string protocol, [MarshalAs(UnmanagedType.LPStr)] string port, ref short handle, byte performSwReset); private AdcOpen adcOpen;Luego mapeamos todas las funciones nativas algo como esto:
IntPtr pAddressOfFunction; pDll = LoadLibrary(LibraryName); // check if library is loaded if (pDll != IntPtr.Zero) { // get proc address of the native functions pAddressOfFunction = GetProcAddress(pDll, "AdcOpen"); if (pAddressOfFunction != IntPtr.Zero) adcOpen = (AdcOpen)Marshal.GetDelegateForFunctionPointer(pAddressOfFunction, typeof(AdcOpen)); else message += "Function AdcOpen not found\n"; //snip loading all the exported functions }En la aplicación de Linux, obviamente, no podemos usar kernel32.dll. Estamos escribiendo C# en .Net core en Linux y tenemos el módulo nativo ourdevice.so. ¿Cómo puedo importar las funciones del módulo nativo de Linux a nuestro contenedor de C#? ¿Es posible?
Un par de pensamientos.
Primero, sí, DllImport (P/Invoke) también funciona en Linux (y macOS).
Esto parece un poco extraño:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate short AdcOpen([MarshalAs(UnmanagedType.LPStr)] string adcName, [MarshalAs(UnmanagedType.LPStr)] string protocol, [MarshalAs(UnmanagedType.LPStr)] string port, ref short handle, byte performSwReset); private AdcOpen adcOpen; Con el código de mapeo ( LoadLibrary , GetProcAddress , FreeLibrary ), parece que está haciendo manualmente el trabajo que hace .NET si hace algo como esto, donde deja que .NET haga la función de encontrar y enlazar:
[DllImport("ourdevice.dll", CallingConvention = CallingConvention.Cdecl)] private static extern short AdcOpen([MarshalAs(UnmanagedType.LPStr)] string adcName, [MarshalAs(UnmanagedType.LPStr)] string protocol, [MarshalAs(UnmanagedType.LPStr)] string port, ref short handle, byte performSwReset);¿Alguna razón para hacerlo de manera tan diferente?
Porque si tuviera que hacerlo de esa manera, espero que la versión de Linux se vea así y funcione de la misma manera que en Windows:
[DllImport("libourdevice.so", CallingConvention = CallingConvention.Cdecl)] private static extern short AdcOpen([MarshalAs(UnmanagedType.LPStr)] string adcName, [MarshalAs(UnmanagedType.LPStr)] string protocol, [MarshalAs(UnmanagedType.LPStr)] string port, ref short handle, byte performSwReset); Suponiendo que está utilizando la misma convención de llamada y clasificación de argumentos. La única diferencia es el cambio de nombre de "ourdevice.dll" a "libdevice.so" . También deberá colocar libdevice.so en algún lugar donde se pueda encontrar; junto al ejecutable de la aplicación debería funcionar.
Si aún desea hacerlo manualmente a través del enfoque LoadLibrary / GetProcAddress / FreeLibrary , puede usar los métodos aproximadamente equivalentes en Linux:
Consulte https://stackoverflow.com/a/13492645/3561275 para ver ejemplos y código de muestra sobre cómo usar estos métodos.
Cargar biblioteca:
[DllImport("libdl", ExactSpelling = true)] public static extern IntPtr dlopen(string filename, int flags);ObtenerDirecciónProc:
[DllImport("libdl", ExactSpelling = true)] public static extern IntPtr dlsym(IntPtr handle, string symbol);Librería:
[DllImport("libdl", ExactSpelling = true)] public static extern int dlclose(IntPtr handle);Ejemplo de uso:
const int RTLD_NOW = 0x002; IntPtr pDll = dlopen("ourdevice.so.0", RTLD_NOW); IntPtr pAddressOfFunction = dlsym(pDll, "AdcOpen"); ... dlclose(pDll);