Отображение IntPtr в Struct через Marshal.PtrToStructure, вызывающее исключение нарушения прав доступа

Я пытаюсь использовать C++ DLL (сторонняя библиотека, реализующая протокол EMI, с доступным исходным кодом) в .NET. Я успешно выполнил сортировку, вызвал функции и заставил все работать нормально.

Проблема возникает, когда я хочу выполнить сортировку из IntPtr обратно в .NET Struct, вот код (измененный, как было предложено - удалено «ref» и изменено AllocHGlobal, чтобы выделить только размер emiStruct):

private EMI emiStruct;
private IntPtr emiIntPtr;

emiIntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(emiStruct));
Marshal.StructureToPtr(emiStruct, emiIntPtr, false);
EMIStruct.Error result = emi_init(emiIntPtr, hostname, portNumber, password, shortNumber, windowSize, throughput);
Marshal.PtrToStructure(emiIntPtr, emiStruct);

Последняя строка (PtrToStructure) вызывает исключение «Попытка чтения или записи в защищенную память. Часто это указывает на то, что другая память повреждена».

Кроме того, я вижу вывод отладки:

A first chance exception of type 'System.AccessViolationException' occurred in mscorlib.dll
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0xc3fffff8.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x01fffff7.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x00001f1d.

Я предполагаю, что проблема где-то в выделении памяти для указателя emiIntPtr. Хотя, когда я запускаю код и возникает проблема с подключением к серверу (например, сервер не найден), последующая маршалинг в Struct emiStruct выполняется правильно (без исключений). Проблема возникает только тогда, когда соединение успешно установлено и сервер отправляет ответ.

Кроме того, я написал пример приложения на C++, используя ту же библиотеку DLL, которую я пытаюсь использовать в .NET, и это приложение (когда я его компилирую) работает нормально — это означает, что DLL C++ должна быть в порядке и не вызывать сбоев.

Кроме того, я нашел несколько подсказок, чтобы проверить / снять отметку с нескольких свойств для компилятора проекта (используя JIT, скомпилировать его для процессора x86 и т. Д.), К сожалению, ничего из этого не помогло.

Есть ли у вас какие-либо предложения, в чем может быть проблема или как правильно выполнить инициализацию IntPtr в .NET и сопоставление между IntPtr и Struct?

Спасибо всем за ваши ответы:

Здесь я добавляю заголовок C++ функции emi_init:

FUNC( init)( EMI*           emi,         /* out */
const char*    hostname,    /* in  */
unsigned short port,        /* in  */
const char*    password,    /* in  */
const char*    origin_addr, /* in  */
int            window_sz,   /* in  */
         int            throughput); /* in  */

А вот объявление C# emi_init (я удалил атрибут "ref" для emiPtr, как было предложено):

[System.Runtime.InteropServices.DllImport("emi.dll", EntryPoint = "_emi_init")]
    public static extern EMIStruct.Error emi_init(
    System.IntPtr emiPtr,
    [System.Runtime.InteropServices.InAttribute()]  [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string hostname,
    ushort port,
    [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string password,
    [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string origin_addr,
    int window_sz, int throughput);

Тем не менее, все еще получаю то же исключение.


person Frankie    schedule 23.08.2011    source источник
comment
почему вы передаете ptr по ссылке? Это кажется странным. Было бы неплохо увидеть заголовки C++.   -  person David Heffernan    schedule 23.08.2011
comment
@David Heffernan: Интересное место :)   -  person leppie    schedule 23.08.2011
comment
@frankie Почему бы тебе не показать нам сторону интерфейса C++?   -  person David Heffernan    schedule 23.08.2011


Ответы (2)


Вы используете Marshal.PtrToStructure неправильно.

Для второго аргумента требуется тип iow typeof(EMI).

Возвращаемое значение содержит результирующую структуру.

Итак, решение должно быть:

var s = (EMI) Marshal.PtrToStructure(emiIntPtr, typeof(EMI));
person leppie    schedule 23.08.2011
comment
У меня тоже так раньше было, но безуспешно. Я только что попробовал это с предложенным вами синтаксисом, но все еще получаю то же исключение :( - person Frankie; 23.08.2011
comment
Попробуйте выделить только один «экземпляр», а не диапазон 100000. - person leppie; 23.08.2011
comment
@leppie Есть две перегрузки, и Фрэнки использует другую. - person David Heffernan; 23.08.2011
comment
@David Heffernan: вы не можете использовать коробочный тип значения в этой перегрузке, см. документы. Однако для этой перегрузки можно использовать ссылочный тип последовательного или фиксированного макета. - person leppie; 23.08.2011
comment
@leppie Очевидно, что с перегрузкой все в порядке, поскольку код компилируется. - person David Heffernan; 23.08.2011
comment
@David Heffernan: Это самая наивная вещь, которую я слышал за последние месяцы! - person leppie; 23.08.2011
comment
@David Heffernan: Если вы пропустили это, см. msdn.microsoft.com/en-us /library/30ex8z62 , особенно в разделе "Примечания". Последнее предложение. - person leppie; 23.08.2011
comment
@леппи Почему? Компилятор может проверить все заявленные вами требования и предположительно делает это. Это все, что я имел в виду. Все было бы намного проще, если бы Фрэнки показал нам C++! - person David Heffernan; 23.08.2011
comment
@ Дэвид Хеффернан: так говорят документы! Не убивайте мессенджера :) Я даже не знаю, почему он использует такой подход. Структура ref или unsafe были бы намного лучше. - person leppie; 23.08.2011
comment
Этот документ кажется противоречивым? Его нельзя использовать с типом значения, но можно использовать с последовательной или явной структурой. Я не эксперт по .net и предполагаю, что компилятор справится с этим. Если использование этой перегрузки было реальной проблемой, почему ошибка должна быть AV? - person David Heffernan; 23.08.2011
comment
@ Дэвид Хеффернан: я могу только предположить некоторые необходимые функции COM. - person leppie; 23.08.2011
comment
ref struct мне тоже нравится. Кто знает, что здесь происходит на самом деле?! - person David Heffernan; 23.08.2011
comment
@David Heffernan: Что касается AV, это может быть многое. Скорее всего, размеры нативной и управляемой структур не совпадают. - person leppie; 23.08.2011

Я предполагаю, что вы неправильно объявили первый параметр в С#. Вы объявили его как ref IntPtr, что эквивалентно EMI** в C++. Но держу пари, что декларация C++, которую вы, к сожалению, не включили, читается как EMI*. Так что просто удалите ссылку, и все должно быть хорошо.

Я ожидаю, что emi_init не читает из параметра EMI, т.е. имеет аут семантику. В этом случае вам не нужен вызов StructureToPtr перед вызовом emi_init.

person David Heffernan    schedule 23.08.2011