使用C#进行应用程序间通信(WPF与Unity通信)
首先程序主体来自网络,我只是应用在我自己的项目中,其中出现了一系列的问题,有些已经解决,有些使用了折中的方案,如果有大神能够给予知道,感激不尽!
首先是发送端程序:
这是我的程序任务执行主界面,此处已经显示了每个消防队员的空呼数据;
消防员在着火的大楼内部的具体方位采用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。