0

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

更新: dll 函数是正确的,因为如果通过普通链接从其他非托管代码调用,那么它可以完美运行。

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

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

4

2 回答 2

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 Vista 之前的 Windows 操作系统上,__declspec( thread ) 有一些限制。如果 DLL 将任何非本地数据或对象声明为__declspec( thread ),则在动态加载时可能会导致保护错误。使用 LoadLibrary 加载 DLL 后,只要代码引用非本地__declspec( thread )数据,就会导致系统故障。因为线程的全局变量空间是在运行时分配的,所以这个空间的大小是基于对应用程序需求的计算加上所有静态链接的 DLL 的需求。使用 LoadLibrary 时,不能扩展此空间以允许使用__declspec( thread ). 如果 DLL 可能与 LoadLibrary 一起加载,请使用 DLL 中的 TLS API(例如 TlsAlloc)来分配 TLS。

所以,由于我使用了 erl 的 erlang for windows 的预编译二进制发行版提供的 erl 接口库,我想知道它们是否USE_DECLSPEC_THREAD在编译这些二进制文件时定义。如果没有,那么我就走投无路了,我会尝试其他事情来完成我的工作。如果他们确实定义了它,那么我必须安装 cygwin 并重新编译源而不定义它。(哎呀...)。

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

于 2009-11-18T12:14:22.743 回答
0

当非托管代码导致内存访问冲突时,会发生此异常。检查非托管函数是否正确。如果您有非托管代码的源代码,您还可以启用调试器来单步执行非托管代码并查看问题所在。

于 2009-11-13T15:02:32.430 回答