Punteros en C # para recuperar la referencia de la función DllImport
-
23-09-2019 - |
Pregunta
Me estoy refiriendo a una DLL en mi proyecto de C # de la siguiente manera:
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, string cout, string flimit,
string frate, string fwindow, string fincrement, string fbird,
string fparameter, string fvalidation, string fcoupon);
La función FeeCalculation se exporta como sigue en el DLL:
extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin,
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);
La función DLL devuelve una referencia a su estructura interna en forma de char * por lo que si se va a hacer referencia a este archivo DLL en C ++, tendría que hacer lo siguiente para hacer el cálculo y obtener las estructuras devueltos:
FeeCalculation(buff, (char *)&fans, (char *)fl, (char *)ft, (char *)fw, (char *)fi, (char *)fe, (char *)&fm, (char *)val, (char *)cpn);
Ahora, ¿cómo puedo recuperar esos valores devueltos por referencia usando C #? Es decir, ¿cómo lo hago lo mismo en C # para obtener las estructuras vuelto a conseguir mis cálculos vuelto? Sé que necesito para crear un método inseguro, pero no tengo muy claro sobre cómo tratar con las direcciones de memoria en C # como lo haría en C ++.
Editar:? A continuación los estados utilizar IntPtr pero ¿cómo colocan en una estructura idéntica por lo que se puede hacer referencia a los campos de la estructura
Edit: Aquí está la estructura devuelta que estoy interesado en (cout):
struct feeAnswer {
unsigned int fee;
unsigned int tax1;
unsigned int tax2;
unsigned int tax3;
unsigned int tax4;
unsigned int surcharge1;
unsigned int surcharge2;
unsigned int validationFee;
unsigned int couponFee1;
unsigned int couponFee2;
unsigned int couponFee3;
unsigned int couponFee4;
unsigned short int dstay; //Day Stay
unsigned short int mstay; //Minute Stay
};
Esta es la (CIN) que iba a pasar junto con otras estructuras (son cero byte en este momento, quiero conseguir que esto funcione primero y luego voy a poner en práctica el resto):
struct feeRequest {
unsigned char day;
unsigned char month;
unsigned int year; //2000 ~ 2099
unsigned char hour;
unsigned char minute;
unsigned char rate;
unsigned char validation;
unsigned char coupon1;
unsigned char coupon2;
unsigned char coupon3;
unsigned char coupon4;
};
Solución
Edit: ahora que tenemos estructuras de trabajar, una solución mejor es posible. Sólo declarar estructuras en C # que responden a su C ++ estructuras, y utilizarlos en la declaración externa
[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer {
public uint fee;
public uint tax1;
public uint tax2;
public uint tax3;
public uint tax4;
public uint surcharge1;
public uint surcharge2;
public uint validationFee;
public uint couponFee1;
public uint couponFee2;
public uint couponFee3;
public uint couponFee4;
public ushort dstay; //Day Stay
public ushort mstay; //Minute Stay
};
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct feeRequest {
public byte day;
public byte month;
public uint year; //2000 ~ 2099
public byte hour;
public byte minute;
public byte rate;
public byte validation;
public byte coupon1;
public byte coupon2;
public byte coupon3;
public byte coupon4;
};
[DllImport ("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation (
feeRequest cin,
out feeAnswer cout,
...
....
respuesta original (antes de tener estructuras) a continuación
Me parece que estas no son las referencias a las cadenas internas, sino más bien punteros a buffers de cadena que serán llenados por la llamada. Si se va regresando punteros de cadena, char**
entonces estos serían declarados en lugar de char*
.
Así que creo que estos son sólo parámetros out estándar. Sólo hay un montón de ellos. Por lo que su C # interoperabilidad se vería así
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string cout,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string flimit,
o esto si sus "cadenas" no son realmente cadenas
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] cout,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] flimit,
....
Otros consejos
El char * parámetros en este caso no son secuencias pero punteros a trozos de bytes que representan los datos en bruto. Usted debe reunir sus parámetros como las instancias del tipo IntPtr, conjuntamente con Marshal.AllocHGlobal para crear un trozo de la memoria y luego Marshal.PtrToStructure para convertir ese bloque de memoria en un tipo de .NET utilizable.
A modo de ejemplo:
[StructLayout(LayoutKind.Sequential)]
struct MyUnmanagedType
{
public int Foo;
public char C;
}
IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyUnmanagedType)));
try
{
FeeCalculation(memory);
MyUnmanagedType result = (MyUnmanagedType)Marshal.PtrToStructure(
memory, typeof(MyUnmanagedType));
}
finally
{
Marshal.FreeHGlobal(memory);
}
Para responder a su edición, es necesario crear una estructura, y luego usar el StructLayoutAttribute en los campos con el fin de hacer que el orden de los bytes y el relleno de la misma que la DLL original, lo hicieron.