引言
有一次心血來潮,突然想研究一下進程間的通信,能夠實現消息傳遞的方法有幾種,其中win32api中的sendmessage就是當中的一種比較簡單的方法。於是參考了網上各種資料,做了一個小demo。
發送方Winform
1.新建一個Winform項目,添加控件,如下

2.界面做好,接着來編寫代碼,首先利用DllImport來聲明SendMessage函數原型,如下:
[DllImport("User32.dll")] private static extern int SendMessage(IntPtr hWnd,int Msg, int wParam, IntPtr lParam );
3.其中,lParam參數說明如下
lParam指向一個COPYDATASTRUCT的結構:
typedef struct tagCOPYDATASTRUCT
{
DWORD dwData; //用戶定義數據
DWORD cbData; //數據大小
PVOID lpData; //指向數據的指針
} COPYDATASTRUCT;
4.所以為了方便起見,還需要定義個一個結構,如下
public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; }
5.相應地,sendmessage函數更改為如下:
[DllImport("User32.dll")] private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam );
6.至此,sendmessage函數定義完成,但是還不夠,我們還需要能找到接收消息的窗體的函數,所以還要聲明兩個函數:
[DllImport("User32.dll", EntryPoint = "FindWindow")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll", EntryPoint = "FindWindowEx“)] private static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow);
7.接下來,我們可以開始寫發送消息具體實現。
發送到form的textbox按鈕的方法實現如下:
IntPtr WINDOW_HANDLER = FindWindow(null, "Win32窗體"); if (WINDOW_HANDLER != IntPtr.Zero) { IntPtr hwndThree = FindWindowEx(WINDOW_HANDLER, 0, null, ""); hwndThree =new IntPtr(GetWindow(hwndThree.ToInt32(), 2)); hwndThree = new IntPtr(GetWindow(hwndThree.ToInt32(), 2)); //獲取按鈕的句柄 找3次才找到目標textbox SendMessage(hwndThree, WM_SETTEXT, 0, this.sendtext.Text); }
發送到Winform按鈕的方法實現如下:
IntPtr WINDOW_HANDLER = FindWindow(null, "Win32窗體"); if (WINDOW_HANDLER != IntPtr.Zero) { string text = this.sendtext.Text; byte[] sarr = System.Text.Encoding.Default.GetBytes(text); int len = sarr.Length; COPYDATASTRUCT cds; cds.dwData = (IntPtr)100; cds.lpData = text; cds.cbData = len + 1; SendMessage(WINDOW_HANDLER, WM_COPYDATA, 0, ref cds); }
發送到WPF按鈕的方法實現如下:
IntPtr WINDOW_HANDLER = FindWindow(null, "WPF窗體"); if (WINDOW_HANDLER != IntPtr.Zero) { string text = this.sendtext.Text; byte[] sarr = System.Text.Encoding.Default.GetBytes(text); int len = sarr.Length; COPYDATASTRUCT cds; cds.dwData = (IntPtr)100; cds.lpData = text; cds.cbData = len + 1; SendMessage(WINDOW_HANDLER, WM_COPYDATA, 0, ref cds); }
8.至此,發送端的編碼完成,其中第一個按鈕的功能是將消息直接發送到winform接收方的textbox上,第二個按鈕是將消息發送到winform接收方的窗體上,再由窗體的方法處理,第三個按鈕是將消息發送到wpf接收方的窗體上,再由wpf的方法處理。為什么沒有直接發送到wpf的textbox的方法呢,那是因為wpf里面的控件是沒有句柄的,只有窗體才有句柄,然后發送消息需要接收方的句柄,所以無法實現。
接收方winform
1.新建winform項目,編寫界面如下:

2.編寫后台代碼,定義結構COPYDATASTRUCT和重寫winform的消息處理方法WndProc,代碼如下:
public const int WM_COPYDATA = 0x004A; public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } protected override void WndProc(ref System.Windows.Forms.Message m) { switch (m.Msg) { case WM_COPYDATA: COPYDATASTRUCT MyKeyboardHookStruct = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT)); this.textBox2.Text = MyKeyboardHookStruct.lpData; break; default: base.WndProc(ref m); // 調用基類函數處理其他消息。 break; } }
3.至此,winform接收端編碼完成,我們只需在WndProc處理一下消息類型為WM_COPYDATA的消息即可。
接收方wpf
1.新建wpf項目,界面如下

2.編寫后台代碼,wpf沒有消息處理方法WndProc,所以處理上復雜些。主要利用HwndSource實現接收消息,具體代碼如下:
public MainWindow() { InitializeComponent(); this.Loaded += Window_Loaded; } #region 定義常量消息值 public const int WM_GETTEXT = 0x0D; public const int WM_SETTEXT = 0x0C; public const int WM_SIZEING = 0x0214; public const int WM_COPYDATA = 0x004A; public const int WM_LBUTTONDBLCLK = 0x0203; #endregion #region 定義結構體 public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } #endregion private void Window_Loaded(object sender, RoutedEventArgs e) { HwndSource hWndSource; WindowInteropHelper wih = new WindowInteropHelper(this); hWndSource = HwndSource.FromHwnd(wih.Handle); //添加處理程序 hWndSource.AddHook(MainWindowProc); } private IntPtr MainWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WM_COPYDATA: { COPYDATASTRUCT mystr = new COPYDATASTRUCT(); Type mytype = mystr.GetType(); COPYDATASTRUCT MyKeyboardHookStruct = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT)); this.textbox.Text = MyKeyboardHookStruct.lpData; break; } default: { break; } } return IntPtr.Zero; }
3.wpf接收端完成。
最終界面效果

小結
本文介紹了如何用sendmessage函數在窗體間發送消息,sendmessage函數是win32api的一種,然而win32api又是一個好龐大的話題了,我現在還只是入門未遂,漸行漸學罷了.另外,消息的傳遞方法不只一種,例如我們還可以用wcf進行通信,有時間再研究.最后,如果您有更好的建議,請不吝指教,感激不盡!
