Как выделить GCHandle для структуры, когда в структуре содержится bool

Я пытаюсь создать дескриптор типа структуры, потому что мне нужен привязанный указатель к нему, но я получаю сообщение об ошибке "Объект содержит не примитивные или невоспроизводимые данные"

Моя структура выглядит следующим образом:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.U1)]
    public bool Test;
}

Теперь, когда я звоню,

var mystruct = new MyStruct();
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned);

Я получаю сообщение об ошибке "Объект содержит не примитивные или невоспроизводимые данные". Теперь я понимаю, что поле bool является неимпульсным типом. Но у меня создалось впечатление, что добавив атрибут MarshalAs, я мог бы сказать маршаллеру, как преобразовать тип. (Я также пробовал UnmanagedType.Bool)

Эта структура должна быть определена глобально, потому что она необходима во всем классе. Единственной причиной, по которой мне нужен указатель, является то, что у меня есть неуправляемый API, который должен передать эту структуру как указатель. Затем я должен получить эту структуру в элементах обратного вызова и чтения/обновления.

Итак, это основной сценарий.

  • Структура создается глобально в управляемом классе
  • Получен указатель на структуру
  • Указатель на структуру передается в API
  • API вызывает обратный вызов статического метода, когда мне тогда нужно получить свою структуру и прочитать/обновить элементы.

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

Кто-нибудь знает, как я могу получить привязанный указатель на мою структуру, чтобы я мог читать/изменять публичные элементы и иметь их в обратном вызове?

Спасибо

Ответ 1

У вас здесь несколько проблем. Использование структуры крайне нецелесообразно. Он будет вставлен в коробку перед вызовом GCHandle.Alloc(), и этот объект в коробке будет закреплен. Вы не можете видеть никаких обновлений с помощью своей переменной mystruct. Вместо этого используйте класс.

И избегать bool, это не-blittable тип из-за его высокой переменной реализации. Это 4 байта в C, 1 байт в С++, 2 байта в COM. Просто сделайте это байтом. Вы можете написать свойство, чтобы вернуть его в bool.

Итак:

[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
    private byte _test;
    public bool Test {
       get { return _test != 0; }
       set { _test = value ? 1 : 0; }
    }
}

Ответ 2

Вы правы, что говорите маршаллеру, как маршалировать тип.

Но это не принесет вам пользы, когда вы попытаетесь обойти маршаллера.

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

Если вы хотите использовать маршаллер:

Как правило, хороший способ справиться с этим - использовать его в обоих направлениях. Вы можете использовать Marshal.StructureToPtr (как вы уже нашли), вызвать внешнюю функцию, а затем использовать Marshal.PtrToStructure, чтобы преобразовать ее обратно в управляемое представление.

Или вы можете использовать методы, которые настроены таким образом, что сортировка происходит автоматически, без необходимости указывать это вручную. Например, вызов метода native, принимающего параметр ref MyStruct, позволит это выполнить.

Если вы не хотите использовать маршаллер:

Не используйте любые типы, требующие сортировки. Как пишет Hans Passant, вместо этого используйте другой тип, byte, вероятно, будет хорошим выбором.

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