我是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
必须为空。
如果参数声明为“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函数,特别是当不同参数组合的数量可以很多时。
有什么建议吗?
答案 0 :(得分:0)
如果你有一个枚举的可选参数,就像你在这里一样,那么我认为除了声明两个独立的重载之外别无选择。用于传递null的重载声明了如下参数:
IntPtr pCurrentTopologyId
调用此重载时,您始终会传递IntPtr.Zero
。
另一个重载,即传递非null值时调用的重载声明了如下参数:
out DISPLAYCONFIG_TOPOLOGY_ID pCurrentTopologyId
其中DISPLAYCONFIG_TOPOLOGY_ID
是基本类型为enum
的C#int
。
您的代码错误地获取了第二个变体,因为您将其声明为out IntPtr pCurrentTopologyId
。好IntPtr
在64位上是错误的大小(8个字节而不是4个)。
如果您只想声明一个p / invoke并且不使用重载,那么您只需将工作转移到其他地方。要做到这一点,你必须选择第一个选项:
IntPtr pCurrentTopologyId
想要传递IntPtr.Zero
时很好。但是当您需要传递枚举变量的地址时,您需要使用GCHandle
和AddrOfPinnedObject
来固定变量。所有完美可能但更多的样板。所以,请你选择!