在interop方法中为`ref struct`参数传递`null`引用

时间:2013-03-05 06:42:00

标签: c# null interop parameter-passing value-type

我正在使用C#来调用DLL函数。

[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
        pHandle handle,
        ref somestruct a,
        ref somestruct b);

如何为参数3传递null参考?

当我尝试时,我收到编译时错误:

  

无法从<null>转换为ref somestruct。

我也试过IntPtr.Zero

4 个答案:

答案 0 :(得分:5)

您有两种选择:

  1. 使somestruct成为一个类,并将函数签名更改为:

    [DllImport("MyDLL.dll", SetLastError = true)]
    public static extern uint GetValue(
        pHandle handle, somestruct a, somestruct b);
    

    除非您可以null作为ab的值传递,否则不得更改任何其他内容。

  2. 为函数添加另一个重载,如下所示:

    [DllImport("MyDLL.dll", SetLastError = true)]
    public static extern uint GetValue(
        pHandle handle, IntPtr a, IntPtr b);
    

    现在,您可以使用IntPtr.Zero调用该函数,以及对ref类型的对象somestruct调用:

    GetValue(myHandle, ref myStruct1, ref myStruct2);
    GetValue(myHandle, IntPtr.Zero, IntPtr.Zero);
    

答案 1 :(得分:0)

This answer建议让SomeStruct成为一个班级。我想展示一个看起来效果很好的想法的实现......即使你不能改变SomeStruct的定义(例如当它是System.Guid这样的预定义类型时;也参见{{3 }})。

  1. 定义通用包装类:

    [StructLayout(LayoutKind.Explicit)]
    public sealed class SomeStructRef
    {
        [FieldOffset(0)]
        private SomeStruct value;
    
        public static implicit operator SomeStructRef(SomeStruct value)
        {
            return new SomeStructRef { value = value };
        }
    }
    

    此处的基本想法与this answer相同。

  2. 将互操作方法定义更改为以下内容:

    [DllImport("MyDLL.dll", SetLastError = true)]
    public static extern uint GetValue(
        pHandle handle,
        ref SomeStruct a,
        [MarshalAs(UnmanagedType.LPStruct)] SomeStructRef b);
    
  3. 第三个参数b将是“可空的”。由于SomeStructRef是引用类型,因此您可以传递null引用。您还可以传递SomeStruct值,因为存在从SomeStructSomeStructRef的隐式转换运算符。并且(至少在理论上),由于[StructLayout] / [FieldOffset]编组指令,SomeStructRef的任何实例都应该像SomeStruct的实际实例一样进行编组。

    如果是互操作专家,那么我会很高兴能够验证这项技术的合理性。

答案 2 :(得分:0)

另一个明显的解决方案是使用unsafe代码并将interop方法声明更改为:

[DllImport("MyDLL.dll", SetLastError = true)]
unsafe public static extern uint GetValue(
        pHandle handle,
        ref somestruct a,
        somestruct* b);

请注意,该方法现已标记为unsafe,并且参数已从ref somestruct更改为somestruct*

这具有以下含义:

  • 只能从unsafe上下文中调用该方法。例如:

    somestruct s;
    unsafe { GetValue(…, …, &s);   }  // pass a struct `s`
    unsafe { GetValue(…, …, null); }  // pass null reference
    
  • 为了使上述工作正常,项目必须允许unsafe代码(在项目设置中,或通过/unsafe命令行编译器开关)。

  • 使用unsafe会导致无法验证的IL代码。 IIRC,这意味着加载这个程序集将需要完全信任(在某些情况下这可能会有问题)。

答案 3 :(得分:0)

从 .NET 5.0 开始,System.CompilerServices.Unsafe.NullRef<T>()

GetValue(myHandle, ref myStruct1, ref myStruct2);
GetValue(myHandle, ref Unsafe.NullRef<somestruct>(), ref Unsafe.NullRef<somestruct>());