调用非托管 dll 时发生 AccessViolation

发布于 2024-08-11 06:09:28 字数 1008 浏览 9 评论 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 内部,任何对 ei_connect() 的调用,或任何读取 erl_errno 的尝试(这些在 ei 中定义) .lib)在 XP 上运行并被托管代码调用时会导致尝试读取或写入受保护的内存,从而导致应用程序崩溃。它不可能是一件微不足道的事情,因为它可以在 Vista 上运行,并且在由非托管代码调用时也可以运行。

When calling an unmanaged Dll from a c# application I get an AccessViolationException. The strange thing is that the exported function has no arguments, so the problem is not in the Marshalling of data. The function gets no argument and just returns an integer. Also note that calling convention is not an issue. An identical function with the same zero arguments and integer return value (but different name) works just fine. What are the remaining candidate reasons that such a call could cause this exception considering the fact that marshalling and calling convention is ruled out?

UPDATE: The dll function is correct because if called from other unmanaged code through plain linking, then it works perfectly.

UPDATE 2: Everything is compiled and run on 32 bit. I tried Win XP SP2 and Vista. Here is an interesting fact: On Vista Systems it works like a charm. On XP it fails.

UPDATE 3: I didnt get the source code but I learned what essentially this dll does, so i tried to reproduce the problem with my own dll. Here is the story: The original dll is some kind of a wrapper to ei.lib (Erlang's c interface library). It exports some helper funcs. So to reproduce the problem I have made a wrapper dll around ei.lib which exports only one function, namely "test()". I did that so I wouldnt mess with marshalling and stuff. I wanted just to test an initialization, connecting and sending a message. So this test() func of my dll just calls ei_connect_init(), then ei_connect() and finaly ei_reg_send(), with arguments hardcoded inside. The problem is that if I call this dll and use the test() function from another unmanaged code, it works ok. Message is sent. But when I call it from c# through DllImport then it works only on Vista. Not on XP. On XP it fails with a AccessViolationException on the .net layer. I ve tried to trace down the problem and I see that from inside my dll, any call to ei_connect(), or any attempt to read erl_errno (these are defined in ei.lib) when running on XP and being called by managed code result in trying to read or write protected memory so the app crashes. It cant be something trivial since it works on Vista and it works when called by unmanaged code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冰葑 2024-08-18 06:09:28

好的,我想我知道问题所在: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( 线程 )
有一些限制。如果一个DLL
声明任何非本地数据或对象
__declspec( thread ),它可能会导致
如果动态保护故障
已加载。 DLL加载后
LoadLibrary,导致系统故障
每当代码引用
非本地 __declspec( thread ) 数据。
因为全局变量空间为
线程在运行时分配,
该空间的大小基于
的要求的计算
申请加上要求
所有静态 DLL
已链接。当您使用 LoadLibrary 时,您
无法扩展此空间以允许
声明的线程局部变量
与 __declspec( 线程 ) 。使用 TLS
DLL 中的 API,例如 TlsAlloc
如果 DLL 可能是,则分配 TLS
使用 LoadLibrary 加载。

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

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

OK I think i know the problem: the ei.lib uses TLS (Thread Local Storage). In the file ei_pthreads.c of the ei interface source code there is this snippet:

#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

If USE_DECLSPEC_THREAD is not defined then, lower in the source file, TLS Api is used instead.
Now, from msdn i found that:

On Windows operating systems before
Windows Vista, __declspec( thread )
has some limitations. If a DLL
declares any nonlocal data or object
as __declspec( thread ), it can cause
a protection fault if dynamically
loaded. After the DLL is loaded with
LoadLibrary, it causes system failure
whenever the code references the
nonlocal __declspec( thread ) data.
Because the global variable space for
a thread is allocated at run time, the
size of this space is based on a
calculation of the requirements of the
application plus the requirements of
all the DLLs that are statically
linked. When you use LoadLibrary, you
cannot extend this space to allow for
the thread local variables declared
with __declspec( thread ). Use the TLS
APIs, such as TlsAlloc, in your DLL to
allocate TLS if the DLL might be
loaded with LoadLibrary.

So , since I use the erl interface libs provided with the precompiled binary distribution of erlang for windows, I wonder if they defined USE_DECLSPEC_THREAD when compiling those binaries. If not then I am in dead end and I will try something else to do my job with. If they did define it then I must install cygwin and recompile the sources without defining it. (Yikes...).

FINAL UPDATE: Indeed this was the problem. I had to install cygwin and compile the erl_interface code again without defining USE_DECLSPEC_TRHEAD. Also there is another little catch when recompiling, a tiny change is needed so that definition of _WIN32_WINNT happens before inclusion of winbase.h because after the omission of USE_DECLSPEC_THREAD the code uses SwitchToThread which is defined in winbase.h only if _WIN32_WINNT is defined and with a value greater than 0x400.

暖风昔人 2024-08-18 06:09:28

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

This exception occurs when the unmanaged code causes a memory access violation. Check to see that the unmanaged function is correct. If you have the source for the unmanaged code you can also enable the debugger to step into the unmanaged code and see where the problem is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文