【轉】 unity進程間通信 unity hook WM_COPYDATA


原貼: 

unity3d進程通信利用WM_COPYDARE和HOOK

hello,最近用unity做了進程通信,應該是和c++的PC端實現通信,才開始一頭霧水,后來實現了才知道好繁雜......先感謝對我提供幫助的百度,谷歌以及游戲圈的大大們。

 

在進程通信中很多方法,但是wm_copydate絕對要比別的什么內存共享好了許多。unity大部分用c#語言,c#本事Forms這個dll里面也提供了對windows消息的接收但是在unity中無法很好地使用System.Windows.Forms,所以在下面我的代碼我用unity發送進程消息的是 user32.dll 中的sendMessage,對於接收則是用的hook(鈎子)。下面代碼是unity打包出來的exe的通信。就不和c++通信了,原理都一樣。

 

整個過程要導入user32.dll ,所以在需要using System.Runtime.InteropServices;剩下需要引用什么添加什么,里面還有發送json數據以及許多細節的c#取地址讀取地址,我也一並分享大家樂,吐舌頭以后也要幫我哦.

 

發送端(利用sendMessage),test1.cs掛載在unity場景中

 

using System;
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;


public class test15 : MonoBehaviour
{
    #region
    public IntPtr m_hWnd;  

    /// <summary>
    /// 發送windows消息方便user32.dll中的SendMessage函數使用
    /// </summary>
    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int cbData;
        public IntPtr lpData;
    }

    //user32.dll中的SendMessage
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, ref COPYDATASTRUCT lParam);
    //user32.dll中的獲得窗體句柄
    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);

    //宏定義

    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;//最大緩沖長度

    //查找的窗體

    private IntPtr hWndPalaz;

     //數據包頭配合使用
    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];  //指針        存放json數據 利用byte[]接收存放
    }

 #endregion

    /// <summary>
    /// 發送把json轉換為指針傳到SendData()方法
    /// </summary>
    private void sendJson() {
        IntPtr hWndPalaz = FindWindow(null, "你們要查找的窗體名字");//就是窗體的的標題
        Debug.Log(hWndPalaz);
        if (hWndPalaz != null)
        {
            //獲得游戲本身句柄 
            m_hWnd = FindWindow("UnityWndClass", null);


           

            //發送用戶准備好消息(這個是個json插件我就不提供了你們自己搞自己的json new一個實例這里不改會報錯)
            JSONObject jsStart = new JSONObject();
            jsStart.AddField("六六", "是我");
            jsStart.AddField("sya", "學習游戲為了裝逼小組");
            jsStart.AddField("doing", "this is your time");


            string uRstr = jsStart.ToString();
            byte[] bytes = Encoding.UTF8.GetBytes(uRstr);
            IntPtr pData = Marshal.AllocHGlobal(2 * bytes.Length);
            Marshal.Copy(bytes, 0, pData, bytes.Length);
            SendData(m_hWnd, 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;
    }
    void Update()
    {
        sendJson();//一直發送方便測試
    }
}
    

接收端 test2.cs 隨便建場景保存將這個腳本掛載場景的物體上面 ,然后用unity打包pc端的exe  接收利用windows的hook鈎子(這里我就不做詳細的注釋了,自己體會hook的妙用了,不懂可以給我留言)

 

using UnityEngine;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Debug = UnityEngine.Debug;

public class test2: MonoBehaviour
{

//鈎子接收消息的結構

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 Start()
    {

//安裝鈎子
        HookLoad();
    }


    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);
                Debug.Log("json數據:" + str);
            }
            if (CallNextProc)
            {
                return CallNextHookEx(idHook, nCode, wParam, lParam);
            }
            else
            {
                //return 1;
                return CallNextHookEx(idHook, nCode, wParam, lParam);
            }
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
            return 0;
        }
       
    }
}

 

 

OK,所有代碼終於特么的完畢了,把test2.cs的場景打包,把test1.cs的代碼放在unity運行就行了。最終看test2.cs的exe中的這些代碼花了我好長時間,很值錢的我就這么分享給大家了,希望大家有好的東西也不要吝嗇啊。嘿嘿,最后有不懂的可以加我的Q群479853988問我哦。里面很多大神也可以問。轉載注重原創哦。微笑


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM