DllImport Возвращение строки с нулевым символом в конце AccessViolationException - PullRequest
3 голосов
/ 15 июля 2011

Я пытаюсь взаимодействовать с Dll, который реализует несколько функций, одна из которых принимает строку с нулевым символом в конце и int и возвращает строку с нулевым символом в конце.Я пытался взаимодействовать с методом следующим образом:

[DllImport(dll_loc)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern StringBuilder GetErrorMessage([MarshalAs(UnmanagedType.LPStr)]
                                                       StringBuilder message, 
                                                       int error_code);

Затем я пытаюсь вызвать метод следующим образом:

StringBuilder message = new StringBuilder(1000);
StringBuilder out2 = new StringBuilder(1000);
out2 = GetErrorMessage(message, res0);

Однако, когда я пытаюсь это сделать, AccessViolationExceptionвыдает сообщение, что я пытаюсь получить доступ к защищенной памяти.

Мне удалось объявить другой метод как таковой:

[DllImport(dll_loc)]
public static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)]
                                        StringBuilder version);

и вызвать его таким же образом, но этот метод выигралне работает для текущего вызова функции.

Я также пытался вернуть IntPtr, поскольку технически в документации сказано, что метод возвращает указатель на первый символ строкового буфера, но безрезультатно.

Кто-нибудь знает, что здесь может пойти не так?Что может отличаться между этими двумя методами, что заставляет dll пытаться получить доступ к памяти, не должно.Или как бы вы порекомендовали отладить эту проблему?

Ответы [ 2 ]

10 голосов
/ 15 июля 2011

Функции C, которые возвращают строку, являются проблемой управления памятью.Для строки AC требуется массив, и память для этого массива должна быть освобождена после использования строки.Это делает такие функции очень сложными для использования в программе на Си, почти невозможно использовать с pinvoke.Это также классическая ошибка C, возвращающая указатель на строку в стеке.

Пинвоукс маршаллер попытается освободить возвращенную строку, как требуется, чтобы избежать утечки памяти.Он будет использовать CoTaskMemFree ().Это не часто приводит к хорошему завершению, редко, когда код C фактически использует CoTaskMemAlloc для выделения памяти для массива.На XP это приведет к тихой утечке памяти.В Vista и Win7 гораздо более строгие диспетчеры кучи, они будут вызывать разрыв отладки, если подключен неуправляемый отладчик.Затем выполните бомбардировку программы с помощью AccessViolation.

Вы можете избежать поведения автоматического маршалинга, объявив тип возвращаемого значения как IntPtr и выполнить маршалинг строки самостоятельно.Обычно с Marshal.PtrToStringAnsi ().Но тогда вы все еще сталкиваетесь с задачей освобождения памяти.Вы не можете, у вас нет дескриптора кучи CRT, и вы не можете вызвать функцию free ().

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

 int GetErrorMessage(int errorCode, char* buffer, size_t bufferSize);

Теперь все просто, вызывающая сторона может выделить память для буфера и освободить ее.И функция C просто копирует строку в буфер.Возвращаемое значение можно использовать, чтобы указать, сколько символов было скопировано, или указать, что необходим больший буфер.Не экономьте на аргументе bufferSize, переполнение буфера смертельно и вызывает страшное исключение FatalExecutionEngineError, исключение, которое можно отладить так же, как AV, так как повреждение кучи GC не обнаруживается намного позже.Вы используете StringBuilder на стороне C #, соответственно инициализированный с ненулевой емкостью.Значение bufferSize.

1 голос
/ 15 июля 2011

Заменить все StringBuilder на System.IntPtr, выделить только message var на System.Runtime.InteropServices.Marshal.AllocHGlobal

затем преобразуйте message или out2 в строку с PtrToStringAuto

, затем позвоните System.Runtime.InteropServices.Marshal.FreeHGlobal

Всегда работал для меня таким образом, когда имел дело с char* или wchar_t*.

...