你有几个选择
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) 使用IntPtr 和IntPtr.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) 重载您的方法以避免编组
使用struct,如2)。
只需为非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,以便在排除cStruct时自动传递null。
感谢@dialer 推荐这个方法。