【问题标题】:How do I handle null or optional DLL struct parameters如何处理 null 或可选的 DLL 结构参数
【发布时间】:2018-06-08 10:31:52
【问题描述】:

如何处理使用 pinvoke 从 C# 调用的 dll 方法中的可选 struct 参数?例如,lpSecurityAttributes parameter here 不存在时应传递null

struct的正确传递方式似乎是使用ref,但不能有可选参数,或者一般取null

有什么方法可以做到这一点?

【问题讨论】:

    标签: c# null interop pinvoke optional-parameters


    【解决方案1】:

    你有几个选择

    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) 重载您的方法以避免编组

    使用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 推荐这个方法。

    【讨论】:

    • 您也可以(阅读:我个人最喜欢的是)使用不安全的指针声明 P/Invoke 签名。 static unsafe extern int DLLFunction(TheStruct* struct, ...);。这有几个优点。您可以使用实际值类型而不是引用类型(如果堆栈分配性能很重要,则相关),可以传递null,不需要另一个重载,没有封送处理(实际上甚至强制它是 blittable,这反过来又提高了性能再次)并且它是类型安全的(不像IntPtr)。明显的缺点是您必须使用unsafe(尽管公平地说,使用IntPtr并不更安全)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-19
    • 2011-04-08
    • 2021-11-18
    相关资源
    最近更新 更多