如何从 C# 调用 CM_Register_Notification?

问题描述 投票:0回答:1

我发现 CM_Register_Notification 这似乎是获得设备添加和删除通知的最简单方法,如第一个注释here.

我什至找到了this它解释了如何去做。

不幸的是,这一切都是为 C++ 开发人员准备的。

那么如何从 C# 中调用它 (pInvoke)? 我什至不需要那个函数提供的信息。如果我被告知某些事情已经改变就足够了。然后我会用另一种方式检查发生了什么。 (任何帮助将不胜感激。它不一定是一个完整的答案。)

c# .net windows winapi pinvoke
1个回答
0
投票

文档都在这里。您只需要正确的 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),最好启动另一个线程以异步方式执行操作。

© www.soinside.com 2019 - 2024. All rights reserved.