在互操作方法中为"ref struct"参数传递"null"引用
本文关键字:quot struct 参数传递 null 引用 ref 操作方法 | 更新日期: 2023-09-27 18:02:09
我正在使用C#来调用DLL函数。
[DllImport("MyDLL.dll", SetLastError = true)]
public static extern uint GetValue(
pHandle handle,
ref somestruct a,
ref somestruct b);
如何传递参数3的null
引用?
当我尝试时,我得到了一个编译时错误:
无法从
<null>
转换为引用somestruct
我也试过IntPtr.Zero
。
您有两个选项:
-
将
somestruct
设为类,并将函数签名更改为:[DllImport("MyDLL.dll", SetLastError = true)] public static extern uint GetValue( pHandle handle, somestruct a, somestruct b);
通常,这不会改变任何其他内容,除了可以传递
null
作为a
和b
的值。 -
为该功能添加另一个过载,如下所示:
[DllImport("MyDLL.dll", SetLastError = true)] public static extern uint GetValue( pHandle handle, IntPtr a, IntPtr b);
现在,除了对
somestruct
:类型的对象调用ref
之外,还可以用IntPtr.Zero
调用函数GetValue(myHandle, ref myStruct1, ref myStruct2); GetValue(myHandle, IntPtr.Zero, IntPtr.Zero);
自.NET 5.0以来,出现了System.Runtime.CompilerServices.Unsafe.NullRef<T>((
GetValue(myHandle, ref myStruct1, ref myStruct2);
GetValue(myHandle, ref Unsafe.NullRef<somestruct>(), ref Unsafe.NullRef<somestruct>());
这个答案建议将SomeStruct
作为一个类。我想展示这个想法的一个实现,它似乎很好地工作……即使您无法更改SomeStruct
的定义(例如,当它是像System.Guid
这样的预定义类型时;另请参阅此答案(。
-
定义一个通用包装类:
[StructLayout(LayoutKind.Explicit)] public sealed class SomeStructRef { [FieldOffset(0)] private SomeStruct value; public static implicit operator SomeStructRef(SomeStruct value) { return new SomeStructRef { value = value }; } }
这里的基本思想与拳击相同。
-
将interop方法定义更改为以下内容:
[DllImport("MyDLL.dll", SetLastError = true)] public static extern uint GetValue( pHandle handle, ref SomeStruct a, [MarshalAs(UnmanagedType.LPStruct)] SomeStructRef b);
第三个参数b
将是"可以为null"的。由于SomeStructRef
是引用类型,因此可以传递null
引用。您也可以传递SomeStruct
值,因为存在从SomeStruct
到SomeStructRef
的隐式转换运算符。并且(至少在理论上(,由于[StructLayout]
/[FieldOffset]
编组指令,SomeStructRef
的任何实例都应该像SomeStruct
的实际实例一样进行编组。
如果一个互操作专家能够验证这项技术的可靠性,我会很高兴。
另一个显而易见的解决方案是使用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,这意味着加载此程序集需要完全信任(在某些情况下可能会出现问题(。