C#学习教程:在传递给非托管代码之前固定updateble结构?分享


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

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

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

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

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

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

谢谢。

编辑:

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

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

// 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); 

这会编译并且不会抛出exception,但我手边没有非托管函数来测试它是否有效。

来自MSDN :

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

在这种情况下使用固定内存不是一个好主意,因为结构的内存需要长时间有效。 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; } } 

您需要使用Marshal.StructureToPtr和Marshal.PtrToStructure将结构编组到可在本机代码中使用的内存中,而不是固定。

结构示例:

 [StructLayout(LayoutKind.Sequential)] public struct OVERLAPPED_STRUCT { public IntPtr InternalLow; public IntPtr InternalHigh; public Int32 OffsetLow; public Int32 OffsetHigh; public IntPtr EventHandle; } 

如何将其固定到结构并使用它:

 OVERLAPPED_STRUCT over_lapped = new OVERLAPPED_STRUCT(); // edit struct in managed code over_lapped.OffsetLow = 100; IntPtr pinned_overlap_struct = Marshal.AllocHGlobal(Marshal.SizeOf(over_lapped)); Marshal.StructureToPtr(over_lapped, pinned_overlap_struct, true); // Pass pinned_overlap_struct to your unmanaged code // pinned_overlap_struct changes ... // Get resulting new struct OVERLAPPED_STRUCT nat_ov = (OVERLAPPED_STRUCT)Marshal.PtrToStructure(pinned_overlap_struct, typeof(OVERLAPPED_STRUCT)); // See what new value is int offset_low = nat_ov.OffsetLow; // Clean up Marshal.FreeHGlobal(pinned_overlap_struct); 

如果让struct包含一个ActOnMe()接口和方法如下:

 委托void ActByRef (ref T1 p1,ref T2 p2); 接口IActOnMe  {ActOnMe (ActByRef  proc,ref T param);}  struct SuperThing:IActOnMe   {   这个;    int;    ...    void ActOnMe (ActByRef ,ref T param)    {      proc(ref this,ref param);    }  } 

因为委托通过引用获取generics参数,所以在大多数情况下应该可以通过将委托传递给静态方法以及对结构的引用来避免创建闭包的开销,以便将数据传入或传出该方法。 此外,将已经装箱的SuperThing实例强制转换为IActOnMe并在其上调用ActOnMe将公开该盒装实例的字段进行更新,而不是创建另一个副本,就像对其进行类型转换一样。结构。

上述就是C#学习教程:在传递给非托管代码之前固定updateble结构?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—猴子技术宅(www.ssfiction.com)

本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ssfiction.com/ckf/1031791.html

发表评论

邮箱地址不会被公开。 必填项已用*标注