进程间通信的一个方法(.NET)

本文介绍了一种基于共享内存的C#进程间通信方法,适用于同一系统内的C#程序。文章详细展示了如何创建和使用共享内存,以及如何通过循环检查共享内存实现进程间的消息传递。
环境:VS2005 C# WinForm

注:我的方法仅适用于同一系统内的进程,并且相关进程都是您自己编写的C#程序。


网上关于C#进程间通信的资料:WM_COPYDATA、共享内存、管道、消息队列、SOCKET...
其中WM_COPYDATA是最简单的,但它受制于窗口。当窗口不处在顶层时,WM_COPYDATA就可能失效。共享内存是较为基

础的一种办法,但要用好它需要一些技巧。另外小弟对进程间消息传递的及时性非常看重,希望一个进程发出消息后

,目标进程能够立即做出反应。但是进程边界决定了这是不可能的,因为发送消息的进程不可能直接调用目标进程里

的函数。归根结底,目标进程得自己通过循环来查看外面是否有属于自己的消息。WM_COPYDATA是通过窗口消息循环

来获取,其它几种方法则是在内部通过一个while循环来获取信息。因为要避免这个while循环影响到UI线程,所以必

需另开线程。在了解了进程间通迅一些基础原理后,小弟总结出自己的方法。其原理是:

以共享内存为基础,发送方将消息码(代表了消息含义)及消息相关数据,以及接收进程的标识(进程ID或主窗口Text)

都放到共享内存中。接收方则循环检查共享内存中有无属于自己的消息数据,若有则取出处理。处理完后接收方将共

享内存数据清空。
理论上需要对共享内存的写操作进行同步,不过目前没提供同步代码(小弟工程中不需要,所以懒得弄了...)


首先是共享内存操作类,用于进程间数据传递。可根据需要对其进行修改以附合您要求,比如其中结构体定义。
C# code
            
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 PAGE_EXECUTE_READWRITE = 0x40 ; const int SEC_COMMIT = 0x8000000 ; const int SEC_IMAGE = 0x1000000 ; const int SEC_NOCACHE = 0x10000000 ; const int SEC_RESERVE = 0x4000000 ; const int INVALID_HANDLE_VALUE = - 1 ; IntPtr m_hSharedMemoryFile = IntPtr.Zero; IntPtr m_pwData = IntPtr.Zero; bool m_bAlreadyExist = false ; bool m_bInit = false ; long m_MemSize = 0 ; public ShareMem() { } ~ ShareMem() { Close(); } /// <summary> /// 初始化共享内存 /// </summary> /// <param name="strName"> 共享内存名称 </param> /// <param name="lngSize"> 共享内存大小 </param> /// <returns></returns> public int Init( string strName, long lngSize) { if (lngSize <= 0 || lngSize > 0x00800000 ) lngSize = 0x00800000 ; m_MemSize = lngSize; if (strName.Length > 0 ) { // 创建内存共享体(INVALID_HANDLE_VALUE) m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, ( uint ) PAGE_READWRITE, 0 , ( uint )lngSize, strName); if (m_hSharedMemoryFile == IntPtr.Zero) { m_bAlreadyExist = false ; m_bInit = false ; return 2 ; // 创建共享体失败 } else { if (GetLastError() == ERROR_ALREADY_EXISTS) // 已经创建 { m_bAlreadyExist = true ; } else // 新创建 { m_bAlreadyExist = false ; } } // --------------------------------------- // 创建内存映射 m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0 , 0 , ( uint )lngSize); if (m_pwData == IntPtr.Zero) { m_bInit = false ; CloseHandle(m_hSharedMemoryFile); return 3 ; // 创建内存映射失败 } else { m_bInit = true ; if (m_bAlreadyExist == false ) { // 初始化 MyData data = new MyData( - 1 ); byte [] bd = Tools.StructToBytes(data); Marshal.Copy(m_pwData, bd, 0 , bd.Length); } } // ---------------------------------------- } else { return 1 ; // 参数错误 } return 0 ; // 创建成功 } // end fun /// <summary> /// 关闭共享内存 /// </summary> public void Close() { if (m_bInit) { UnmapViewOfFile(m_pwData); CloseHandle(m_hSharedMemoryFile); } } /// <summary> /// 读数据 /// </summary> /// <param name="bytData"> 数据 </param> /// <param name="lngAddr"> 起始地址 </param> /// <param name="lngSize"> 个数 </param> /// <returns></returns> public int Read( ref byte [] bytData, int lngAddr, int lngSize) { if (lngAddr + lngSize > m_MemSize) return 2 ; // 超出数据区 if (m_bInit) { Marshal.Copy(m_pwData, bytData, lngAddr, lngSize); } else { return 1 ; // 共享内存未初始化 } return 0 ; // 读成功 } /// <summary> /// 写数据 /// </summary> /// <param name="bytData"> 数据 </param> /// <param name="lngAddr"> 起始地址 </param> /// <param name="lngSize"> 数据长度 </param> /// <returns></returns> public int Write( byte [] bytData, int lngAddr, int lngSize) { if (lngAddr + lngSize > m_MemSize) return 2 ; // 超出数据区 if (m_bInit) { Marshal.Copy(bytData, lngAddr, m_pwData, lngSize); } else { return 1 ; // 共享内存未初始化 } return 0 ; // 写成功 } } // end class


--下面继续
 
 

 

 

 

 

C# code
            
// 根据需要扩展该结构体 [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


如果要向一个进程发送消息:
C# code
            
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);
  } 


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值