Закрепление структуры 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 для структуры.