原貼:
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問我哦。里面很多大神也可以問。轉載注重原創哦。