同一个进程通信、不同进程之间的通信

这里记录一下使用Win32 API在同一台机器上进行同进程通信、不同进程通信。

使用的API
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);

/// 
/// 消息发送API
/// 
/// 指定要接收消息的窗口的句柄
/// 指定被发送的消息ID
/// 指定附加的消息特定信息
/// 指定附加的消息特定信息
/// 返回值指定消息处理的结果,依赖于所发送的消息
[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref string lParam);

[DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref COPYDATASTRUCT cds);
/// 
/// 异步消息发送API
/// 
/// 指定要接收消息的窗口的句柄
/// 指定被发送的消息ID
/// 指定附加的消息特定信息
/// 指定附加的消息特定信息
/// 
[DllImport("User32.dll", EntryPoint = "PostMessage")]
public static extern int PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

 [DllImport("User32.dll", EntryPoint = "SendMessage")]
 public static extern int PostMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref COPYDATASTRUCT cds);

 [DllImport("User32.dll", EntryPoint = "SendMessage")]
 public static extern int PostMessage(IntPtr win, int WM_COPYDATA, IntPtr wParam, ref string str);

 /// 
/// 接收信息
/// 
/// 带文本的窗口或控件的句柄
/// 指向接收文本的缓冲区的指针
/// 指定要保存在缓冲区内的字符的最大个数,其中包含NULL字符。如果文本超过界限,它就被截断
/// 
[DllImport("User32.Dll")]
public static extern int GetWindowText(int hwnd, StringBuilder buf, int nMaxCount);
找到本机要接收的窗体
// 不能再加上类名限定,否则会找不到
IntPtr win = Win32API.FindWindow(null,"窗体标题");
1. 同一进程

这比较简单。

  • 数字:直接调用SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam),在“protected override void DefWndProc(ref Message m)”接收的时候取Message.LParam、Message.WParam,因为数字与IntPtr之间可以强转。
  • 字符串:注意类型的转换
// 发
Win32API.PostMessage(win,0x890,IntPtr.Zero,Marshal.StringToHGlobalAnsi(str));

// 收
string str = Marshal.PtrToStringAnsi(m.LParam);
2. 不同进程
  • 数字:与上面一致。
  • 字符串:比较麻烦
    • 方法1
      自定义结构体,注意该方法必须使用WM_COPYDATA 参数,就是说不能自定义消息号了。可能会觉得这里使用结构体多此一举,之所以不直接使用string,是因为在使用同样的方式接收数据的时候,会报错“string没有提供无参的构造函数”,struct有默认的无参构造函数。 这个方法的问题在于:如果是跟第三方的API在进行通信的时候,你不能要求别人也必须按照你的struct来传参,所以只有在接收、发送都是自己的程序的时候,以及在协同开发有约定类型、第三方有指定类型的时候,才适用。
    public struct COPYDATASTRUCT
     {
         public IntPtr dwData;
         public int cbData;
         [MarshalAs(UnmanagedType.LPStr)]
         public string lpData;
     }
    
     const int WM_COPYDATA = 0x004A;
     // 发
     string str = "test";
     byte[] sarr = System.Text.Encoding.Default.GetBytes(str);
     int len = sarr.Length;
     COPYDATASTRUCT cds;
     cds.dwData = (IntPtr)Convert.ToInt16(123);//可以是任意值
     cds.cbData = len + 1;//指定lpData内存区域的字节数
     cds.lpData = str;//发送给目标窗口所在进程的数据
     Win32API.PostMessage(win, WM_COPYDATA, IntPtr.Zero, ref cds);
     // 收
      protected override void DefWndProc(ref Message m)
      {
         switch (m.Msg)
         {
             case WM_COPYDATA:
                 COPYDATASTRUCT cds = new COPYDATASTRUCT();
                 Type t = cds.GetType();
                 cds = (COPYDATASTRUCT)m.GetLParam(t);
                 string strResult = cds.dwData.ToString() + ":" + cds.lpData;
                 MessageBox.Show(strResult);
                 break;
             default:
                 base.DefWndProc(ref m);
                 break;
         }
     }
    
    • 方法2
      传递控件句柄,通过GetWindowText来接数据。这里不需要再封装struct,可以自定义消息号,但需要有控件的Handle来作为参数,而且只能传递字符串。
    // 发送:0x890是自定义的消息号,btn_Send是界面的一个控件,这会将该控件的Text值发送出去
    Win32API.PostMessage(win, 0x890, this.btn_Send.Handle, IntPtr.Zero);
    
    // 接收
    protected override void DefWndProc(ref Message m)
    {
       StringBuilder sb = new StringBuilder(1024 * 2);
       switch (m.Msg)
       {
           case 0x890:
               Win32API.GetWindowText(m.WParam.ToInt32(), sb, sb.Capacity);
               MessageBox.Show(sb.ToString());
               break;
           default:
               base.DefWndProc(ref m);
               break;
        }
    }
    

其实这些方法用起来都不太爽,因为简单来说我就是想一个进程给另一个进程发一条信息而已,不想使用Socket、WebService、WCF等,因为已经确定在同一台机器了。还有什么好办法吗???

你可能感兴趣的:(Win32,API)