AccessViolation при вызове неуправляемой DLL - PullRequest
0 голосов
/ 13 ноября 2009

При вызове неуправляемой Dll из приложения c # я получаю AccessViolationException. Странно то, что экспортируемая функция не имеет аргументов, поэтому проблема не в распределении данных. Функция не получает аргументов и просто возвращает целое число. Также обратите внимание, что соглашение о вызовах не является проблемой. Идентичная функция с одинаковыми нулевыми аргументами и целочисленным возвращаемым значением (но с другим именем) работает просто отлично. Каковы остальные возможные причины, по которым такой вызов может вызвать это исключение, учитывая тот факт, что соглашение о распределении и вызове исключено?

ОБНОВЛЕНИЕ: функция dll верна, потому что если она вызывается из другого неуправляемого кода через простое связывание, то она работает отлично.

ОБНОВЛЕНИЕ 2: Все скомпилировано и работает на 32-битной. Я пробовал Win XP SP2 и Vista. Вот интересный факт: в Vista Systems это работает как шарм. На XP это не получается.

ОБНОВЛЕНИЕ 3: Я не получил исходный код, но я узнал, что по сути делает эта dll, поэтому я попытался воспроизвести проблему с моей собственной dll. Вот история: оригинальный dll является своего рода оболочкой для ei.lib (библиотека интерфейса Erlang's c). Он экспортирует некоторые вспомогательные функции. Итак, чтобы воспроизвести проблему, я сделал оболочку dll для ei.lib, которая экспортирует только одну функцию, а именно «test ()». Я сделал это, чтобы не связываться с сортировкой и прочим. Я хотел просто проверить инициализацию, подключение и отправку сообщения. Так что эта функция test () из моей библиотеки DLL просто вызывает ei_connect_init(), затем ei_connect() и, наконец, ei_reg_send() с аргументами, жестко закодированными внутри. Проблема в том, что если я вызываю эту dll и использую функцию test () из другого неуправляемого кода, она работает нормально. Сообщение отправлено. Но когда я вызываю его из c # через DllImport, тогда он работает только на Vista. Не на XP. В XP происходит сбой с AccessViolationException на уровне .net. Я пытался отследить проблему, и я вижу, что изнутри моей dll, любой вызов ei_connect(), или любая попытка прочитать erl_errno (они определены в ei.lib) при работе на XP и вызове управляемым код приводит к попытке чтения или записи защищенной памяти, поэтому приложение вылетает. Это не может быть чем-то тривиальным, так как он работает в Vista и работает при вызове неуправляемым кодом.

Ответы [ 2 ]

1 голос
/ 18 ноября 2009

ОК. Мне кажется, я знаю проблему: ei.lib использует TLS (Thread Local Storage). В файле ei_pthreads.c исходного кода интерфейса ei есть этот фрагмент:

#ifdef __WIN32__
#ifdef USE_DECLSPEC_THREAD
/* Define (and initialize) the variable __erl_errno */
volatile __declspec(thread) int __erl_errno = 0;
#else
static volatile DWORD errno_tls_index = TLS_OUT_OF_INDEXES;
static LONG volatile tls_init_mutex = 0;
#endif
#endif

Если USE_DECLSPEC_THREAD не определено, то ниже в исходном файле вместо него используется TLS Api. Теперь из MSDN я обнаружил, что:

В операционных системах Windows до Windows Vista, __declspec( thread ) имеет некоторые ограничения. Если DLL объявляет любые нелокальные данные или объект как __declspec( thread ), это может привести к ошибка защиты, если динамически загружен. После загрузки DLL LoadLibrary, это вызывает сбой системы всякий раз, когда код ссылается на нелокальные __declspec( thread ) данные. Потому что глобальная переменная пространство для поток выделяется во время выполнения, Размер этого пространства основан на расчет требований приложение плюс требования все DLL, которые статически связаны между собой. Когда вы используете LoadLibrary, вы не может расширить это пространство, чтобы учесть объявлены локальные переменные потока с __declspec( thread ). Используйте TLS API, такие как TlsAlloc, в вашей DLL для выделить TLS, если DLL может быть загружен с помощью LoadLibrary.

Итак, поскольку я использую библиотеки интерфейсов erl, предоставляемые с предварительно скомпилированным двоичным дистрибутивом erlang для окон, мне интересно, определили ли они USE_DECLSPEC_THREAD при компиляции этих двоичных файлов. Если нет, то я нахожусь в тупике, и я попробую что-то еще, чтобы выполнить свою работу. Если они это определили, я должен установить cygwin и перекомпилировать исходники, не определяя его. (Хлоп ...).

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ: Действительно, это была проблема. Мне пришлось установить cygwin и снова скомпилировать код erl_interface без определения USE_DECLSPEC_TRHEAD. Также есть еще одна маленькая хитрость при перекомпиляции, требуется небольшое изменение, чтобы определение _WIN32_WINNT происходило до включения winbase.h, потому что после пропуска USE_DECLSPEC_THREAD код использует SwitchToThread, который определен в winbase.h, только если _WIN32_WINNT определено и имеет значение больше 0x400.

0 голосов
/ 13 ноября 2009

Это исключение возникает, когда неуправляемый код вызывает нарушение доступа к памяти. Проверьте правильность неуправляемой функции. Если у вас есть источник для неуправляемого кода, вы также можете включить отладчик, чтобы войти в неуправляемый код и посмотреть, в чем проблема.

...