Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ dll returned string is corrupted in the C# caller, why?

I have a C# app that calls a C++ DLL.

In C#, I have code as follows:

[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static String Func1(String arg);
......
String arg = "test text";
String retstring = Func1(arg);

In CPP, I have the function defined as follows:

extern "C"
{
__declspec(dllexport) LPWSTR Func1(LPWSTR arg)
{
          ....
          LPWSTR ret1 = L"1?2?3?4?5";
          LPWSTR ret2 = SomeActualFunction(arg);
          retturn ret1; // return ret2;
     }
}

If I return ret1 in C++'s Func1(), all works fine. And in VS2008's memory window, I can see correct Unicode binaries. In C++ the binaries of ret1 is

"31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"

, and in C# the binaries of retstring is

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 31 00 3f 00 32 00 3f 00 33 00 3f 00 34 00 3f 00 35 00"

. I think the C# binaries

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00"

are the header of System.String type.

What's more, if I add the following line just before return in CPP code, I can get correct retstring in C# code too:

ret2 = L"1?2?3?4?5";

But when I return ret2 in the C++ DLL, the returned string ret in C# seems to be corrupted. The binaries in the C++ DLL are correct Unicode on my inspection. But the binaries of retstring in the C# code are

"28 67 a3 f7 fe 07 00 00 0a 00 00 00 09 00 00 00 dd dd dd dd dd dd dd dd dd dd dd dd ....".

I can only notice ret2 is longer than ret1 - ret2 has some hundreds of WCHARs.

Any ideas? Thx in advance.

like image 779
McArthor Lee Avatar asked Jun 15 '11 07:06

McArthor Lee


3 Answers

I would always use a BSTR for this because it makes the responsibility of memory allocation/deallocation transparent.

C++

#include <comutil.h>
BSTR GetSomeText()
{
    return ::SysAllocString(L"Greetings from the native world!");
}

C#

[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();

You don't say how your strings are being allocated, but as soon as you use a dynamically allocated string you need to tackle that issue. The great thing about BSTR is that it uses the shared COM allocator which enables the C# marshaller to deallocate the string with the same allocator as the C++ code that allocated it.

like image 107
David Heffernan Avatar answered Nov 03 '22 10:11

David Heffernan


[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static String Func1(String arg);

or

[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static IntPtr Func1(String arg);
// In your calling code
string result = Marshal.PtrToStringUni(Func1("somestring"));
like image 30
Waleed Avatar answered Nov 03 '22 08:11

Waleed


Is your func __cdecl or __stdcall calling convention? IIRC, the default is for C# is __stdcall, but the default for C++ is __cdecl. Try adding CallingConvention=CallingConvention.Cdecl.


Another possibility: Since you say returning a static string works, is the pointer returned by SomeActualFunction still valid? If it pointed to a local buffer in that function, it would no longer be valid after returning from the function.

like image 1
Mark Tolonen Avatar answered Nov 03 '22 08:11

Mark Tolonen