Нарушение доступа там, где его раньше не было

Я P/вызываю Graphviz как показано здесь. Когда я написал эту запись в блоге, код работал просто отлично. Теперь я собираю HttpModule, который отображает графики Graphviz с использованием этого кода, но я получаю AccessViolationException на agmemread.

// Native signature
Agraph_t agmemread(char *);

// P/Invoke Signature
[DllImport(LIB_GRAPH)]
private static extern IntPtr agmemread(string data);

// Usage
IntPtr g = agmemread(data);

Как я уже сказал, раньше это работало отлично. Но теперь я не могу заставить свой код работать ни в чем. Даже мои старые приложения Graphviz, основанные на том же коде, больше не работают.

Что я мог изменить, что могло вызвать это? Я даже не скачивал новую версию Graphviz или что-то в этом роде, так что все библиотеки DLL одинаковы.

EDIT: я попытался изменить string на StringBuilder, но это дало тот же результат. Затем я добавил атрибут MarshalAs:

static extern IntPtr agmemread([MarshalAs(UnmanagedType.LPWStr)] string data);

При этом я больше не получаю AccessViolationException, но Graphviz не может правильно прочитать строку и возвращает нулевой указатель.


person David Brown    schedule 30.01.2010    source источник
comment
те же данные? Разрядность ОС одинаковая (32/64бит)? Вы знаете, действительно ли AV находится на этой линии или где-то внутри вызова agmemread()?   -  person Bruce    schedule 30.01.2010
comment
Те же данные, та же ОС. agmemread — это встроенная функция, поэтому я понятия не имею, что она делает под капотом, чтобы вызвать нарушение прав доступа.   -  person David Brown    schedule 30.01.2010
comment
Как насчет MarshalAs(UnmanagedType.LPStr) вместо этого? Я ничего не знаю о Graphviz, но Бинг говорит мне, что параметром является «char *», а не wchar.   -  person Bruce    schedule 30.01.2010
comment
LPStr тоже не работает. Я пробовал StringBuilder, потому что параметр - char *, но это ничего не дало. LPWStr - единственное, что хотя бы близко к рабочему состоянию.   -  person David Brown    schedule 30.01.2010


Ответы (1)


Неуправляемому коду редко требуется помощь C#, чтобы начать генерировать нарушения прав доступа. В вашей подписи P/Invoke нет ничего плохого, это не может быть причиной.

Наиболее распространенным источником AV в неуправляемом коде является повреждение кучи. Код C/C++ не имеет сборщика мусора, память должна управляться явно. Он не только должен позаботиться об освобождении памяти (иначе произойдет утечка), он также отвечает за выделение правильного размера и обеспечение того, чтобы код, который записывает в выделенную память, не записывал за конец выделенного блока памяти или записывает в память, которая уже была освобождена. Это последнее требование часто приводит к сбою кода C/C++.

Проблема с повреждением кучи заключается в том, что его крайне сложно диагностировать. Это может оставаться незамеченным довольно долгое время. Типичный ущерб заключается в том, что внутренняя структура кучи скомпрометирована или данные в другом выделении кучи перезаписаны. Это не вызывает проблемы до тех пор, пока блок кучи не будет освобожден или не будут использованы перезаписанные данные. Код, генерирующий исключение, фактически не несет ответственности за ущерб, нанесенный ранее. Что сбивает вас с пути, пытаясь найти источник проблемы.

Найти настоящего создателя проблем очень сложно, у вас будет всего несколько хлебных крошек, чтобы понять, что могло пойти не так. Это очень сложно, когда у вас есть исходный код C/C++, но помогает запуск его в отладочной сборке с распределителем отладки. Это невозможно, когда у вас нет исходного кода.

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

person Hans Passant    schedule 30.01.2010
comment
Я считаю, что вы попали в точку! До появления исключений нарушения прав доступа я передавал недопустимые данные в agmemread, что приводило к сбою. Итак, имея в виду то, что вы сказали, я подумал, что, возможно, agmemread не очищает должным образом, когда выходит из строя. Я перезапустил свой компьютер, чтобы получить чистый лист, и теперь он отлично работает с исходным кодом и правильными входными данными. Тем не менее, я начал пытаться воспроизвести его, выполнив несколько недопустимых вызовов agmemread, но на этот раз никаких нарушений прав доступа не обнаружено. Очень странно. Возможно, я напишу об этом разработчикам Graphviz. - person David Brown; 30.01.2010
comment
Кстати, когда я сказал вызвать сбой и сделать несколько недопустимых вызовов, я имел в виду, что функция возвращает нулевой указатель, что она и должна делать при возникновении ошибки. - person David Brown; 30.01.2010
comment
Конечно, код, который обычно тестируется меньше всего, — это код обработки ошибок. - person Hans Passant; 30.01.2010
comment
Я отправил отчет об ошибке команде Graphviz, и они думают, что это связано с тем, как синтаксический анализатор пытается восстановиться после ошибок. Надеюсь, это скоро разрешится. Спасибо, что снова указали мне правильное направление! - person David Brown; 31.01.2010
comment
Я получаю что-то подобное с GraphViz interop AccessViolationException в if (gvLayout(gvc, g, layout) != SUCCESS) ... Частота отказов составляет 1 из 4, может быть, но всегда в этом месте. Любые обновления? - person Jason Kleban; 27.01.2011
comment
gvc и g — это IntPtr, которые, как было проверено, не равны 0. layout — это строка. Теперь, если gvc и g должны быть закреплены или что-то в этом роде, я не могу придумать, что именно я могу контролировать. Вы упомянули ошибку обработки ошибок в GraphViz. Любая идея, что это за ошибка, поэтому мой формат DOT может ее избежать? Спасибо. - person Jason Kleban; 27.01.2011