【转】 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问我哦。里面很多大神也可以问。转载注重原创哦。