昨天在项目开发中,遇到了USB口拔插的操作,想在程序中,实现对com口拔插的监控,但是常规的WndProc是在winform实现的,所以查询了一下wpf是怎么实现的。
有下面的方法,测试可行:
1、USB消息结构的定义
// usb消息定义
public const int WM_DEVICE_CHANGE = 0x219;
public const int DBT_DEVICEARRIVAL = 0x8000;
public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004;
public const UInt32 DBT_DEVTYP_PORT = 0x00000003;
[StructLayout(LayoutKind.Sequential)]
struct DEV_BROADCAST_HDR
{
public UInt32 dbch_size;
public UInt32 dbch_devicetype;
public UInt32 dbch_reserved;
}
[StructLayout(LayoutKind.Sequential)]
protected struct DEV_BROADCAST_PORT_Fixed
{
public uint dbcp_size;
public uint dbcp_devicetype;
public uint dbcp_reserved;
// Variable?length field dbcp_name is declared here in the C header file.
}
2、定义钩子方法:WndProc
/// <summary>
/// 检测USB串口的拔插
/// </summary>
/// <param name="m"></param>
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_DEVICE_CHANGE) // 捕获USB设备的拔出消息WM_DEVICECHANGE
{
switch (wParam.ToInt32())
{
case DBT_DEVICE_REMOVE_COMPLETE: // USB拔出
break;
case DBT_DEVICEARRIVAL: // USB插入获取对应串口名称
DEV_BROADCAST_HDR dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_HDR));
if (dbhdr.dbch_devicetype == DBT_DEVTYP_PORT)
{
string portName = Marshal.PtrToStringUni((IntPtr)(lParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed))));
Console.WriteLine("Port '" + portName + "' arrived.");
}
break;
}
}
return hwnd;
}
3、在loaded中,注册这个方法
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
if (source != null) source.AddHook(WndProc);
扩展阅读:
Marshal.StructureToPtr方法简介
1. 功能及位置
将数据从托管对象封送到非托管内存块,属于.NET Framework 类库
命名空间:System.Runtime.InteropServices
程序集:mscorlib(在 mscorlib.dll 中)
2. 语法
C#:
[ComVisibleAttribute(true)] public static void StructureToPtr (Object structure,IntPtr ptr,bool fDeleteOld);
C++:
[ComVisibleAttribute(true)]public: static void StructureToPtr (Object^ structure, IntPtr ptr, bool fDeleteOld);
3. 参数说明
structure:托管对象,包含要封送的数据。该对象必须是格式化类的实例。
ptr:指向非托管内存块的指针,必须在调用此方法之前分配该指针。
fDeleteOld:设置为 true 可在执行Marshal.DestroyStructure方法前对 ptr 参数调用此方法。请注意,传递 false 可导致内存泄漏。
4. 异常
异常类型:ArgumentException
条件:structrue参数是泛型类型
5. 备注
StructureToPtr将结构的内容复制到 ptr 参数指向的预分配内存块。如果 fDeleteOld 参数为 true,则使用嵌入指
针上适当的删除 API 来删除最初由 ptr 指向的缓冲区,但该缓冲区必须包含有效数据。此方法为在镜像托管类中指
定的每个引用字段执行清理工作。
假设 ptr 指向非托管内存块。此内存块的布局由相应的托管类 structure 描述。StructureToPtr将字段值从结构封
送到指针。假设 ptr 块包含引用字段,该字段指向当前包含“abc”的字符串缓冲区。假设托管端上相应的字段是包含“vwxyz”的字符串。如果不另行通知它,StructureToPtr将分配一个新的非托管缓冲区来保存“vwxyz”,并将它挂钩到 ptr 块。这将丢弃旧缓冲区“abc”使之漂移而不将其释放回非托管堆。最后,您将得到一个孤立的缓冲区,它表示在代码中存在内存泄漏。如果将 fDeleteOld 参数设置为真,则 StructureToPtr 在继续为“vwxyz”分配新缓冲区之前释放保存“abc”的缓冲区。
参考:
在Visual的句柄创建后(如OnLoad、OnSourceInitialized代码里),使用下面方法:
方法一:
HwndSource source =PresentationSource.FromVisual(this) as HwndSource;
if(source != null) source.AddHook(WndProc);
方法二:
HwndSource source =HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
if(source != null) source.AddHook(WndProc);
注:两种方法中的this可换成Visual对象。
然后就可以HwndSourceHook委托的WndProc方法了:
private IntPtr WndProc(IntPtr hwnd,int msg,IntPtr wParam,IntPtr lParam,ref bool handled)
{
// Handle messages...
return IntPtr.Zero;
}