C # interop AccessViolationException с управляемым обратным вызовом - PullRequest
2 голосов
/ 02 марта 2011

У меня есть неуправляемая DLL, которая выделяет структуру и передает указатель на эту структуру.Я создал ac # эквивалент этой структуры и могу скомпилировать и запустить мой код для ее использования.Структура имеет дополнительный указатель внутри, чтобы вы могли подключить указатель функции, который будет вызываться при запуске неуправляемого кода.Когда я пытаюсь подключить управляемый делегат к указателю структуры и передать его обратно, он взрывается с AccessViolationException.Чего мне не хватает?

Немного подробнее:

Неуправляемый код c:

typedef struct MyStruct {
   :
   :
   int flags
   :
   int (*cback)(MyStruct *s, Other *o)
   :
} MyStruct;

Эквивалент C #:

[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
   :
   :
   [MarshalAs(UnmanagedType.I4)]
   public int flags;
   :
   public IntPtr cback;
   :
};

Получив указательв неуправляемую структуру I

managedMyStruct = (MyStruct)
    Marshal.PtrToStructure(pUnmanagedMyStruct, typeof(MyStruct));                    

managedMyStruct.cback = 
    Marshal.GetFunctionPointerForDelegate(ManagedDelegateRef);                    

// Update pointer
Marshal.StructureToPtr(managedMyStruct, pUnmanagedStruct, true);

Когда я передаю pUnmanagedStruct неуправляемой функции, которая в конечном итоге вызывает cback, мой делегат cback вызывается один раз, и приложение разрывается с AccessViolationException.

Любые подсказки с благодарностью получены.

A

Ответы [ 2 ]

1 голос
/ 02 марта 2011

На что указывает ManagedDelegateRef?Статический метод или метод экземпляра?Если это метод экземпляра, убедитесь, что экземпляр не получает мусор.

0 голосов
/ 20 сентября 2011

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

Я не получил AccessViolationException -- Я не получил никаких исключений - но, возможно, причина вашей проблемы такая же, как и у меня.

Исправление для моей проблемы было следующим:

Согласно [1] существуют различные соглашения о вызовах функций: более старый __cdecl и более новый __stdcall;неуправляемый C / C ++ использует __cdecl по умолчанию, а C # использует __stdcall по умолчанию.

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

К сожалению для меня, я использовал стороннюю DLL и не мог изменить соглашение о неуправляемых вызовах в ней.Что нужно было сделать для моей программы, так это сказать C #, что делегат, которого я передавал, должен был использовать соглашение __cdecl.

К сожалению, нет способа сообщить об этом C # напрямую.(Можно подумать, что будет атрибут, который можно было бы использовать, но, очевидно, MS не реализовал его для C #, хотя я считаю, что в управляемом C ++ он есть.)

Чтобы немного обойти это,необходимо использовать хак:

Вывод программы (DLL / EXE) необходимо декомпилировать с помощью команды ildasm в командной строке Visual Studio:

cmd>  ildasm /out=output.il OUTPUT.EXE

AnЗатем к методу Invoke делегата в коде IL был добавлен атрибут, указывающий ему использовать __cdecl соглашение о вызовах:

// output.il
.
.
.

.class public auto ansi sealed NAMESPACE.ManagedDelegate
       extends [mscorlib]System.MulticastDelegate
{
  .custom instance void NAMESPACE.UseCCallingConventionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ManagedDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance void  Invoke(native int pUser,
                                int32 state) runtime managed
  {
  } // end of method ManagedDelegate::Invoke

  .method public hidebysig newslot virtual 
          instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(native int pUser,

.
.
.

стало:

.
.
.

.class public auto ansi sealed NAMESPACE.ManagedDelegate
       extends [mscorlib]System.MulticastDelegate
{
  .custom instance void NAMESPACE.UseCCallingConventionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ManagedDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance void #####modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)##### Invoke(native int pUser,
                                int32 state) runtime managed
  {
  } // end of method ManagedDelegate::Invoke

  .method public hidebysig newslot virtual 
          instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(native int pUser,

.
.
.

без хэшей,(Имя типа делегата в этом примере - ManagedDelegate - я не был уверен, каким было имя вашего типа.)

(Примечание: [1] и [2] рекомендуют помещать атрибут заполнителя в делегаттак что вы можете легко найти метод в файле .il; UseCCallingConventionAttribute был моим.)

Затем файл кода был перекомпилирован с помощью ilasm:

cmd>  ilasm output.il

с помощью /DLL или /EXE, в зависимости от вашего типа вывода - /EXE по умолчанию.

И это было исправление, которое работало для моего проекта.

Весь этот процесс описан внемного подробнее в [1], и кто-то опубликовал Perl-скрипт для этого в [2].Я еще не автоматизировал вещи и являюсь VS n00b, поэтому я не знаю, может ли это быть добавлено как шаг в сборке или нет, но это так.

Надеюсь, это поможет.

[1] http://www.codeproject.com/KB/cs/cdeclcallback.aspx

[2] http://www.dotnet247.com/247reference/msgs/17/87210.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...