从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,它可以在非托管代码调用时运行。
答案 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 调试器进入非托管代码并查看问题所在。