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

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

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

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

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

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

спасибо.

Edit:

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

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

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

С MSDN:

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


использование закрепленной памяти в этом случае не является хорошей идеей, учитывая, что память для структуры должна быть действительной в течение длительного времени. GCHandle.Alloc () поместит структуру в коробку и сохранит ее в куче. С его закреплением это будет долгосрочная нагрузка на сборщик мусора, поскольку ему нужно постоянно находить путь вокруг скалы на дороге.

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

вместо закрепления, вам нужно использовать Маршал.StructureToPtr и Маршал.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);

как насчет того, чтобы структура включала 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 to IActOnMe<SuperThing>, а вызов ActOnMe<T> на нем будут выставлены поля этого коробочного экземпляра для обновления, в отличие от создания другой копии, как это произошло бы с typecast для структуры.