调用非托管DLL时的AccessViolation

时间:2009-11-13 14:54:37

标签: c# interop erlang unmanaged erl-interface

从c#应用程序调用非托管Dll时,我得到AccessViolationException。奇怪的是导出的函数没有参数,所以问题不在于数据的编组。该函数没有参数,只返回一个整数。另请注意,调用约定不是问题。具有相同零参数和整数返回值(但名称不同)的相同函数可以正常工作。考虑到编组和调用约定被排除在外,这种调用可能导致此异常的剩余候选原因是什么?

UPDATE:dll函数是正确的,因为如果通过普通链接从其他非托管代码调用,那么它可以正常工作。

更新2:所有内容都编译并在32位上运行。我试过Win XP SP2和Vista。这是一个有趣的事实:在Vista系统上,它就像一个魅力。在XP上它失败了。

更新3:我没有得到源代码,但我学会了这个dll的功能,所以我尝试用自己的dll重现问题。这是故事:原始的dll是某种ei.lib(Erlang的c接口库)的包装器。它导出一些辅助函数。所以为了重现这个问题,我在ei.lib周围创建了一个包装器,它只导出一个函数,即“test()”。我这样做,所以我不会搞乱编组和东西。我只想测试初始化​​,连接和发送消息。因此,我的dll的这个test()函数只调用ei_connect_init(),然后是ei_connect()和最终ei_reg_send(),内部硬编码参数。问题是如果我调用这个DLL并使用另一个非托管代码中的test()函数,它可以正常工作。消息已发送。但是当我从c#通过DllImport调用它时,它只适用于Vista。不是XP。在XP上,它在.net层上失败并出现AccessViolationException。我试图追查问题,我在我的dll内部看到,ei_connect()的任何调用,或任何尝试读取erl_errno(这些都是在ei.lib中定义)在XP上运行时被托管代码调用导致尝试读取或写入受保护的内存,以便应用程序崩溃。它不可能是微不足道的,因为它适用于Vista,它可以在非托管代码调用时运行。

2 个答案:

答案 0 :(得分:1)

好吧我想我知道这个问题:ei.lib使用TLS(线程本地存储)。在ei接口源代码的文件ei_pthreads.c中有以下代码段:

#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 )数据。   因为全局变量空间   一个线程是在运行时分配的   这个空间的大小是基于a   计算要求   申请加上要求   所有静态的DLL   链接。当您使用LoadLibrary时,您   无法扩展此空间以允许   声明的线程局部变量   与__declspec( thread )。使用TLS   您的DLL中的API,例如TlsAlloc   如果DLL可能是,则分配TLS   加载了LoadLibrary。

因此,由于我使用erc接口库提供了erlang的预编译二进制分发版,因此我想知道在编译这些二进制文件时是否定义了USE_DECLSPEC_THREAD。如果没有,那么我就陷入了死胡同,我会尝试别的东西来完成我的工作。如果他们确实定义了它,那么我必须安装cygwin并重新编译源代码而不定义它。 (让人惊讶...)。

最终更新:确实是这个问题。我必须安装cygwin并再次编译erl_interface代码而不定义USE_DECLSPEC_TRHEAD。在重新编译时还有另一个小问题,需要进行微小的更改,以便在包含winbase.h之前发生_WIN32_WINNT的定义,因为在省略USE_DECLSPEC_THREAD之后代码使用在winbase中定义的SwitchToThread。 h仅在定义_WIN32_WINNT且值大于0x400时才会生成。

答案 1 :(得分:0)

当非托管代码导致内存访问冲突时,会发生此异常。检查非托管功能是否正确。如果您拥有非托管代码的源代码,您还可以enable 调试器进入非托管代码并查看问题所在。