3
votes

Using this function in my C# exe, I try to pass a Unicode string to my C++ DLL:

    [DllImport("Test.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    public static extern int xSetTestString(StringBuilder xmlSettings);

This is the function on the C++ DLL side:

__declspec(dllexport) int xSetTestString(char* pSettingsXML);

Before calling the function in C#, I do a MessageBox.Show(string) and it displays all characters properly. On the C++ side, I do: OutputDebugStringW((wchar_t*)pString);, but that shows that the non-ASCII characters were replaced by '?'.

2
What encoding is pSettingsXML expected to be?Matthew Flaschen
Shouldn't your C++ function use wchar_t* instead of char* if it accepts Unicode?Joey
Not necessarily. It could be using UTF-8.Matthew Flaschen
pSettings is expected to be UTF-16, which is what I believe Microsoft uses internally (not sure if that is correct)Warpin
If it's UTF-16, I agree with @Johannes. It probably should be wchar_t, which is 16-bit on Windows.Matthew Flaschen

2 Answers

5
votes

Just change your export in native DLL to:

extern "C" __declspec(dllexport) int xSetTestString(wchar_t* pSettingsXML);

This will do the trick.

BTW - You cant simply do char* str1 = (wchar_t*)pSettingsXML; because it does not convert the string. You need to use wcstombs_s to convert from wchar_t* to char*. But in your case you don't have to do it.

Notes: Best practice IMO is to use TCHAR* instead of wchar_t* directly, and set your native dll project General option Character Set to Use Unicode Character Set. This defines TCHAR* as wchar_t*.

Mirosoft natively uses two sets of functions: ANSI, using 1-byte chracter, marked as FunctionNameA and Unicode, using 2-bytes character, marked as FunctionNameW. This Unicode is in fact UTF-16.

UTF-8 is a multi-byte string that uses 1-byte for standard character and 2-bytes for non-standard characters. To convert UTF-8 to UTF-16 you can use MultiByteToWideChar function.

0
votes

Try:

[DllImport("Test.dll")]
[return : MarshalAs(UnmanagedType.I4)]
private static extern int externalTestString(
    [MarshalAs(UnmanagedType. // The string type the C uses ansi/unicode/...) ] String st
    );

public int TestString(String st // or string builder here)
{
     // Perform input/output checks here + exception handling
}

Make sure the C++ functions are visible outside the DLL (using __declspec dllexport). Check using depends (comes with VS2005) if you can see the functions and what there name is.

If your C++ functions are NOT within classes, then you can use unmangled names if you use extern "C", if they are in classes, you will need to specify an entry point with the mangled names from depends.