▪ Windows 消息
在做一些 COM 串口、USB 相关的软件时,我们需要程序实时监测这些端口的状态:比如是否新插入、是否拔出等等,从而让软件做出相应的动作。
实际编写过程中,
▪ WPF 处理 Windows 消息的几种方式
1. Windows API 钩子
WPF 的窗体并没有 Winform 中的 Handle,所以在使用 API 时并不能直接使用设置或者移除钩子。要想使用 API 的 SetWindowsHookEx, UnhookWindowsHookEx, CallNextHookEx 可以首先使用 WindowInteropHelper 得到 Handle。
2. WPF HwndSource
WPF提供的 HwndSource 可以使你更快的实现处理 Windows 消息。通 HwndSource.FromVisual 得到的 HwndSource 可以添加(AddHook)移除(Remove)Hook。HwndSource 有一定的局限性,如果你的 WPF 应用中包含 Winform 控件那你可能要有些失望了,这种方式不能接收到这类消息。
3.ComponentDispatcher
在互操作方案中,启用 Win32 与 WPF 之间的消息泵的共享控件,使用ComponentDispatcher的ThreadFilterMessage等事件可以更好的处理Windows消息。使用这种方式可以避免HwndSource的缺陷。
总结:WPF 也有多种方式处理 Windows 消息,但各种方式各有特点:Windows API 钩子方式功能最强能实现系统级的消息处理,ComponentDispatcher 次之,HwndSource 相对较弱。
WPF 处理 Windows 消息:WPF HwndSource
完整的消息处理代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// ↓ 关键代码
this.SourceInitialized += new EventHandler(MainWindow_SourceInitialized);
}
// ↓ 关键代码
void MainWindow_SourceInitialized(object sender, EventArgs e)
{
IntPtr hwnd = new WindowInteropHelper(this).Handle;
HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(WndProc));
}
// ↓ 关键代码
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch(msg)
{
case 1:
break;
case 2:
break;
default :
break;
}
return IntPtr.Zero;
}
}
代码片段:USB插拔监控
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// 初始化常量
// 设备改变、USB插入、USB拔出
Int32 WM_DEVICE_CHANGE = 0x219;
Int32 DBT_DEVICE_ARRIVAL = 0x8000;
Int32 DBT_DEVICE_REMOVE_COMPLETE = 0x8004;
// 设备改变
if( msg == WM_DEVICE_CHANGE ){
// USB插入
if( wParam.ToInt32() == DBT_DEVICE_ARRIVAL ){
// do something ...
}
// USB拔出
if (wParam.ToInt32() == DBT_DEVICE_REMOVE_COMPLETE){
// do something ...
}
}
return IntPtr.Zero;
}