zoukankan      html  css  js  c++  java
  • C# 进程同步,通信

    进程之间通讯的几种方法:
    常用的方法有:
    1.使用内存映射文件
    2.通过共享内存DLL共享内存
    3.使用SendMessage向另一进程发送WM_COPYDATA消息.
     

    发送WM_COPYDATA消息

    比起前两种的复杂实现来,WM_COPYDATA消息无疑是一种经济实惠的一中方法.

    WM_COPYDATA消息的主要目的是允许在进程间传递只读数据。Windows在通过WM_COPYDATA消息传递期间,不提供继承同步方式。SDK文档推荐用户使用SendMessage函数,接受方在数据拷贝完成前不返回,这样发送方就不可能删除和修改数据:
    这个函数的原型及其要用到的结构如下:
    SendMessage(hwnd,WM_COPYDATA,wParam,lParam);
    其中,WM_COPYDATA对应的十六进制数为0x004A
    wParam设置为包含数据的窗口的句柄。lParam指向一个COPYDATASTRUCT的结构:
    typedef struct tagCOPYDATASTRUCT{
    DWORD dwData;//用户定义数据
    DWORD cbData;//数据大小
    PVOID lpData;//指向数据的指针
    }COPYDATASTRUCT;
    该结构用来定义用户数据。
    具体过程如下:

    const int WM_COPYDATA = 0x004A;

    private void button1_Click(object sender, System.EventArgs e)
    {
    int WINDOW_HANDLER = FindWindow(null,@"接收方窗体");
    if(WINDOW_HANDLER == 0)
    {
    }
    else
    {
    byte[] sarr = System.Text.Encoding.Default.GetBytes(this.textBox1.Text);
    int len = sarr.Length;
    COPYDATASTRUCT cds;
    cds.dwData = (IntPtr) 100;
    cds.lpData = this.textBox1.Text;
    cds.cbData = len + 1;
    SendMessage(WINDOW_HANDLER, WM_COPYDATA, 0, ref cds);

    }
    }
    }
    public struct COPYDATASTRUCT
    {
    public IntPtr dwData;
    public int cbData;
    [MarshalAs(UnmanagedType.LPStr)] public string lpData;
    }

    }

    // 接收方

    this.Text = "接收方窗体";

    protected override void DefWndProc(ref System.Windows.Forms.Message m)
    {
    switch(m.Msg)
    {
    case WM_COPYDATA:
    COPYDATASTRUCT mystr = new COPYDATASTRUCT();
    Type mytype = mystr.GetType();
    mystr =(COPYDATASTRUCT)m.GetLParam(mytype);
    this.textBox1.Text =mystr.lpData;
    break;
    default:
    base.DefWndProc(ref m);
    break;
    }
    }
    }
    public struct COPYDATASTRUCT
    {
    public IntPtr dwData;
    public int cbData;
    [MarshalAs(UnmanagedType.LPStr)] public string lpData;

    }

    共享内存

    发消息实现的C#进程间的通讯,和使用共享内存的应用范围是不同的,共享内存适用于共享大量数据的情况。
    本文章利用了前面的一篇.net 1.1 下实现的信号量的类,在.net 1.1 下实现,如果在.net 2.0 下实现的话就用不到我的那个信号量的类了,因为这个类在.net 2.0是提供的。

    首先还是定义非托管调用,如下:


    const int INVALID_HANDLE_VALUE = -1;
    const int PAGE_READWRITE = 0x04;
      //共享内存
      [DllImport("Kernel32.dll",EntryPoint="CreateFileMapping")]
      private static extern IntPtr CreateFileMapping(IntPtr hFile, //HANDLE hFile,
       UInt32 lpAttributes,//LPSECURITY_ATTRIBUTES lpAttributes,  //0
       UInt32 flProtect,//DWORD flProtect
       UInt32 dwMaximumSizeHigh,//DWORD dwMaximumSizeHigh,
       UInt32 dwMaximumSizeLow,//DWORD dwMaximumSizeLow,
       string lpName//LPCTSTR lpName
       ); 

      [DllImport("Kernel32.dll",EntryPoint="OpenFileMapping")]
      private static extern IntPtr OpenFileMapping(
       UInt32 dwDesiredAccess,//DWORD dwDesiredAccess,
       int bInheritHandle,//BOOL bInheritHandle,
       string lpName//LPCTSTR lpName
       ); 

      const int FILE_MAP_ALL_ACCESS = 0x0002;
      const int FILE_MAP_WRITE = 0x0002; 

      [DllImport("Kernel32.dll",EntryPoint="MapViewOfFile")]
      private static extern IntPtr MapViewOfFile(
       IntPtr hFileMappingObject,//HANDLE hFileMappingObject,
       UInt32 dwDesiredAccess,//DWORD dwDesiredAccess
       UInt32 dwFileOffsetHight,//DWORD dwFileOffsetHigh,
       UInt32 dwFileOffsetLow,//DWORD dwFileOffsetLow,
       UInt32 dwNumberOfBytesToMap//SIZE_T dwNumberOfBytesToMap
       ); 

      [DllImport("Kernel32.dll",EntryPoint="UnmapViewOfFile")]
      private static extern int UnmapViewOfFile(IntPtr lpBaseAddress); 

      [DllImport("Kernel32.dll",EntryPoint="CloseHandle")]
      private static extern int CloseHandle(IntPtr hObject);

    然后分别在AB两个进程中定义如下两个信号量及相关变量;

      private Semaphore m_Write;  //可写的信号
      private Semaphore m_Read;  //可读的信号
      private IntPtr handle;     //文件句柄
      private IntPtr addr;       //共享内存地址
      uint mapLength;            //共享内存长
     
    定义这两个信号量是为读写互斥用的。
    在A进程中创建共享内存:
    m_Write = new Semaphore(1,1,"WriteMap");
    m_Read = new Semaphore(0,1,"ReadMap");
    mapLength = 1024;
    IntPtr hFile = new IntPtr(INVALID_HANDLE_VALUE);   
    handle = CreateFileMapping(hFile,0,PAGE_READWRITE,0,mapLength,"shareMemory");
    addr = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,0);

    然后再向共享内存中写入数据:

    m_Write.WaitOne();
    byte[] sendStr = Encoding.Default.GetBytes(txtMsg.Text + '/0');
    //如果要是超长的话,应另外处理,最好是分配足够的内存
    if(sendStr.Length < mapLength)
          Copy(sendStr,addr);
    m_Read.Release();


    这是在一个单独的方法中实现的,可多次调用,但受信号量的控制。其中txtMsg是一个文本框控件,实际中可用任意字符串,加最后的'/0'是为了让在共享内存中的字符串有一个结束符,否则在内存中取出时是以'/0'为准的,就会出现取多的情况。
    Copy方法的实现如下:

    static unsafe void Copy(byte[] byteSrc,IntPtr dst)
      {
       fixed (byte* pSrc = byteSrc)
       {
        byte* pDst = (byte*)dst;
        byte* psrc = pSrc;
        for(int i=0;i<byteSrc.Length;i++)
        {
         *pDst = *psrc;
         pDst++;
         psrc ++;
        }
       }
      }
     
    注意unsafe 关键字,在编译时一定要打开非安全代码开关。
    最后不要忘了在A进程中关闭共享内存对象,以免内存泄露。
       UnmapViewOfFile(addr);
       CloseHandle(handle);

    要在B进程中读取共享内存中的数据,首先要打开共享内存对象:
    m_Write = Semaphore.OpenExisting("WriteMap");
    m_Read = Semaphore.OpenExisting("ReadMap");
    handle = OpenFileMapping(0x0002,0,"shareMemory");
    读取共享内存中的数据:
       m_Read.WaitOne();
       string str = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,0);
       txtMsg.Text = str;
       m_Write.Release();
    这里获取了字符串,如果要获取byte数组,请参考上面的Copy函数实现。
     
     
     
     
     
     

    参考文章

    c# 进程间同步实现 进程之间通讯的几种方法(转载)

  • 相关阅读:
    HttpClient调用RestFul接口(post和get方式)
    mysql权限异常
    javascript:用法
    Java哈希值HashCode理解
    Java的CountDownLatch和CyclicBarrier的理解和区别
    Java并发编程与技术内幕:ThreadGroup线程组应用
    面试官: 谈谈什么是守护线程以及作用 ?
    java 成员变量 静态成员变量 方法 静态方法初始化顺序
    【java并发核心一】Semaphore 的使用思路
    threadlocal原理及常用应用场景
  • 原文地址:https://www.cnblogs.com/arxive/p/6482594.html
Copyright © 2011-2022 走看看