Marshalling LayoutKind. Явная структура с перекрывающимися ошибками в сборке выпуска

У меня есть структура, в которой неперекрывающееся поле указано как перекрытое.

[FieldOffset(8)]
Int32 X;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
[FieldOffset(12)]
string Y;

[FieldOffset(28)]
int Z;

Сообщенная ошибка:

Не удалось загрузить тип "XXX"... он содержит поле объекта со смещением 12, которое неправильно выровнено или перекрыто необъектным полем.

Это происходит только в конфигурации Release (флаги TRACE, DEBUG и небезопасный код включены, оптимизация отключена), догадываясь - что с этим происходит?

UPD: спасибо @svick. Подтвердили, что сборка x64 - это не то, что нужно для сортировки.

3 ответа

Решение

Во-первых, конфигурация релиза не имеет к этому никакого отношения. Это влияет на платформу Target: если вы установите x64, вы получите это исключение, но если вы установите x86, он будет работать нормально.

Я думаю, что причина такого поведения в том, что FieldOffset используется для указания макета struct в управляемой памяти (даже если документация не говорит об этом), но MarshalAs не используется в управляемой памяти.

Из-за этого объект в управляемой памяти содержит ссылку со смещением 12. И поскольку все ссылки должны быть выровнены в.Net (до 4 байтов в 32-битном приложении и до 8 байтов в 64-битном), вы получите исключение если вы запускаете приложение как 64-битное.

Итак, проблема не в том, что у вас есть перекрывающиеся поля, а в другой части сообщения об ошибке: поле выровнено неправильно.

Простой обходной путь - скомпилировать приложение как x86. Если это невозможно для вас, я не уверен, как это исправить.

Обозначая правильный ответ @svick, проблема в том, что ваше объявление структуры нарушает твердое обещание, что CLR делает объектные присвоения атомарными. Это не может работать в 64-битном режиме, при смещении в 12 указатель объекта может находиться в конце строки кэша. Доступ к такому смещенному члену всегда требует двух операций чтения или записи, и это никогда не может быть атомарным. Я думаю, что это на самом деле ошибка в верификаторе типов CLR, но это не поможет вам преодолеть эту проблему.

Конечно, вы делаете это для взаимодействия с 32-битным кодом, и вы правильно изменили целевой параметр Platform для сборки Debug, но забыли сделать это для сборки Release. Это индивидуальная настройка. Легко исправить, просто измените настройку для версии Release.

Если вам действительно нужно это для работы в 64-битном режиме, тогда вам нужно объявить это как fixed char[16] вместо.

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

Другие вопросы по тегам