在 C# 中使用 P/Invoke 调用 Win API,当 _Out_ 参数可以为 NULL 或非 NULL 时
本文关键字:NULL 参数 Out 或非 API Win 调用 Invoke | 更新日期: 2023-09-27 18:32:22
我是C#的新手,我正在通过编写一些小工具来学习C#。
有许多 Windows API 的指针参数可以是 NULL 或非 NULL,具体取决于不同的用例。我的问题是,如何在 DllImport 中声明这些参数?
例如:
LONG QueryDisplayConfig(
_In_ UINT32 Flags,
_Inout_ UINT32 *pNumPathArrayElements,
_Out_ DISPLAYCONFIG_PATH_INFO *pPathInfoArray,
_Inout_ UINT32 *pNumModeInfoArrayElements,
_Out_ DISPLAYCONFIG_MODE_INFO *pModeInfoArray,
_Out_opt_ DISPLAYCONFIG_TOPOLOGY_ID *pCurrentTopologyId
);
当Flags
QDC_DATABASE_CURRENT
时,最后一个参数pCurrentTopologyId
不能为空。当Flags
是其他值时,pCurrentTopologyId
必须为 null。
如果参数声明为"out IntPtr"或"ref IntPtr",以便 API 可以更改引用的内存。但是,如果按照 API 的要求将其传递 IntPtr.Zero,则 API 调用将返回ERROR_NOACCESS。
[DllImport(user32_FileName, SetLastError=true)]
internal static extern int QueryDisplayConfig(
[In] QDC_FLAGS Flags,
[In, Out] ref UInt32 pNumPathArrayElements,
[Out] DISPLAYCONFIG_PATH_INFO[] pPathInfoArray,
[In, Out] ref UInt32 pNumModeInfoArrayElements,
[Out] DISPLAYCONFIG_MODE_INFO[] pModeInfoArray,
out IntPtr pCurrentTopologyId
);
如果参数声明为"IntPtr",则可以将 IntPtr.Zero 作为 NULL 指针传递。但是,如果传递 IntPtr,API 调用也将返回ERROR_NOACCESS。
[DllImport(user32_FileName, SetLastError=true)]
internal static extern int QueryDisplayConfig(
[In] QDC_FLAGS Flags,
[In, Out] ref UInt32 pNumPathArrayElements,
[Out] DISPLAYCONFIG_PATH_INFO[] pPathInfoArray,
[In, Out] ref UInt32 pNumModeInfoArrayElements,
[Out] DISPLAYCONFIG_MODE_INFO[] pModeInfoArray,
IntPtr pCurrentTopologyId
);
我不希望声明不同版本的 extern 函数,尤其是当不同参数组合的数量可能很多时。
有什么建议吗?
当你有一个作为枚举的可选参数时,就像你在这里一样,那么我认为除了声明两个单独的重载之外别无选择。用于传递 null 的重载声明参数,如下所示:
IntPtr pCurrentTopologyId
调用此重载时,始终传递IntPtr.Zero
。
在传递非 null 值时调用的重载,如下所示声明参数:
out DISPLAYCONFIG_TOPOLOGY_ID pCurrentTopologyId
其中DISPLAYCONFIG_TOPOLOGY_ID
是基类型为 int
的 C# enum
。
您的代码弄错了第二个变体,因为您将其声明为 out IntPtr pCurrentTopologyId
。好吧IntPtr
64 位(8 字节而不是 4 字节)的大小是错误的。
如果你只想声明一个p/invoke而不使用重载,那么你只需将工作转移到其他地方。为此,您必须选择第一个选项:
IntPtr pCurrentTopologyId
当你想通过IntPtr.Zero
时很好。但是当你需要传递枚举变量的地址时,你需要使用 GCHandle
和 AddrOfPinnedObject
来固定你的变量。所有这些都完全可能,但更多的样板。所以,任你挑选!