如何在C#中处理null或者可选的dll struct参数

时间:2017-12-27 19:58:34

标签: c# null interop pinvoke optional-parameters

如何处理使用pinvoke从C#调用的dll方法中的可选struct参数?例如,lpSecurityAttributes parameter here在缺席时应通过null

传递struct的正确方法似乎是使用ref,但它不能包含可选参数,或者通常采用null

有哪些方法可以实现这一目标?

1 个答案:

答案 0 :(得分:16)

你有几个选择

1)使用class代替struct

我认为这种方法最简单。只需将struct声明为class

即可
[StructLayout(LayoutKind.Sequential)]
public class CStruct
{
    //member-list
}

然后声明你的方法:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(CStruct cStruct, ...);

如果您的可选参数恰好是最后一个参数,则可以使用CStruct cStruct = null作为参数。这允许您排除它而不是显式传递null。您还可以编写一个使用此方法的包装器方法,并确保可选参数最后一次。

2)使用IntPtrIntPtr.Zero

使用struct

[StructLayout(LayoutKind.Sequential)]
public struct CStruct
{
    //member-list
}

并将您的方法声明为:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);

在非null的情况下,marshal the struct指向并调用方法:

IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CStruct)));
try{
    Marshal.StructureToPtr(myCStruct, ptr, false);
    DLLFunction(ptr, ...);
} finally {
    Marshal.FreeHGlobal(ptr);
}

null案例中,使用IntPtr.Zero调用方法:

DLLFunction(IntPtr.Zero, ...);

同样,如果这恰好是列表中的最后一个(或者使用包装器来实现),则可以使此参数成为可选参数。通过使用IntPtr cStruct = default(IntPtr)作为参数来执行此操作。 (作为default(IntPtr) creates a IntPtr.Zero。)

3)重载您的方法以避免编组

使用 2中的struct

只需为非null案例声明一个选项:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(ref cStruct, ...);

和另一个null案例:

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static extern int DLLFunction(IntPtr cStruct, ...);

传递struct时会自动调用第一个方法,传递IntPtr.Zero时会自动调用第二个方法。如果使用可选参数声明IntPtr版本(如上面 2的底部所示),则会在您排除cStruct参数时自动调用它。

4)使用unsafe

的原始指针

使用 2)中的结构并声明您的方法(请注意unsafe关键字):

[DllImport("mydll.dll", OptionName = optionValue, ...)]
static unsafe extern int DLLFunction(CStruct* cStruct, ...);

在非null案例中,您在&myCStruct案例中传递了null,而只是null。与 1)一样,如果此可选参数为最后一个,则可以将参数声明为CStruct* cStruct = null,以便在排除null时自动传递cStruct

感谢@dialer建议使用此方法。