DllImport Null-Terminated Return String AccessViolationException

I am trying to interact with a Dll that implements several functions, one of which takes a string with a terminating zero and int and returns a string with a zero termination. I tried interacting with this way:

[DllImport(dll_loc)] [return : MarshalAs(UnmanagedType.LPStr)] public static extern StringBuilder GetErrorMessage([MarshalAs(UnmanagedType.LPStr)] StringBuilder message, int error_code); 

Then I try to call the method as follows:

 StringBuilder message = new StringBuilder(1000); StringBuilder out2 = new StringBuilder(1000); out2 = GetErrorMessage(message, res0); 

However, when I try to do this, an AccessViolationException message appears informing me that I am trying to access protected memory.

I managed to declare another method as such:

 [DllImport(dll_loc)] public static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder version); 

and calls it in the same way, but this method will not work for the current function call.

I also tried to return IntPtr, as the documentation technically states that the method returns a pointer to the first character of the string buffer, but to no avail.

Can anyone understand what could be wrong here? What may be different between the two methods that cause the DLL to try to access memory, it should not. Or, how would you recommend debugging this problem?

+3
source share
2 answers

C functions returning a string is a memory management issue. String C requires an array, and memory for this array must be freed after the string has been consumed. This makes such functions very difficult to use in a C program that cannot be used with pinvoke. This is also a classic C error, returning a pointer to a line in the stack.

The pinvoke router will attempt to free the returned string as required to avoid a memory leak. It will use CoTaskMemFree (). This doesn't always work well, it is rare that C code actually used CoTaskMemAlloc to allocate memory for an array. In XP, this will result in a memory leak without memory. Vista and Win7 have much more strict heap managers; they will cause a debug break if an unmanaged debugger is connected. Then run the AccessViolation program.

You can avoid automatic marshaling by declaring the return type as IntPtr and marshal the string yourself. Usually with Marshal.PtrToStringAnsi (). But then you still face the task of freeing memory. You cannot, you do not have a CRT heap descriptor, and you cannot call free ().

C functions that return strings must be declared with an argument that passes a string buffer. And an argument indicating how large the buffer is. Like this:

  int GetErrorMessage(int errorCode, char* buffer, size_t bufferSize); 

Now it is simple, the caller can allocate memory for the buffer and free it. Function C simply copies the string to the buffer. The return value can be used to indicate how many characters have been copied, or indicate that a larger buffer is needed. Do not bufferSize argument, buffer overflows are fatal and raise the FatalExecutionEngineError dangerous exception, an exception that is as undebuggable as AV, because GC heap corruption is not detected until much later. You are using a StringBuilder from the C # side, appropriately initialized with nonzero capacity. Value bufferSize.

+10
source

Replace all StringBuilder with System.IntPtr , highlight only message var with System.Runtime.InteropServices.Marshal.AllocHGlobal

then convert message or out2 to string using PtrToStringAuto

then call System.Runtime.InteropServices.Marshal.FreeHGlobal

Always worked for me this way, dealing with char* or wchar_t* .

+1
source

Source: https://habr.com/ru/post/905141/


All Articles