C#調用c++dll檔案是一件很麻煩的事情,首先面臨的是資料類型轉換的問題,相信經常做c#開發的都和我一樣把學校的那點c++底子都忘光了吧(語言屬性類別)。
網上有一大堆得轉換對應表,也有一大堆的轉換執行個體,但是都沒有強調一個更重要的問題,就是c#資料類型和c++資料類型占記憶體長度的對應關係。
如果dll檔案中只包含一些基礎類型,那這個問題可能可以被忽略,但是如果是組合類別型(這個叫法也許不妥),如結構體、類類型等,在其中的成員變數的長度的申明正確與否將決定你對dll檔案調用的成敗。
如有以下代碼,其實不是dll檔案的源碼,而是廠商給的c++例子代碼
c++中的結構體申明
typedef struct
{
unsigned charPort;
unsigned longId;
unsigned charCtrl;
unsigned charpData[8];
}HSCAN_MSG;
c++中的函數申明(一個c++程式引用另一個c++的dll檔案)
extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength);
c++中的調用:
....
HSCAN_MSG msg[100];
.....
HSCAN_SendCANMessage(m_nDevice,m_nPort,msg,nFrames);
由上述代碼可見,msg是個結構體的數組。
下面是我的c#的代碼
c#結構體申明:(申明成)
[StructLayout(LayoutKind.Sequential)]
public struct HSCAN_MSG
{
// UnmanagedType.ByValArray, [MarshalAs(UnmanagedType.U1)]這個非常重要,就是申明對應類型和長度的
[MarshalAs(UnmanagedType.U1)]
public byte Port;
[MarshalAs(UnmanagedType.U4)]
public uint nId;
[MarshalAs(UnmanagedType.U1)]
public byte nCtrl;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] pData;
};
c#函數申明
[DllImport("HS2106API.dll")]
public static extern int HSCAN_SendCANMessage(
byte nDevice, byte nPort, HSCAN_MSG[] pMsg, int nLength);
C#函數調用
HSCAN_MSG[] msg = new HSCAN_MSG[1];//發送緩衝區大小可根據需要設定;
for (int yy = 0; yy < msg.Length; yy++)
{
msg[yy] = new HSCAN_MSG();
}
//...結構體中的成員的執行個體化略
HSCAN_SendCANMessage(0x0, 0x0, msg, 1)
那些只能用指標不能用結構體和類的地方
c++中的結構體申明
typedef struct
{
unsigned charPort;
unsigned longId;
unsigned charCtrl;
unsigned charpData[8];
}HSCAN_MSG;
c++中的函數申明(一個c++程式引用另一個c++的dll檔案)
extern "C" int _stdcall HSCAN_SendCANMessage(unsigned char nDevice,unsigned char nPort,HSCAN_MSG *msg,int nLength);
c#中的結構體申明:
[StructLayout(LayoutKind.Sequential)]
public struct HSCAN_MSG
{
[MarshalAs(UnmanagedType.U1)]
public byte Port;
/// <summary>
/// 節點標識,nEFF=1 時(擴充幀),為29 位nEFF=0(標準幀)時,為11 位;
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public uint nId;
[MarshalAs(UnmanagedType.U1)]
public byte nCtrl;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] pData;
};
c#函數的調用:包含使用指標IntPtr替代結構體數組和讀取IntPtr的方法
HSCAN_MSG[] msg1 = new HSCAN_MSG[10];
for (int i = 0; i < msg1.Length; i++)
{
msg1[i] = new HSCAN_MSG();
msg1[i].pData = new byte[8];
}
IntPtr[] ptArray = new IntPtr[1];
ptArray[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)) * 10);
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(HSCAN_MSG)));
Marshal.Copy(ptArray, 0, pt, 1);
int count = HSCAN_ReadCANMessage(0x0, 0,pt, 10);
textBoxStatus.Text += "\r\n" + "讀取0口:" + count.ToString() + "幀資料";
for (int j = 0; j < 10; j++)
{
msg1[j] =
(HSCAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)pt+ j * Marshal.SizeOf(typeof(HSCAN_MSG)))
, typeof(HSCAN_MSG));
textBoxStatus.Text += "\r\n收到0口" + Convert.ToByte(msg1[j].pData[0]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[1]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[2]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[3]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[4]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[5]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[6]).ToString()
+ "|" + Convert.ToByte(msg1[j].pData[7]).ToString();
}