除 MSMQ,Windows CE 原生 **Windows Message** 機制 (`coredll.dll`) 也是輕量級 IPC 選擇:無需額外服務、延遲極低、支持廣播與對象封送 (WM_COPYDATA)。本文講解 CF.NET 中 `Microsoft.WindowsCE.Forms.MessageWindow` 封裝、自定義消息註冊、字符串/對象傳遞 (COPYDATASTRUCT) 完整實現。
using Microsoft.WindowsCE.Forms;
using System.Runtime.InteropServices;
[DllImport("coredll.dll", EntryPoint = "RegisterWindowMessage", SetLastError = true)]
private static extern uint RegisterWindowMessage(string lpString);
private static readonly uint MsgId = RegisterWindowMessage("MY_APP_UNIQUE_MSG_ID");
private const int HWND_BROADCAST = 0xFFFF;
`RegisterWindowMessage` 返回系統全局唯一消息號,避免衝突。
public void BroadcastSimple()
{
var msg = Message.Create((IntPtr)HWND_BROADCAST, (int)MsgId, IntPtr.Zero, IntPtr.Zero);
MessageWindow.SendMessage(ref msg);
}
public class MsgListener : MessageWindow
{
protected override void WndProc(ref Message msg)
{
if (msg.Msg == MsgId)
{
// 處理消息
OnMessageReceived();
}
base.WndProc(ref msg);
}
public event Action MessageReceived;
private void OnMessageReceived() => MessageReceived?.Invoke();
}
public partial class MainForm : Form
{
private MsgListener _listener;
public MainForm()
{
InitializeComponent();
_listener = new MsgListener();
_listener.MessageReceived += () =>
{
if (InvokeRequired)
Invoke(new Action(UpdateUI));
else
UpdateUI();
};
}
private void UpdateUI() { /* 更新控件 */ }
}
標準 `MessageWindow.SendMessage` 不支持 `WM_COPYDATA` (0x004A),需 P/Invoke `SendMessageW` 並手工構造 `COPYDATASTRUCT`。
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public int dwData; // 用戶定義數據
public int cbData; // 數據字節長度
public IntPtr lpData; // 數據指針 (非托管內存)
}
public static class Win32
{
private const int WM_COPYDATA = 0x004A;
private const int LMEM_FIXED = 0x0000;
private const int LMEM_ZEROINIT = 0x0040;
private const int LPTR = LMEM_FIXED | LMEM_ZEROINIT;
[DllImport("coredll.dll")] public static extern IntPtr LocalAlloc(int flags, int size);
[DllImport("coredll.dll")] public static extern IntPtr LocalFree(IntPtr hMem);
[DllImport("coredll.dll")] public static extern int SendMessageW(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public static void SendString(IntPtr hWnd, string text)
{
var cds = new COPYDATASTRUCT
{
dwData = 1,
cbData = (text.Length + 1) * 2, // Unicode 字節長度
lpData = LocalAlloc(LPTR, (text.Length + 1) * 2)
};
Marshal.Copy(text.ToCharArray(), 0, cds.lpData, text.Length);
var lParam = LocalAlloc(LPTR, Marshal.SizeOf(cds));
Marshal.StructureToPtr(cds, lParam, false);
SendMessageW(hWnd, WM_COPYDATA, IntPtr.Zero, lParam);
LocalFree(cds.lpData);
LocalFree(lParam);
}
}
private void btnSend_Click(object sender, EventArgs e)
{
Win32.SendString((IntPtr)HWND_BROADCAST, txtInput.Text);
}
public class MsgListener : MessageWindow
{
private const int WM_COPYDATA = 0x004A;
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_COPYDATA)
{
var cds = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
string text = Marshal.PtrToStringUni(cds.lpData);
OnDataReceived(text);
}
base.WndProc(ref msg);
}
public event Action<string> DataReceived;
private void OnDataReceived(string text) => DataReceived?.Invoke(text);
}
// 發送端
byte[] data = SerializeToByteArray(myObject); // BinaryFormatter / Protobuf
// 將 byte[] 按上述方式塞入 COPYDATASTRUCT.lpData 發送
// 接收端
byte[] data = ReadBytesFromPtr(cds.lpData, cds.cbData);
var obj = DeserializeFromByteArray(data);
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Windows Message | 輕量、無服務依賴、超低延遲、支持廣播 | 同機器、同會話、數據量小、無持久化 | 進程間輕量通知、UI 同步、單機多進程協作 |
| Point-to-Point Message Queue | 內核級、優先級、可命名管道 | CE 支持有限、文檔少 | 實時性要求極高的本地通信 |
| MSMQ | 離線存轉、事務、跨網絡、可靠投遞 | 需安裝服務、較重、延遲較高 | 斷網環境、異地同步、關鍵業務命令 |
Windows Message 是「手術刀」,MSMQ 是「運輸車」,按業務體量與可靠性需求選擇。