2011-03-01 85 views
2

我有一个非托管的dll,它分配一个结构并传递出一个指向该结构的指针。我创建了一个相当于该结构的c#,并且可以愉快地编译和运行我的代码来使用它。该结构中有一个可选的指针,允许您挂接一个函数指针,该指针在非托管代码运行时将被调用。当我尝试将托管代理挂接到结构的指针并将其传回时,它会爆发AccessViolationException。我错过了什么?C#互操作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; 
    : 
}; 

已经有了一个指向非托管结构我

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

managedMyStruct.cback = 
    Marshal.GetFunctionPointerForDelegate(ManagedDelegateRef);      

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

当一世 将pUnmanagedStruct传递给最终调用cback的非托管函数,我的cback委托会被调用一次,并且应用程序爆发AccessViolationException。

感激地收到了任何线索。

一个

+0

不可能没有看到ManagedDelegateRef的声明来猜测。肯定是错的。 – 2011-03-01 21:20:29

+0

是的,请提供有关ManagedDelegateRef的更多详细信息。 – Coincoin 2011-03-01 21:23:44

回答

1

什么是ManagedDelegateRef指向?静态方法或实例方法?如果它是一个实例方法,请确保该实例没有被垃圾收集。

0

我遇到了类似的情况:我将一个指向托管回调的指针传递给非托管代码,并且当调用回调被调用时,该函数运行一次,然后程序崩溃。

我没有得到一个AccessViolationException - 我没有得到的任何例外 - 但也许你的问题的原因是我的一样。

我的问题的解决方法如下:

根据文献[1]也有不同的函数调用约定:老__cdecl和新__stdcall;非托管C/C++默认使用__cdecl,默认情况下C#使用__stdcall

我猜你的非托管代码使用默认的__cdecl约定。如果您可以在非托管代码中更改惯例,那么这可能是您的修补程序。

对我来说不幸的是,我使用的是第三方DLL,无法更改其中的非托管调用约定。我的程序需要做的是告诉C#我所通过的代表是使用__cdecl惯例。

不幸的是,没有办法直接告诉C#。 (你会认为会有一个可以使用的属性,但显然MS没有为C#实现,尽管我相信托管的C++有一个)。

为了解决这个位需要一个劈的使用:

需要使用在Visual Studio命令提示的ildasm命令被反编译程序(DLL/EXE)的输出:

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

一个属性,然后加入到在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]中有更详细的概述,有人在[2]中发布了一个Perl脚本。我还没有自动化的东西,而且是一个VS n00b,所以我不知道这是否可以作为构建中的一个步骤添加,但它是。

希望这会有所帮助。

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

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