当_Out_参数可以为NULL或非NULL时,使用P / Invoke在C#中调用Win API [重复]

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

这个问题在这里已有答案:

我是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
);

FlagsQDC_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函数,特别是当不同参数组合的数量可以很多时。

有什么建议吗?

c# winapi pinvoke optional-parameters
1个回答
0
投票

如果你有一个枚举的可选参数,就像你在这里一样,那么我认为除了声明两个独立的重载之外别无选择。用于传递null的重载声明了如下参数:

IntPtr pCurrentTopologyId

调用此重载时,总是传递IntPtr.Zero

另一个重载,即传递非null值时调用的重载声明参数如下:

out DISPLAYCONFIG_TOPOLOGY_ID pCurrentTopologyId

其中DISPLAYCONFIG_TOPOLOGY_ID是C#enum,基本类型为int

您的代码错误地获取了第二个变体,因为您将其声明为out IntPtr pCurrentTopologyId。好吧IntPtr在64位上是错误的大小(8个字节而不是4个)。

如果您只想声明一个p / invoke并且不使用重载,那么您只需将工作转移到其他地方。要做到这一点,你必须选择第一个选项:

IntPtr pCurrentTopologyId

很好,如果你想通过IntPtr.Zero。但是当你需要传递枚举变量的地址时,你需要使用GCHandleAddrOfPinnedObject来固定你的变量。所有完美可能但更多的样板。所以,接受你的选择!

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