How to prevent AccessViolationException when returning a string from C ++ to C # on 64-bit Windows?

I use a third-party, proprietary DLL for which the source code is not available to me. However, the Wrapper code is available to me, which seems to have been automatically generated using SWIG 1.3.39. The shell code consists of a C ++ file that compiles (using some headers describing the DLL) into a DLL and a C # project that calls PInvoke calls to the C ++ DLL shell.

In my interpretation of the vendor documentation, I compiled everything in the solution as x86 or x64, depending on the target platform. The provider provides both 32-bit and 64-bit versions of its own DLL, and I ensured that I am using the correct one for this assembly. My car is 32 bit. Testing the x86 version of my application on my machine, both in releases and in debug builds, seems to work fine. However, in the 64-bit version, the application runs in debug mode, but with a System.AccessViolationException disabled in Release mode, it does not work.

I read this nice blog post that seems to describe the debugging and release issue well, as well as this question and answer that led to the blog post. However, I am not sure how to fix the problem in this case.

It seems that an AccessViolationException occurs the first time a string of any real length is returned (or is trying to be returned) from a C ++ shell. Here is the abusive C # code:

// In one file of the C# wrapper: public string GetKey() { // swigCPtr is a HandleRef to an object already created string ret = csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr); return ret; } // In the csWrapperPINVOKE class in another file in the C# wrapper: [DllImport("csWrapper.dll", EntryPoint="CSharp_mdMUHybrid_GetKey")] public static extern StringBuilder mdMUHybrid_GetKey(HandleRef jarg1); 

And the nasty C ++ code from the C ++ shell:

 SWIGEXPORT char * SWIGSTDCALL CSharp_mdMUHybrid_GetKey(void * jarg1) { char * jresult ; mdMUHybrid *arg1 = (mdMUHybrid *) 0 ; char *result = 0 ; arg1 = (mdMUHybrid *)jarg1; result = (char *)(arg1)->GetKey(); jresult = SWIG_csharp_string_callback((const char *)result); return jresult; } 

SWIGEXPORT has already been defined as __declspec(dllexport) . In debugging, I found that SWIG_csharp_string_callback , defined as:

 /* Callback for returning strings to C# without leaking memory */ typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *); static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL; 

was set to the delegate (in a C # wrapper):

 static string CreateString(string cString) { return cString; } 

I tried messing up this code to use constructs like Marshal.PtrToStringAut to no avail. How to fix and / or fix this problem?

+8
c ++ swig access-violation pinvoke
source share
1 answer

The right way to do this is to have your managed code allocate a buffer into which unmanaged code (string data) will be written. Assuming this is impractical for some reason, you need to isolate the string data so that it can be freed from managed code.

The usual approach is to allocate LocalAlloc memory, which can then be freed from managed code using Marshal.FreeHGlobal . So you no longer need (kludgy and obviously non-functional) SWIG_csharp_string_callback and CreateString .

C ++ Code :

 SWIGEXPORT HLOCAL SWIGSTDCALL CSharp_mdMUHybrid_GetKey(mdMUHybrid* jarg1) { char const* const str = jarg1->GetKey(); std::size_t const len = std::strlen(str); HLOCAL const result = ::LocalAlloc(LPTR, len + 1u); if (result) std::strncpy(static_cast<char*>(result), str, len); return result; } 

C # code :

 // In one file of the C# wrapper: public string GetKey() { return csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr); } // ... public static class csWrapperPINVOKE { // ... [DllImport("csWrapper.dll")] private static extern IntPtr CSharp_mdMUHybrid_GetKey(HandleRef jarg1); public static string mdMUHybrid_GetKey(HandleRef jarg1) { var ptr = CSharp_mdMUHybrid_GetKey(jarg1); try { return Marshal.PtrToStringAnsi(ptr); } finally { Marshal.FreeHGlobal(ptr); } } } 

As an aside, this little C ++ code snippet you showed is a disgusting relic of C-classes; if this representative is the rest of the code, then just wow ...: - /

+3
source share

All Articles