在传递给非托管代码之前固定updateble结构?

DxC*_*xCK 10 .net c# struct unmanaged pinning

我使用一些旧的API,需要将结构的指针传递给异步运行的非托管代码.

换句话说,在我将struct指针传递给非托管代码之后,非托管代码会复制指针并立即返回.非托管代码可以在后台访问该结构,在另一个线程中.我无法控制在另一个线程和线程本身中运行的非托管代码.

fixed {}语句不能用于固定,因为它不是为异步非托管固定而设计的.

GCHandle只能引用引用,因此必须将结构框设置为使用GCHandle.我尝试过,它的确有效.它的主要问题是您无法从托管代码更新结构.要更新结构,首先我们需要将其取消装箱,然后更新,然后再次装箱,但是......哎呀......再次装箱?!?这意味着内存中的前一个指针仍然指向旧的非最新结构,而新结构有另一个指针,这意味着我需要将新指针传递给非托管代码...不适用于我的案件.

如何在没有固定{}语句的情况下在内存中固定结构,以便我可以不更改指针的情况下从托管代码更新它?

谢谢.

编辑:

只是想...有没有办法固定包含结构的父对象,然后获取结构的指针而不是容器对象?

Han*_*ant 6

在这种情况下使用固定内存不是一个好主意,因为结构的内存需要长时间有效.GCHandle.Alloc()将打包结构并将其存储在堆上.由于它被固定,对于垃圾收集器来说将是一个长期的负担,因为它需要不断地在路上的岩石周围寻找方法.

简单的解决方案是在非托管内存中为结构分配内存.使用Marshal.SizeOf()获取结构的大小,使用Marshal.AllocCoTaskMem()来分配内存.这将获得您需要传递给非托管代码的指针.使用Marshal.StructureToPtr()初始化内存.并使用PtrToStructure()读取非托管代码编写的结构的更新.

如果经常这样做,你将不断复制结构.这可能是昂贵的,取决于结构的大小.为避免这种情况,请使用不安全的指针直接访问非托管内存.一些基本语法:

using System;
using System.Runtime.InteropServices;

class Program {
  unsafe static void Main(string[] args) {
    int len = Marshal.SizeOf(typeof(Test));
    IntPtr mem = Marshal.AllocCoTaskMem(len);
    Test* ptr = (Test*)mem;
    ptr->member1 = 42;
    // call method
    //..
    int value = ptr->member1;
    Marshal.FreeCoTaskMem(mem);
  }
  public struct Test {
    public int member1;
  }
}
Run Code Online (Sandbox Code Playgroud)


dtb*_*dtb 5

不安全代码是一种选择吗?

// allocate unmanaged memory
Foo* foo = (Foo*)Marshal.AllocHGlobal(sizeof(Foo));

// initialize struct
foo->bar = 0;

// invoke unmanaged function which remembers foo
UnsafeNativeMethods.Bar(foo);
Console.WriteLine(foo->bar);

// update struct
foo->bar = 10;

// invoke unmanaged function which uses remembered foo
UnsafeNativeMethods.Qux();
Console.WriteLine(foo->bar);

// free unmanaged memory
Marshal.FreeHGlobal((IntPtr)foo);
Run Code Online (Sandbox Code Playgroud)

这会编译并且不会引发异常,但我手头没有非托管函数来测试它是否有效。

MSDN

当 AllocHGlobal 调用 LocalAlloc 时,它会传递一个 LMEM_FIXED 标志,这会导致分配的内存被锁定到位。此外,分配的内存不是零填充的。