首先程序主體來自網絡,我只是應用在我自己的項目中,其中出現了一系列的問題,有些已經解決,有些使用了折中的方案,如果有大神能夠給予知道,感激不盡!
首先是發送端程序:
這是我的程序任務執行主界面,此處已經顯示了每個消防隊員的空呼數據;
消防員在着火的大樓內部的具體方位采用Unity3d進行開發,因此我wpf程序需要將隊員的位置信息傳輸到三維場景中;
發送數據的程序如下:
/// <summary> /// 查找窗口 /// </summary> [DllImport("user32.dll")] public static extern IntPtr FindWindowA(string lpClassName, string lpWindowName); //user32.dll中的SendMessage [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, ref COPYDATASTRUCT lParam); //本窗口句柄 public IntPtr m_hWnd; //接收方窗口句柄 private IntPtr hWndPalaz; /// <summary> /// 發送windows消息方便user32.dll中的SendMessage函數使用 /// </summary> public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; public IntPtr lpData; } //宏定義 private const ushort IPC_VER = 1; private const int IDT_ASYNCHRONISM = 0x0201; private const uint WM_COPYDATA = 0x004A; private const ushort IPC_CMD_GF_SOCKET = 1; private const ushort IPC_SUB_GF_SOCKET_SEND = 1; private const int IPC_SUB_GF_CLIENT_READY = 1; private const int IPC_CMD_GF_CONTROL = 2; private const int IPC_BUFFER = 10240;//最大緩沖長度 //數據包頭配合使用 public unsafe struct IPC_Head { public ushort wVersion; public ushort wPacketSize; public ushort wMainCmdID; public ushort wSubCmdID; } public unsafe struct IPC_Buffer { public IPC_Head Head; //IPC_Head結構 public fixed byte cbBuffer[IPC_BUFFER]; //指針 存放數據 利用byte[]接收存放 } /// <summary> /// 將字符串轉換為指針用於發送 /// </summary> public void SendData(string data) { hWndPalaz = FindWindowA(null, "Navigation2.0");//獲取接收窗口句柄 if (hWndPalaz != null) { //獲得當前窗口句柄 m_hWnd = FindWindowA("Mission", null); byte[] bytes = Encoding.UTF8.GetBytes(data.PadRight(186, '/')); IntPtr pData = Marshal.AllocHGlobal(2 * bytes.Length); Marshal.Copy(bytes, 0, pData, bytes.Length); SendData(hWndPalaz, IPC_CMD_GF_SOCKET, IPC_SUB_GF_SOCKET_SEND, pData, (ushort)bytes.Length); } } /// <summary> /// SendMessage發送 /// </summary> /// <param name="hWndServer">指針</param> /// <param name="wMainCmdID">主命令</param> /// <param name="wSubCmdID">次命令</param> /// <param name="pData">json轉換的指針</param> /// <param name="wDataSize">數據大小</param> /// <returns></returns> public unsafe bool SendData(IntPtr hWndServer, ushort wMainCmdID, ushort wSubCmdID, IntPtr pData, ushort wDataSize) { //給IPCBuffer結構賦值 IPC_Buffer IPCBuffer; IPCBuffer.Head.wVersion = IPC_VER; IPCBuffer.Head.wSubCmdID = wSubCmdID; IPCBuffer.Head.wMainCmdID = wMainCmdID; IPCBuffer.Head.wPacketSize = (ushort)Marshal.SizeOf(typeof(IPC_Head)); //內存操作 if (pData != null) { //效驗長度 if (wDataSize > 1024) return false; //拷貝數據 IPCBuffer.Head.wPacketSize += wDataSize; byte[] bytes = new byte[IPC_BUFFER]; Marshal.Copy(pData, bytes, 0, wDataSize); for (int i = 0; i < IPC_BUFFER; i++) { IPCBuffer.cbBuffer[i] = bytes[i]; } } //發送數據 COPYDATASTRUCT CopyDataStruct; IPC_Buffer* pPCBuffer = &IPCBuffer; CopyDataStruct.lpData = (IntPtr)pPCBuffer; CopyDataStruct.dwData = (IntPtr)IDT_ASYNCHRONISM; CopyDataStruct.cbData = IPCBuffer.Head.wPacketSize; SendMessage(hWndServer, 0x004A, (int)m_hWnd, ref CopyDataStruct); return true; }
SendData函數重載了,只要調用void SendData(string args)即可。
接下來是接收端,接收端比較特殊,因為是Unity程序發布的exe,不是普通的窗口程序;
首先是我的三維程序界面:
地面是直接加載的百度地圖或高德地圖,手動繪制建築物的輪廓,再通過設置層數、層高等參數即可生成樓層;
數據接收的代碼:
using UnityEngine; using System.Collections; using System.Net.Sockets; using System.Net; using System; using System.Threading; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; public class DataClient : MonoBehaviour { List<string> caches = new List<string>(); void Start() { //安裝鈎子 HookLoad(); }void OnGUI() { GUI.contentColor = Color.red; GUILayout.Label(caches.Count.ToString()); for (int i = 0; i < caches.Count; i++) GUILayout.Label(caches[i].Length + ":" + caches[i]); } void OnApplicationQuit() {
//關閉鈎子
HookClosing();
}
//鈎子接收消息的結構
public struct CWPSTRUCT { public int lparam; public int wparam; public uint message; public IntPtr hwnd; }
//建立鈎子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint dwThreadId);
//移除鈎子 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern bool UnhookWindowsHookEx(int idHook); //把信息傳遞到下一個監聽 [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); //回調委托 private delegate int HookProc(int nCode, int wParam, int lParam); //鈎子 int idHook = 0; //是否安裝了鈎子 bool isHook = false; GCHandle gc; private const int WH_CALLWNDPROC = 4; //鈎子類型 全局鈎子 //定義結構和發送的結構對應 public unsafe struct IPC_Head { public int wVersion; public int wPacketSize; public int wMainCmdID; public int wSubCmdID; } private const int IPC_BUFFER = 10240;//最大緩沖長度 public unsafe struct IPC_Buffer { public IPC_Head Head; public fixed byte cbBuffer[IPC_BUFFER]; //json數據存的地方 } public struct COPYDATASTRUCT { public int dwData; public int cbData; public IntPtr lpData; } void OnDestroy() { //關閉鈎子 HookClosing(); } private void HookLoad() { Debug.Log("開始運行"); //安裝鈎子 { //鈎子委托 HookProc lpfn = new HookProc(Hook); //關聯進程的主模塊 IntPtr hInstance = IntPtr.Zero;// GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); idHook = SetWindowsHookEx(WH_CALLWNDPROC, lpfn, hInstance, (uint)AppDomain.GetCurrentThreadId()); if (idHook > 0) { Debug.Log("鈎子[" + idHook + "]安裝成功"); isHook = true; //保持活動 避免 回調過程 被垃圾回收 gc = GCHandle.Alloc(lpfn); } else { Debug.Log("鈎子安裝失敗"); isHook = false; UnhookWindowsHookEx(idHook); } } } //卸載鈎子 private void HookClosing() { if (isHook) { UnhookWindowsHookEx(idHook); } } private bool _bCallNext; public bool CallNextProc { get { return _bCallNext; } set { _bCallNext = value; } } //鈎子回調 private unsafe int Hook(int nCode, int wParam, int lParam) { try { IntPtr p = new IntPtr(lParam); CWPSTRUCT m = (CWPSTRUCT)Marshal.PtrToStructure(p, typeof(CWPSTRUCT)); if (m.message == 74) { COPYDATASTRUCT entries = (COPYDATASTRUCT)Marshal.PtrToStructure((IntPtr)m.lparam, typeof(COPYDATASTRUCT)); IPC_Buffer entries1 = (IPC_Buffer)Marshal.PtrToStructure((IntPtr)entries.lpData, typeof(IPC_Buffer)); IntPtr intp = new IntPtr(entries1.cbBuffer); string str = new string((sbyte*)intp - wParam); caches.Add(str); if (caches.Count > 16) caches.RemoveAt(0); } if (CallNextProc) { return CallNextHookEx(idHook, nCode, wParam, lParam); } else { return CallNextHookEx(idHook, nCode, wParam, lParam); } } catch (Exception ex) { Debug.Log(ex.Message); return 0; } } }
直接將腳本掛在某個物體上,然后注意的是發布后程序的名字(運行exe在窗口標題欄顯示的名字),因為發送端是通過這個名字來找到該窗口的。
運行的效果如下:
注意,由於程序中用到的unsafe,不安全代碼,因此要講工程設置為允許不安全代碼,而unity中則需要在工程Assets的根目錄創建文本文件,內容設置:-unsafe,注意不要有多余的空格什么的,然后文件復制4分,文件名分別為:us.rsp、smcs.rsp、gmcs.rsp
、csc.rsp、boo.rsp,其實不是全部都要,每個針對一種語言的不安全代碼,文件創建好了之后要重啟一下unity。