English 中文(简体)
AccessViolation when calling unmanaged dll
原标题:

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.

最佳回答

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.

问题回答

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.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签