环境:VS2005 C# WinForm
注:我的方法仅适用于同一系统内的进程,并且相关进程都是您自己编写的C#程序。
网上关于C#进程间通信的资料:WM_COPYDATA、共享内存、管道、消息队列、SOCKET...
其中WM_COPYDATA是最简单的,但它受制于窗口。当窗口不处在顶层时,WM_COPYDATA就可能失效。共享内存是较为基
础的一种办法,但要用好它需要一些技巧。另外小弟对进程间消息传递的及时性非常看重,希望一个进程发出消息后
,目标进程能够立即做出反应。但是进程边界决定了这是不可能的,因为发送消息的进程不可能直接调用目标进程里
的函数。归根结底,目标进程得自己通过循环来查看外面是否有属于自己的消息。WM_COPYDATA是通过窗口消息循环
来获取,其它几种方法则是在内部通过一个while循环来获取信息。因为要避免这个while循环影响到UI线程,所以必
需另开线程。在了解了进程间通迅一些基础原理后,小弟总结出自己的方法。其原理是:
以共享内存为基础,发送方将消息码(代表了消息含义)及消息相关数据,以及接收进程的标识(进程ID或主窗口Text)
都放到共享内存中。接收方则循环检查共享内存中有无属于自己的消息数据,若有则取出处理。处理完后接收方将共
享内存数据清空。
理论上需要对共享内存的写操作进行同步,不过目前没提供同步代码(小弟工程中不需要,所以懒得弄了...)
首先是共享内存操作类,用于进程间数据传递。可根据需要对其进行修改以附合您要求,比如其中结构体定义。
public static class ProcessMessaging { static ShareMem Data = new ShareMem(); /// <summary> /// 获取共享内存(MyData结构) /// </summary> /// <returns></returns> public static MyData GetShareMem() { int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0) { return new MyData(-1); } byte[] temp = new byte[MemSize]; try { Data.Read(ref temp, 0, temp.Length); MyData stuc = (MyData)Tools.BytesToStuct(temp, typeof(MyData)); return stuc; } catch (Exception) { return new MyData(-1); } }//end fun /// <summary> /// 设置共享内存(MyData结构) /// </summary> /// <param name="str"></param> /// <returns></returns> public static bool SetShareMem(MyData data) { int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0)//"MyData"共享内存名称,您起别的名字也可以 { return false; } try { byte[] b = Tools.StructToBytes(data); Data.Write(b, 0, b.Length); } catch (Exception) { return false; } return true; }//end fun }//end class public class ShareMem { [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs (UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32", EntryPoint = "GetLastError")] public static extern int GetLastError(); const int ERROR_ALREADY_EXISTS = 183; const int FILE_MAP_COPY = 0x0001; const int FILE_MAP_WRITE = 0x0002; const int FILE_MAP_READ = 0x0004; const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; const int PAGE_READONLY = 0x02; const int PAGE_READWRITE = 0x04; const int PAGE_WRITECOPY = 0x08; const int PAGE_EXECUTE = 0x10; const int PAGE_EXECUTE_READ = 0x20; const int
//根据需要扩展该结构体 [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)] public struct MyData { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] public char[] c0; public int l0; public int i0; public int i1; public int i2; public int i3; public int i4; public int i5; //placeholder:该参数我用不着,可C#规定必需有,所以起个这名字 public MyData(int placeholder) { c0 = new char[1024]; l0 = 0; i0 = 0; i1 = 0; i2 = 0; i3 = 0; i4 = 0; i5 = 0; }//end fun /// <summary> /// /// 进程间通信,进程ID,-2表示所有进程 /// /// /// </summary> /// public int ProcessID { get { return i4; } set { i4 = value; } } /// <summary> /// /// 进程间通信,消息码 /// /// </summary> public int InfoCode { get { return i5; } set { i5 = value; } } /// <summary> /// 示例字符串参数 /// </summary> public string Url { get { if (l0 > 0) { return new string(c0, 0, l0); } else { return ""; } } set { if (value != null) { value.CopyTo(0, c0, 0, value.Length); l0 = value.Length; } } } /// <summary> /// 示例矩形参数 /// </summary> public Rectangle WindowPosition { get { return new Rectangle(i0, i1, i2, i3); } set { i0 = value.X; i1 = value.Y; i2 = value.Width; i3 = value.Height; } } }//end struct public static class Tools { //序列化结构体复制入byte数组 public static byte[] StructToBytes(object structObj) { //得到结构体的大小 int size = Marshal.SizeOf(structObj); //创建byte数组 byte[] bytes = new byte[size]; //分配结构体大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(size); //将结构体拷到分配好的内存空间 Marshal.StructureToPtr(structObj, structPtr, false); //从内存空间拷到byte数组 Marshal.Copy(structPtr, bytes, 0, size); //释放内存空间 Marshal.FreeHGlobal(structPtr); //返回byte数组 return bytes; } //byte数组复制入序列化结构体,注意在返回结果前加上强制转换 public static object BytesToStuct(byte[] bytes, Type type) { //得到结构体的大小 int size = Marshal.SizeOf(type); //byte数组长度小于结构体的大小 if (size > bytes.Length) { //返回空 return null; } //分配结构体大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(size); //将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, size); //将内存空间转换为目标结构体 object obj = Marshal.PtrToStructure(structPtr, type); //释放内存空间 Marshal.FreeHGlobal(structPtr); //返回结构体 return obj; }//end fun }//end class
如果要向一个进程发送消息: MyData data = ProcessMessaging.GetShareMem();//您new一个也行 data.InfoCode = ...;//消息码 data.ProcessID = ...;//接收进程ID ...其它数据 ProcessMessaging.SetShareMem(data);
接下来说下接收方,以下代码都位于主窗口CPP中:
int ProcessID = 0; System.Timers.Timer time = new System.Timers.Timer(10);//实例化Timer类,设置间隔时间为10毫秒; 下面4行代码在构造函数中添加: ProcessID = Process.GetCurrentProcess().Id; time.Elapsed += new System.Timers.ElapsedEventHandler(theout);//到达时间的时候执行事件; time.AutoReset = true;//设置是执行一次(false)还是一直执行(true); time.Enabled = false;//是否执行System.Timers.Timer.Elapsed事件; //load事件中启动time public void theout(object source, System.Timers.ElapsedEventArgs e) { ProcessMessageHandler pmh = new ProcessMessageHandler(ProcessMessage); this.Invoke(pmh); }//end fun public delegate void ProcessMessageHandler(); public void ProcessMessage() { MyData data = ProcessMessaging.GetShareMem(); if (data.ProcessID == ProcessID) { switch (InfoCode) { ...处理 } } ...清空data ProcessMessaging.SetShareMem(data); }