Я работаю с неуправляемой библиотекой C, в которой есть функция обратного вызова, которая вызывается из внутреннего потока в библиотеке C. Обратный вызов имеет параметр void * context.
Я хочу установить в этом контексте адрес экземпляра класса C#, чтобы я мог получить доступ к полям и свойствам членов из обратного вызова.
Я понимаю, что мне нужно закрепить память класса, чтобы гарантировать, что GC не перемещает его, поскольку код C примет копию адреса экземпляра. Однако GCHandle.Alloc () утверждает, что: «Экземпляр с непримитивными (непреобразуемыми) членами не может быть закреплен».
И, конечно же, достаточно кода, пытающегося закрепить класс или структуру, содержащую поле типа класса, не удается во время выполнения.
Как я могу передать адрес экземпляра класса C# в свой код C++ и убедиться, что адрес остается действительным (т. Е. Не перемещается GC)?
РЕДАКТИРОВАТЬ # 1 Исправление: это библиотека C, а не C++, как указывалось ранее (исправлено в тексте выше). Но это не должно иметь никакого значения.
Это прототип обратного вызова C:
void Callback(uint32_t hCamera, uint32_t dwInterruptMask, void *pvParams);
И завернутый в C#:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public unsafe delegate void PHX_AcquireCallBack(uint hCamera, uint dwInterruptMask,
IntPtr pvParams);
pvParams
передается в библиотеку C при установке обратного вызова. Библиотека хранит указатель, но не пытается получить к нему доступ. Он просто передает его обратно всякий раз, когда вызывает обратный вызов.
При использовании библиотеки из кода C++ я обычно передаю указатель this
на pvParams
.
Не могли бы вы поделиться с нами вызовом функции? Есть ли в этой библиотеке документация?
Поскольку сторона C++ принимает копию адреса, я считаю, что экземпляр C# должен быть закреплен, чтобы гарантировать, что он не перемещается сборщиком мусора. Код C++ - мой; см. EDIT для получения дополнительной информации.
вы можете использовать GCHandle.Alloc () в любом экземпляре класса с обычным типом дескриптора (не закрепленным)
@Selvin Верно, но я считаю, что мне нужно закрепить память, поскольку я передам адрес объекта в свою библиотеку C. Если вы думаете, что мне не нужно закреплять память, дайте мне знать, почему?
потому что GCHandle будет иметь текущие адреса вашего объекта, даже если GC переместит его ... и вы передадите указатель GCHandle там, а не адрес вашего объекта ... см. пример ... TextWriter tw
в этом примере будет вашим объектом контекста
в любом случае я бы изменил PHX_AcquireCallBack
на статический ... и я бы использовал this
из pvParams
, как в примере
Ваш пример работает (я думаю), потому что GCHandle находится в стеке и остается действительным до тех пор, пока обратный вызов не вернется. В моем случае «контекст» хранится в библиотеке C, и внутренний поток C вызывает обратный вызов. Поэтому мне пришлось бы сделать GCHandle членом моего класса или разместить его в куче. В любом случае GCHandle может быть перемещен сборщиком мусора, и, таким образом, указатель, удерживаемый C lib, будет недействительным. Или чего-то не хватает?