Esto es lo que quiero: tengo una gran base de código C/C++ escrita para POSIX, incluidas algunas cosas muy específicas de POSIX como pthreads. Esto puede compilarse en Cygwin/GCC y ejecutarse como un archivo ejecutable en Windows con la DLL de Cygwin.Referencia a GNU C (POSIX) DLL integrado en GCC contra Cygwin, de C#/NET
Lo que me gustaría hacer es construir la base de código en sí misma en una DLL de Windows a la que puedo hacer referencia desde C# y escribir un contenedor para acceder a algunas partes de ella programáticamente.
He intentado este enfoque con el muy simple ejemplo "hello world" en http://www.cygwin.com/cygwin-ug-net/dll.html y parece que no funciona.
#include <stdio.h>
extern "C" __declspec(dllexport) int hello();
int hello()
{
printf ("Hello World!\n");
return 42;
}
creo que debería ser capaz de hacer referencia a un archivo DLL construido con el código anterior en C# usando algo como:
[DllImport("kernel32.dll")]
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);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int hello();
static void Main(string[] args)
{
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "helloworld.dll");
IntPtr pDll = LoadLibrary(path);
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, "hello");
hello hello = (hello)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(hello));
int theResult = hello();
Console.WriteLine(theResult.ToString());
bool result = FreeLibrary(pDll);
Console.ReadKey();
}
pero no parece que este enfoque funcione. LoadLibrary devuelve nulo. Puede encontrar el archivo DLL (helloworld.dll), es como si no pudiera cargarlo o encontrar la función exportada.
Estoy seguro de que si obtengo este caso básico funcionando, puedo hacer referencia al resto de mi código base de esta manera. ¿Alguna sugerencia o sugerencia, o alguien sabe si lo que quiero es incluso posible? Gracias.
Editar: Examinado mi DLL con Dependency Walker (gran herramienta, gracias) y parece exportar la función correctamente. Pregunta: ¿debería estar haciendo referencia a ella como el nombre de la función que parece encontrar Dependency Walker (_Z5hellov)? Dependency Walker Output http://i44.tinypic.com/1yagqv.png
Edit2: sólo para mostrar que lo he intentado, que une directamente a la DLL en ruta relativa o absoluta (es decir, no utilizando LoadLibrary):
[DllImport(@"C:\.....\helloworld.dll")]
public static extern int hello();
static void Main(string[] args)
{
int theResult = hello();
Console.WriteLine(theResult.ToString());
Console.ReadKey();
}
Esta falla con: " No se puede cargar DLL 'C: ..... \ helloworld.dll':. acceso no válido a la ubicación de la memoria (Excepción de HRESULT: 0x800703E6)
***** Datos 3: ***** Oleg tiene sug congestionadas Dumpbin.exe corriendo en mi DLL, esta es la salida:
volcado de helloworld.dll archivo
Tipo de archivo: DLL
sección contiene los siguientes exportaciones para holamundo.DLL
00000000 characteristics 4BD5037F time date stamp Mon Apr 26 15:07:43 2010 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 000010F0 hello
Resumen
1000 .bss 1000 .data 1000 .debug_abbrev 1000 .debug_info 1000 .debug_line 1000 .debug_pubnames 1000 .edata 1000 .eh_frame 1000 .idata 1000 .reloc 1000 .text
Editar 4 Gracias a todos por la ayuda, que mana ged para hacerlo funcionar. La respuesta de Oleg me dio la información que necesitaba para saber qué estaba haciendo mal.
Hay 2 formas de hacerlo. Una es construir con el indicador del compilador gcc -mno-cygwin, que construye el dll sin cygwin dll, básicamente como si lo hubiera construido en MingW. ¡Construirlo de esta manera hizo que mi ejemplo de Hello World funcionara! Sin embargo, MingW no tiene todas las bibliotecas que cygwin tiene en el instalador, por lo que si su código POSIX tiene dependencias en estas bibliotecas (la mía tiene montones) no puede hacerlo de esta manera. Y si su código POSIX no tenía esas dependencias, ¿por qué no simplemente compilar para Win32 desde el principio? Así que eso no es de mucha ayuda a menos que desee perder tiempo configurando MingW correctamente.
La otra opción es compilar con la DLL de Cygwin. La DLL de Cygwin necesita una función de inicialización init() para ser llamada antes de que pueda ser utilizada. Es por eso que mi código no estaba funcionando antes. El código a continuación carga y ejecuta mi ejemplo de Hello World.
//[DllImport(@"hello.dll", EntryPoint = "#1",SetLastError = true)]
//static extern int helloworld(); //don't do this! cygwin needs to be init first
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int MyFunction();
static void Main(string[] args)
{
//load cygwin dll
IntPtr pcygwin = LoadLibrary("cygwin1.dll");
IntPtr pcyginit = GetProcAddress(pcygwin, "cygwin_dll_init");
Action init = (Action)Marshal.GetDelegateForFunctionPointer(pcyginit, typeof(Action));
init();
IntPtr phello = LoadLibrary("hello.dll");
IntPtr pfn = GetProcAddress(phello, "helloworld");
MyFunction helloworld = (MyFunction)Marshal.GetDelegateForFunctionPointer(pfn, typeof(MyFunction));
Console.WriteLine(helloworld());
Console.ReadKey();
}
Gracias a todos los que respondieron a ~~
La herramienta Dependency Walker (http://www.dependencywalker.com/) es útil para asegurarse de que los archivos DLL se puedan cargar y ver sus dependencias. – WildCrustacean
¿está seguro de que mientras helloworld.dll está en la ruta y se puede encontrar, que los archivos cyg * .dll también están en la ruta desde el lugar donde está ejecutando su ensamblado C#. Si está utilizando Visual Studio, vaya a Propiedades del proyecto y asegúrese de poner todas las rutas requeridas en la propiedad "Variable de entorno". Para otras formas de establecer la ruta, consulte http://stackoverflow.com/questions/428085/how-do-i-set-a-path-in-visual-studio – Fuzz
No subestimo por qué quiere llama a tu cygwin DLL desde C# en todos tus experimentos. ¿Qué ventajas tiene desde el lado pragmático en comparación con el uso de C++ no administrado? Ya necesita tener el subsistema POSIX cargado. ¿Por qué necesita cargar .NET en el mismo proceso? Uno puede comunicarse entre el proceso de Win32 y el proceso administrado escrito en C#, pero no deben * compartir el mismo espacio de direcciones * como lo intenta hacer ahora. – Oleg