1

I am having an issue passing a simple string from my .NET app, compiled as 64-bit, to my native DLL, also compiled as 64-bit.

C++ signature:

DllExport void SetFoo(LPWSTR foo);

C# signature:

[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
     [In][MarshalAs(UnmanagedType.LPWStr)] string foo);

Then I run the following code in C#:

X.SetFoo("some string");

When it reaches the debugger in the DLL, the value is swearing at me in Chinese: Ⴐ虘翺

When I change both the .NET and native code to 32-bit, the value I get in the debugger is the correct string. Where am I being stupid?

Minimal reproduction as a Visual Studio 2015 Solution: download here

To reproduce:

  • Create a new Visual Studio solution with a WinForms project.
  • Add a Win32 Project, of type DLL to the solution
  • Add the following files:

Foo.h:

extern "C" {
    __declspec( dllexport ) void SetFoo(LPWSTR);
}

Foo.cpp:

#include "stdafx.h"
#include "Foo.h"

DllExport void SetFoo(LPWSTR foo) 
{

}
  • Set a breakpoint on the opening brace in SetFoo
  • Add a button to the winforms form
  • Double click it, and call SetFoo("abc123").
  • Implement SetFoo:

Form1.cs:

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl,  CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);
  • Change the apps to build in 64-bit mode by opening Configuration Manager.
  • Set Win32Project1 to build as x64
  • Set WindowsFormApplication1 to build as x64, by picking platform new, pick x64, OK.
  • Change the output directory of WindowsFormsApplication1 to match the output directory of the other app.
  • Start without debugging.
  • Attach debugger (Ctrl+Alt+P) by setting Attach to to Managed (v4.5, v4.0) code, Native code and finding the process.
  • Observe value at breakpoint.

1 Answer 1

1

When you interpret ANSI encoded latin text as UTF-16 you see Chinese characters. That's clearly what is happening. So your C# code is sending ANSI encoded text somehow.

The p/invoke would be better written like this:

[DllImport(Library, CallingConvention = CallingConvention.Cdecl, 
    CharSet = CharSet.Unicode)]
internal static extern void SetFoo(string foo);

The [in] is not needed, it is the default. And the MarshalAs is pointless since you specified CharSet.Unicode. However, neither change affects the semantics.

The only sound explanations for what you describe in the question are:

  • The actual code is not as you have described it, or
  • There is a defect in the debugger.

I suggest that you change the unmanaged function to

DllExport void SetFoo(LPWSTR foo) 
{
    MessageBoxW(0, L"", foo, MB_OK);
}

If the message box displays the correct text then the conclusion would appear to be that the debugger is defective.

Sign up to request clarification or add additional context in comments.

8 Comments

David, thanks for your comment, but this is the case. I have reproduced a minimal solution and put it in my Dropbox. Run it without debugging, and then attach the debugger (with native support) after it has started, but before you press the button.
Please include your minimal repro in the question. Off site links are no good. When I use the code in my answer, it works correctly.
I've added a link to the solution in the question. There are quite a few steps to set it up to fail, so I suggest looking at the solution and seeing it fail.
Off site links are not very useful. I don't know why you won't make an MCVE here.
This would appear to be a debugger defect.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.