Cómo realizar la interacción entre los lenguajes C y C#
En general, hay dos formas de interactuar entre C# y C:
Usar C/CLI como capa intermedia de proxy
Usar PInvoke para implementar llamadas directas p>
p>
El primer método: es relativamente simple e intuitivo de implementar, y puede realizar llamadas en C# a clases escritas en C. Sin embargo, el problema es que el marco MONO no es compatible con C/CLI. funciona, por lo que no puede lograr una operación multiplataforma sin Microsoft .NET Framework.
Segundo método: la implementación simple no es problemática. Siempre que agregue el atributo DllImportAttribute, puede importar funciones C/C. Sin embargo, el problema es que PInvoke no puede implementar simplemente llamadas a clases C. En Warensoft3D, para utilizar MONO para lograr multiplataforma (por supuesto, DirectX no puede ser multiplataforma), se utiliza este método. Este método se explicará en detalle a continuación.
Plataforma de prueba:
Windows7 de 64 bits, VS2010, .NET4.0
Notas:
Funcionalmente hablando, función PInvoke Only Se admiten llamadas. Se debe agregar "C" externa delante de la función exportada para indicar que el método del lenguaje C se utiliza para compilar y conectarse al exportar la función. Esto garantiza que el nombre de la definición de la función sea el mismo que el exportado. nombre de lo contrario, si el valor predeterminado es el método C, Exportado, el nombre de esa función se volverá confuso y nuestro programa no podrá encontrar el punto de entrada.
Este artículo explicará los siguientes puntos:
Principios básicos de intermodulación
Transferencia de tipos de datos básicos
Transferencia de punteros p> p>
Transferencia de punteros de función
Transferencia de estructuras
Principios básicos de intermodulación
Primero, veamos un concepto muy convencional:" Tipo de datos"
Sabemos que al definir una variable en la mayoría de los lenguajes estáticos, primero se debe especificar su tipo de datos. El llamado tipo de datos es un nombre impuesto por las personas y que es fácil de recordar. La esencia es para indicar cuántos bytes ocupan los datos en la memoria Cuando el programa se está ejecutando, primero encuentra la dirección de los datos, luego lee la memoria correspondiente de acuerdo con la longitud del tipo y luego la procesa.
Después de entender el asunto anterior, existe una manera de interoperar entre todos los lenguajes de programación. Para la intermodulación entre diferentes idiomas, simplemente pase el puntero (dirección de memoria) de los datos a otro idioma y almacene los datos señalados por el puntero en el tipo de datos correspondiente a la longitud según el protocolo de comunicación en el otro idioma. Se deben cumplir los siguientes puntos:
Para lenguajes de programación de máquinas virtuales en tiempo de ejecución como Java y .NET, dado que la máquina virtual transfiere memoria de montón de un lado a otro, al realizar la intermodulación, asegúrese de que El memoria donde los datos que se intermodulan deben estar fijos y no pueden transferirse.
Algunos lenguajes de programación admiten punteros y algunos lenguajes no admiten punteros (como Java. Este problema no es importante. El llamado puntero es en realidad una dirección de memoria. En sistemas operativos de 64 bits, el puntero es un entero de 32 bits y, para sistemas operativos de máquinas de 64 bits, el puntero es un entero de 64 bits. Dado que la mayoría de los idiomas tienen números enteros, puede utilizar números enteros para recibir punteros.
Transferencia de tipos de datos básicos
Durante el proceso de intermodulación, lo más básico a transferir son valores y caracteres, a saber: int, long, float, char, etc. , pero este tipo No ese tipo, algunos tipos de datos en C/C y C# tienen diferentes longitudes. La siguiente tabla enumera las similitudes y diferencias de los tipos de datos comunes:
C/C
Longitud
corto
corto
2Bytes
int
int
4Bytes
long (este tipo se suele confundir al pasar)
int
4Bytes
bool
bool
1Byte
char (carácter de código Ascii)
byte
1Byte
wchar_t (carácter Unicode, este tipo es compatible con Char en C#)
char
2Bytes
float
float
4Bytes
double
double
8Bytes
Los tipos más confusos son long y char. long e int son ambos de 4 bytes, correspondientes al tipo int en C#, mientras que el tipo char en C/C ocupa un byte y se utiliza para representar un carácter de código ASCII, que se puede representar en C#. Un byte es de tipo byte. El tipo de carácter correspondiente en C# debe ser el tipo wchar_t en C/C, que corresponde a un carácter Unicode de 2 bytes.
El siguiente es un ejemplo para ilustrar el proceso de llamada:
Paso 1:
Cree un archivo C Win32DLL, como se muestra en la siguiente figura:
Asegúrese de seleccionar "Exportar símbolos" aquí para exportar símbolos. Haga clic en Listo.
Segundo paso:
Dado que el nombre del proyecto es "TestCPPDLL", se generarán automáticamente dos archivos, TestCPPDLL.h y TestCPPDLL.cpp. El archivo .h. exporte el archivo de declaración de contenido. Para explicar claramente el problema, eliminamos todo el contenido de los dos archivos TestCPPDLL.h y TestCPPDLL.cpp, y luego agregamos el siguiente contenido en TestCPPDLL.h:
La primera línea Una macro llamada "TESTCPPDLL_API" se define en el código, y el contenido correspondiente de esta macro es "__declspec (dllexport)", lo que significa que el contenido modificado posteriormente se define como el contenido que se exportará en la DLL. Por supuesto, no es necesario utilizar esta macro, puede escribir directamente "__declspec (dllexport)" delante de la función que se va a exportar.
El "EXTERN_C" en la segunda línea es una macro definida en "winnt.h". Agregar "EXTERN_C" delante de la función equivale a agregar "C" externa delante de la función, lo cual significa que la función Utilice lenguaje C al compilar y vincular para garantizar que el nombre de la función permanezca sin cambios.
La segunda línea de código es una declaración de una función, lo que indica que la función se puede llamar fuera del módulo. Su definición se implementa en TestCPPDLL.cpp. El código de TestCPPDLL.cpp es el siguiente:
Paso 3:
Antes de compilar la DLL de C, debe realizar la siguiente configuración. Seleccione "C/C "|"Avanzado" en el cuadro de diálogo de propiedades del proyecto y cambie el valor. de la opción Compilar AS a "C". Luego Aceptar y compilar.
El archivo DLL generado se muestra a continuación:
Paso 4:
Primero, agregue una aplicación C# si desea llamar a C en el archivo DLL C#. Primero agregue un método estático a la clase C # y use DllImportAttribute para modificar el método. El código es el siguiente:
El primer parámetro en DllImport es para indicar la ubicación del archivo DLL. El parámetro "EntryPoint" se utiliza para indicar el nombre de la función correspondiente en C/C. La palabra clave "extern" indica que el método Add declarado es una llamada externa.
Una vez declarado el método, se puede utilizar como llamar a un método estático normal.
El siguiente es un programa de muestra: