我发现 CM_Register_Notification 这似乎是获得设备添加和删除通知的最简单方法,如第一个注释here.
我什至找到了this它解释了如何去做。
不幸的是,这一切都是为 C++ 开发人员准备的。
那么如何从 C# 中调用它 (pInvoke)? 我什至不需要那个函数提供的信息。如果我被告知某些事情已经改变就足够了。然后我会用另一种方式检查发生了什么。 (任何帮助将不胜感激。它不一定是一个完整的答案。)
文档都在这里。您只需要正确的 PInvoke 声明。
你需要的声明如下,注意有两种联合类型,你需要测试并确保它们正确工作
[DllImport("CfgMgr32.dll")]
static extern int CM_Register_Notification(
CM_NOTIFY_FILTER pFilter,
IntPtr pContext, // your custom info for callback
CM_NOTIFY_CALLBACK pCallback,
[Out] out IntPtr pNotifyContext
);
[DllImport("CfgMgr32.dll")]
static extern int CM_Unregister_Notification(IntPtr pContext);
struct CM_NOTIFY_FILTER
{
const int MAX_DEVICE_ID_LEN = 200;
public int cbSize = Marshal.SizeOf<CM_NOTIFY_FILTER>();
public FilterFlags Flags;
public CM_NOTIFY_FILTER_TYPE FilterType;
int Reserved;
public IdUnion union;
[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
public struct IdUnion
{
[FieldOffset(0)]
Guid ClassGuid;
[FieldOffset(0)]
IntPtr hTarget;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_DEVICE_ID_LEN)]
string InstanceId;
};
public CM_NOTIFY_FILTER()
{
}
}
[Flags]
enum FilterFlags
{
None = 0,
CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES = 0x00000001,
CM_NOTIFY_FILTER_FLAG_ALL_DEVICE_INSTANCES = 0x00000002
}
enum CM_NOTIFY_FILTER_TYPE
{
CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
}
delegate int CM_NOTIFY_CALLBACK(
IntPtr hNotify,
IntPtr Context,
CM_NOTIFY_ACTION Action,
IntPtr EventData,
int EventDataSize
);
struct CM_NOTIFY_EVENT_DATA
{
public CM_NOTIFY_FILTER_TYPE FilterType;
int Reserved;
// union
Guid ClassOrEventGuid;
int NameOffset;
int DataSize;
// more data added after struct
}
enum CM_NOTIFY_ACTION
{
/* Filter type: CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE */
CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
/* Filter type: CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE */
CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
/* Filter type: CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE */
CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
}
你会像这样使用它
static IntPtr _contextHandle;
static CM_NOTIFY_CALLBACK _callback = YourCallbackFunction;
// must keep a reference to the callback to avoid being disposed
static void RegisterCallback()
{
var filter = new CM_NOTIFY_FILTER
{
Flags = FilterFlags.CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES,
FilterType = CM_NOTIFY_FILTER_TYPE.CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE
// Or use a FilterType and a Device handle or GUID
};
var result = CM_Register_Notification(in filter, IntPtr.Zero, _callback, out _contextHandle);
if (result != 0)
throw new Exception($"Error occurred {result:x}");
}
static int YourCallback(IntPtr hNotify, IntPtr Context, CM_NOTIFY_ACTION Action, IntPtr EventDataPtr, int EventDataSize)
{
var EventData = Marshal.PtrToStructure<CM_NOTIFY_EVENT_DATA>(EventDataPtr);
var offsetOfMoreInfo = EventDataPtr + Marshal.SizeOf<CM_NOTIFY_EVENT_DATA>();
// Do something with the event
// Make sure whatever it is happens quickly, do not block
return 0;
}
static void UnRegister()
{
if (_contextHandle != IntPtr.Zero)
{
CM_Unregister_Notification(_contextHandle);
_contextHandle = IntPtr.Zero;
}
}
CM_NOTIFY_EVENT_DATA
结构包含额外的信息,这取决于传入的长度,这就是为什么我将它作为 IntPtr
传递的原因。
注意文档中的以下警告,看起来很严重:
确保尽快处理即插即用设备事件。如果您的事件处理程序执行任何可能阻塞执行的操作(例如 I/O),最好启动另一个线程以异步方式执行操作。