使用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。

posted @ 2017-09-14 23:18  叶图大师  阅读(7694)  评论(6编辑  收藏  举报