从C#下使用WM

    技术2022-05-19  20

    从 C# 下使用 WM_COPYDATA 传输数据说到 Marshal 的应用

           笔者曾在一个项目的实施过程中,需要使用 WM_COPYDATA 在本地机器的两个进程间传输数据。在 C++ 中实现非常简单,但在 C# 中实现时却出现了麻烦。由于没有指针,使用 COPYDATASTRUCT 结构传递数据时,无法正确传递 lpData 。从网上搜寻文档,找到一个例子,是将 COPYDATASTRUCT 结构的 lpData 声明为 string 。这样虽然能传递字符串,但不能传递随意的二进制数据。

           偶然地,我查阅 MSDN 帮助时,发现了 Marshal 类。该类概述描述道:提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法 。这时,我豁然开朗,觉得找到了一个托管代码与非托管代码交互的桥梁。

           于是我声明 COPYDATASTRUCT 如下:

           [StructLayout(LayoutKind.Sequential)]

                  public struct COPYDATASTRUCT

                  {

                         public IntPtr dwData;

                         public int cbData;

                         public IntPtr lpData;

                  }

           在发送数据时,我使用 Marshal 类分配一块全局内存,并将数据拷入这块内存,然后发送消息:

           COPYDATASTRUCT cds;

                  cds.dwData = (IntPtr)flag;

                  cds.cbData = data.Length;

                  cds.lpData = Marshal.AllocHGlobal(data.Length);

                  Marshal.Copy(data,0,cds.lpData,data.Length);

                  SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

    在接收数据时,我使用 Marshal 类将数据从这块全局内存拷出,然后处理消息:

    COPYDATASTRUCT cds = new COPYDATASTRUCT();

         Type mytype = cds.GetType();

                  cds = (COPYDATASTRUCT)m.GetLParam(mytype);

                  uint flag = (uint )(cds.dwData);

                  byte [] bt = new byte [cds.cbData];

           Marshal.Copy(cds.lpData,bt,0,bt.Length);

    详细源码如下:

    /// <summary>

           /// Windows 的 COPYDATA 消息封装类。

           /// </summary>

           public class Messager : System.Windows.Forms.Form

           {

                  /// <summary>

                  /// 必需的设计器变量。

                  /// </summary>

                  private System.ComponentModel.Container components = null ;

     

                  // 消息标识

                  private const int WM_COPYDATA = 0x004A;

                  // 消息数据类型 (typeFlag 以上二进制, typeFlag 以下字符 )

                  private const uint typeFlag = 0x8000;

                  /// <summary>

                  /// 重载 CopyDataStruct

                  /// </summary>

                  [StructLayout(LayoutKind.Sequential)]

                  public struct COPYDATASTRUCT

                  {

                         public IntPtr dwData;

                         public int cbData;

                         public IntPtr lpData;

                  }

                  //

                  [DllImport("User32.dll",EntryPoint="SendMessage")]

                  private static extern int SendMessage(

                         int hWnd,                                  // handle to destination window

                         int Msg,                              // message

                         int wParam,                               // first message parameter

                         ref COPYDATASTRUCT lParam    // second message parameter

                         );

                  //

                  [DllImport("User32.dll",EntryPoint="FindWindow")]

                  private static extern int FindWindow(string lpClassName,string lpWindowName);

     

                  // 接收到数据委托与事件定义

                  public delegate void ReceiveStringEvent(object sender,uint flag,string str);        

                  public delegate void ReceiveBytesEvent(object sender,uint flag,byte [] bt);

                  public event ReceiveStringEvent OnReceiveString;

                  public event ReceiveBytesEvent OnReceiveBytes;

                  // 发送数据委托与事件定义

                  public delegate void SendStringEvent(object sender,uint flag,string str);            

                  public delegate void SendBytesEvent(object sender,uint flag,byte [] bt);

                  public event SendStringEvent OnSendString;

                  public event SendBytesEvent OnSendBytes;

                  //

                  public Messager()

                  {

                         //

                         // Windows 窗体设计器支持所必需的

                         //

                         InitializeComponent();

                         //

                         // TODO: 在 InitializeComponent 调用后添加任何构造函数代码

                         //

                  }

     

                  /// <summary>

                  /// 清理所有正在使用的资源。

                  /// </summary>

                  protected override void Dispose( bool disposing )

                  {

                         if ( disposing )

                         {

                                if (components != null )

                                {

                                       components.Dispose();

                                }

                         }

                         base .Dispose( disposing );

                  }

     

                  #region Windows 窗体设计器生成的代码

                  /// <summary>

                  /// 设计器支持所需的方法 - 不要使用代码编辑器修改

                  /// 此方法的内容。

                  /// </summary>

                  private void InitializeComponent()

                  {

                         //

                         // Messager

                         //

                         this .AutoScaleBaseSize = new System.Drawing.Size(6, 14);

                         this .ClientSize = new System.Drawing.Size(200, 14);

                         this .Name = "Messager";

                         this .ShowInTaskbar = false ;

                         this .Text = "Demo_Emluator";

                         this .WindowState = System.Windows.Forms.FormWindowState.Minimized;

     

                  }

                  #endregion           

                  /// <summary>

                  /// 重载窗口消息处理函数

                  /// </summary>

                  /// <param name="m"></param>

                  protected override void DefWndProc(ref System.Windows.Forms.Message m)

                  {

                         switch (m.Msg)

                         {

                                // 接收 CopyData 消息,读取发送过来的数据

                                case WM_COPYDATA:

                                       COPYDATASTRUCT cds = new COPYDATASTRUCT();

                               Type mytype = cds.GetType();

                                       cds = (COPYDATASTRUCT)m.GetLParam(mytype);

                                       uint flag = (uint )(cds.dwData);

                                       byte [] bt = new byte [cds.cbData];

                                       Marshal.Copy(cds.lpData,bt,0,bt.Length);

                                       if (flag <= typeFlag)

                                       {

                                              if (OnReceiveString != null )

                                              {

                                                     OnReceiveString(this ,flag,System.Text.Encoding.Default.GetString(bt));

                                              }

                                       }

                                       else

                                       {

                                              if (OnReceiveBytes != null )

                                              {

                                                     OnReceiveBytes(this ,flag,bt);

                                              }

                                       }

                                       break ;

                                default :

                                       base .DefWndProc(ref m);

                                       break ;

                         }

                  }

                  /// <summary>

                  /// 发送字符串格式数据

                  /// </summary>

                  /// <param name="destWindow"> 目标窗口标题 </param>

                  /// <param name="flag"> 数据标志 </param>

                  /// <param name="str"> 数据 </param>

                  /// <returns></returns>

                  public bool SendString(string destWindow,uint flag,string str)

                  {

                         if (flag > typeFlag)

                         {

                                MessageBox.Show(" 要发送的数据不是字符格式 ");

                                return false ;

                         }

                         int WINDOW_HANDLER = FindWindow(null ,@destWindow);

                         if (WINDOW_HANDLER == 0) return false ;

                         try

                         {

                                byte [] sarr = System.Text.Encoding.Default.GetBytes(str);

                                COPYDATASTRUCT cds;

                                cds.dwData = (IntPtr)flag;

                                cds.cbData = sarr.Length;

                                cds.lpData = Marshal.AllocHGlobal(sarr.Length);

                                Marshal.Copy(sarr,0,cds.lpData,sarr.Length);

                                SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

                                if (OnSendString != null )

                                {

                                       OnSendString(this ,flag,str);

                                }

                                return true ;

                         }

                         catch (Exception e)

                         {

                                MessageBox.Show(e.Message);

                                return false ;

                         }

                  }

                  /// <summary>

                  /// 发送二进制格式数据

                  /// </summary>

                  /// <param name="destWindow"> 目标窗口 </param>

                  /// <param name="flag"> 数据标志 </param>

                  /// <param name="data"> 数据 </param>

                  /// <returns></returns>

                  public bool SendBytes(string destWindow,uint flag,byte [] data)

                  {

                         if (flag <= typeFlag)

                         {

                                MessageBox.Show(" 要发送的数据不是二进制格式 ");

                                return false ;

                         }

                         int WINDOW_HANDLER = FindWindow(null ,@destWindow);

                         if (WINDOW_HANDLER == 0) return false ;

                         try

                         {

                                COPYDATASTRUCT cds;

                                cds.dwData = (IntPtr)flag;

                                cds.cbData = data.Length;

                                cds.lpData = Marshal.AllocHGlobal(data.Length);

                                Marshal.Copy(data,0,cds.lpData,data.Length);

                                SendMessage(WINDOW_HANDLER,WM_COPYDATA,0,ref cds);

                                if (OnSendBytes != null )

                                {

                                       OnSendBytes(this ,flag,data);

                                }

                                return true ;

                         }

                         catch (Exception e)

                         {

                                MessageBox.Show(e.Message);

                                return false ;

                         }

                  }

           通过测试使用,毫无问题。现贴出来,供后来者参考。


    最新回复(0)