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