Закрепление структуры updateble перед переходом на неуправляемый код?

Я использую какой-то старый API и должен передать указатель на неуправляемый код, который работает асинхронно.

Другими словами, после передачи указателя структуры неуправляемому коду неуправляемый код копирует указатель и немедленно возвращается. Неуправляемый код может получить доступ к этой структуре в фоновом режиме, в другом потоке. У меня нет контроля над неуправляемым кодом, который работает в другом потоке или нить.

Фиксированный оператор {} не может использоваться для закрепления, поскольку он не предназначен для асинхронного неуправляемого пиннинга.

GCHandle может ссылаться только на ссылки, поэтому структура должна быть в коробке для использования GCHandle. Я попробовал, и он работает. Основная проблема заключается в том, что вы не можете обновить struct из управляемого кода. Чтобы обновить структуру, прежде всего нам нужно удалить ее, а затем обновить, а затем снова вставить, но... oops... box again?!? это означает, что предыдущий указатель в памяти по-прежнему указывает на старую несовременную структуру, а новая структура имеет другой указатель, а это значит, что мне нужно передать новый указатель на неуправляемый код... неприменимый в моем случай.

Как я могу привязать структуру в памяти без фиксированного оператора {}, и поэтому я могу обновить ее из управляемого кода без изменения ее указателя?

Спасибо.

Edit:

Просто подумал... есть способ привязать родительский объект, который содержит структуру, а затем получить указатель struct, а не объект контейнера?

Ответ 1

Небезопасный код является опцией?

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

Это компилирует и не генерирует исключение, но у меня нет неуправляемой функции, чтобы проверить, работает ли она.

От MSDN:

Когда AllocHGlobal вызывает LocalAlloc, он передает флаг LMEM_FIXED, который блокирует выделенную память. Кроме того, выделенная память не заполняется нулями.

Ответ 2

Использование закрепленной памяти в этом случае не является хорошей идеей, учитывая, что память для структуры должна быть действительной в течение длительного времени. 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;
  }
}

Ответ 3

Вместо фиксации вам нужно использовать Marshal.StructureToPtr и Marshal.PtrToStructure, чтобы упорядочить структуру в памяти, которая может использоваться в собственном коде.

Ответ 4

Пример структуры:

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

Ответ 5

Как насчет того, чтобы структура включала интерфейс ActOnMe() и метод вроде:

delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
interface IActOnMe<TT> {ActOnMe<T>(ActByRef<TT,T> proc, ref T param);}
struct SuperThing : IActOnMe<SuperThing>
{
  int this;
  int that;
  ...
  void ActOnMe<T>(ActByRef<SuperThing,T>, ref T param)
  {
    proc(ref this, ref param);
  }
}

Поскольку делегат принимает общий параметр по ссылке, в большинстве случаев должно быть возможно избежать накладных расходов на создание замыканий, передав делегат статическому методу вместе со ссылкой на структуру для переноса данных на этот метод или из этого метода, Кроме того, при запуске экземпляра с номером от SuperThing до IActOnMe<SuperThing> с уже коробочным кодом и вызовом ActOnMe<T> на нем будут отображаться поля этого экземпляра в коробке для обновления, в отличие от создания другой копии из них как будет происходить с приведением типа к структуре.