实现c函数和参数指针结构到c#
本文关键字:结构 指针 参数 函数 实现 | 更新日期: 2023-09-27 18:06:24
我有一个关于实现c++ DLL到c#的问题
我的头文件代码如下:
typedef struct{
uchar Command[4];
ushort Lc;
uchar DataIn[512];
ushort Le;
}CON_SEND;
typedef struct{
ushort LenOut;
uchar DataOut[512];
uchar outA;
uchar outB;
}CON_RESP;
SDK_C_DLL_API int Csdk_Cmd(uchar port, CON_SEND * ConSend,CON_RESP * ConResp);
SDK_C_DLL_API int Csdk_Open(uchar port);
和c#类
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CON_SEND
{
public byte[] Command;
public System.UInt16 Lc;
public byte[] DataIn;
public System.UInt16 Le;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CON_RESP
{
public System.UInt16 LenOut;
public byte[] DataOut;
public byte outA;
public byte outB;
}
[DllImport("SDK_C.dll")]
public static extern System.Int16 Csdk_Cmd(System.Byte port, ref CON_SEND ConSend, ref CON_RESP ConResp);
[DllImport("SDK_C.dll")]
public static extern System.Int16 Csdk_Open(System.Byte port);
现在我使用Csdk_Cmd;
if(SDK_C.Csdk_Open(0x00)== 0)
lblStatus.Text = "Gate 0 is busy";
else
lblStatus.Text = "You can use Gate 0";
但是当我尝试使用此代码Csdk_Cmd给定的"NotSupportedException"
CON_SEND SendCon = new CON_SEND();
SendCon.Command = new byte[] { 0xE0, 0xA0, 0xC4, 0x72 };
SendCon.DataIn = new byte[]{0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xA0};
SendCon.Lc = 0x0007;
SendCon.Le = 0x0000;
CON_RESP RespCon = new CON_RESP();
RespCon.DataOut = new byte[512];
SDK_C.Csdk_Cmd(0, ref SendCon, ref RespCon); // THIS LINE
您的结构声明不正确。C代码具有内联字节数组,简单地说,它们与byte[]
的默认封送不匹配。修复它的最简单方法是使用MarshalAs(UnmanagedType.ByValArray)
。这样的:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CON_SEND
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] Command;
public ushort Lc;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] DataIn;
public ushort Le;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CON_RESP
{
public ushort LenOut;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] DataOut;
public byte outA;
public byte outB;
}
你的函数也有错误的返回值。c# int
映射到c# int
。所以它们的声明应该是:
[DllImport("SDK_C.dll")]
public static extern int Csdk_Cmd(byte port, ref CON_SEND ConSend,
ref CON_RESP ConResp);
[DllImport("SDK_C.dll")]
public static extern int Csdk_Open(byte port);
另一件需要仔细检查的事情是调用约定。上面的c#函数使用默认的Stdcall
。你确定C代码也可以吗?由于返回值和函数名之间没有指定任何内容,因此我怀疑函数实际上是Cdecl
。在这种情况下,您需要:
[DllImport("SDK_C.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Csdk_Cmd(byte port, ref CON_SEND ConSend,
ref CON_RESP ConResp);
[DllImport("SDK_C.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Csdk_Open(byte port);
这实际上更像是一种猜测,但是您在c++中使用内联(固定)数组声明结构体,因此您也需要告诉c#这样做。你可以设置固定大小的缓冲区:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CON_SEND
{
public fixed byte Command[4];
public System.UInt16 Lc;
public fixed byte DataIn[512];
public System.UInt16 Le;
}
警告:此代码需要使用不安全编译,并出现在不安全块中。
或者,您可以考虑更好地为您的结构定义封送属性:
http://www.codeproject.com/Articles/66244/Marshaling-with-C-Chapter-2-Marshaling-Simple-Type(见@DavidHeffernan,答案如下)