代码改变世界

进程间通信

2011-11-14 09:14  沐海  阅读(650)  评论(0编辑  收藏  举报

环境: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);
  }  
记录生活、工作、学习点滴!
E-Mail:mahaisong@hotmail.com 欢迎大家讨论。
沐海博客园,我有一颗,卓越的心!