近期正在做一個藍牙驅動的使用程序,其中有一塊從c++發送數據到C#的部分,網上查了很多資料,大多都是介紹如何通過調用函數獲取用戶數據。並且在消息發送中,很少介紹如何發送一個結構體,並且結構體里面有
byte數組(硬件開發常用)等如何進行處理。
首先c++里面要建立一個dll文件:
1 BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call,LPVOID lpReserved) 2 { 3 switch (ul_reason_for_call) 4 { 5 case DLL_PROCESS_ATTACH: 6 case DLL_THREAD_ATTACH: 7 case DLL_THREAD_DETACH: 8 case DLL_PROCESS_DETACH: 9 break; 10 } 11 return TRUE; 12 } 13 14 struct DevInfo//定義一個我自己的數據結構體 15 { 16 INT32 id; 17 char name[20]; 18 char mac[25]; 19 BYTE data[100]; 20 }; 21 22 extern "C" __declspec(dllexport) int sendTest(INT32 msg)//測試用的發布函數,這個int32參數一點作用都沒有 23 { 24 DevInfo *pinfo=new DevInfo(); 25 pinfo->id=999; 26 sprintf(pinfo->name,"myDevice"); 27 sprintf(pinfo->mac,"19:4x:3a:4a"); 28 29 for (int i = 0; i < 100; i++) 30 { 31 pinfo->data[i]=(BYTE)(0x40+i); 32 } 33 34 COPYDATASTRUCT cpd; /*給COPYDATASTRUCT結構賦值,COPYDATASTRUCT,這個也是系統自己定義的*/ 35 cpd.dwData = 959; //定義一個標示符(我這里沒有用到) 36 cpd.cbData = sizeof(DevInfo); 37 cpd.lpData = pinfo; //將指針的地址通過消息發送 38 39 HWND handle2=::FindWindowA(NULL,(LPCSTR)"Form1");//獲取窗口的句柄 40 41 ::SendMessageA(handle2,WM_COPYDATA,(WPARAM)13,(LPARAM)&cpd); 42 43 //這個地方必須要用sendMessageA,如果用post的話,則可能會出現數據還沒有發送完 44 //系統就把定義的DevInfo 給清理掉了 45 46 std::cout<<"發送數據消息"<<WM_COPYDATA<<std::endl; 47 delete pinfo; 48 49 return 12; 50 }
/*****************************************************************/
c#語言部分
1 namespace Test 2 { 3 public partial class Form1 : Form 4 { 5 public Form1() 6 { 7 InitializeComponent(); 8 } 9 10 private const int WM_COPYDATA = 0x004A;//自己定義一個消息是必須的了 11 12 [DllImport("BlueTooth4.dll", EntryPoint = "sendTest", CallingConvention = CallingConvention.Cdecl)] 13 public static extern Int32 sendTest(int msg);//我們剛才執行的事件,這個是為了測試用才這樣做的 14 15 16 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 17 public struct COPYDATASTRUCT//定義數據傳輸的結構 18 { 19 public IntPtr dwData; 20 public int cbData; 21 public IntPtr lpData; 22 } 23 24 //定義要傳遞的Struct 25 [StructLayout(LayoutKind.Sequential)]//如果發現處理的數據有亂碼的話,則在此處加上 ,CharSet = CharSet.Ansi 26 struct dataInfo 27 { 28 public IntPtr id; 29 30 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)] 31 public string name; 32 33 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)] 34 public string mac; 35 36 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)] 37 public byte[] data; 38 } 39 40 protected override void DefWndProc(ref Message m)//開始處理消息 41 { 42 switch (m.Msg) 43 { 44 //接收CopyData消息,讀取發送過來的數據 45 case WM_COPYDATA: 46 COPYDATASTRUCT copydata = new COPYDATASTRUCT(); 47 COPYDATASTRUCT RecvData = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT)); 48 dataInfo h = (dataInfo)Marshal.PtrToStructure((IntPtr)RecvData.lpData, typeof(dataInfo));//這句和上面的一句就已經將數據進行轉換了 49 break; 50 default: 51 base.DefWndProc(ref m); 52 break; 53 } 54 55 } 56 } 57 }
1、在這次處理的時候,在消息發送過來的數據遇到過是亂碼的情況,可能是字符串的編碼格式有問題( CharSet = CharSet.Ansi)也可能是長度定義的不夠 托管與非托管之間創建了一個數據塊,然后他將兩個數據塊進行對齊,然后將數據讀取出來轉變成我們想要的結構。
2、查過很多地方,好像在介紹c++post數據的時候,很少有介紹到用Byte數組的,而byte數組是在很多硬件設計(串口、usb)下使用比較多的 他的UnmanagedType應該是ByValArray
3、 dataInfo h = (dataInfo)Marshal.PtrToStructure((IntPtr)RecvData.lpData, typeof(dataInfo));//這句和上面的一句就已經將數據進行轉換了 在這句上,我發現(IntPtr)RecvData.lpData,其實發送的就是pInfo的地址,當時想,我要是直接從dll發送給from不就可以了? 結果測試了一下,不行,提示內存錯誤。應該是托管代碼與非托管代碼之間為了數據交互增加的一個接口,一般不允許繞過! 估計有了這樣的數據傳輸方式,一般硬件的數據傳輸基本上就ok了,以后完全可以用c++寫驅動,寫底層接口,用c#做界面開發了!!