从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; } }
通过测试使用,毫无问题。现贴出来,供后来者参考。
本文来自博客园,作者:jevan,转载请注明原文链接:https://www.cnblogs.com/DoNetCShap/archive/2012/06/26/2564462.html