将字符串从 .NET 传递到本机(64 位)

Passing string from .NET to native (64-bit)

我在将简单字符串从我的 .NET 应用程序(编译为 64 位)传递到我的本机 DLL(也编译为 64 位)时遇到问题。

C++ 签名:

DllExport void SetFoo(LPWSTR foo);

C# 签名:

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

然后我 运行 在 C# 中编写以下代码:

X.SetFoo("some string");

当它到达DLL中的调试器时,值在中文骂我:Ⴐ虘翺

当我将 .NET 和本机代码都更改为 32 位时,我在调试器中获得的值是正确的字符串。我哪里傻了?

最小复制为 Visual Studio 2015 解决方案:download here

重现:

Foo.h:

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

Foo.cpp:

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

DllExport void SetFoo(LPWSTR foo) 
{

}

Form1.cs:

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl,  CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);

当您将 ANSI 编码的拉丁文本解释为 UTF-16 时,您会看到汉字。这显然是正在发生的事情。因此,您的 C# 代码正在以某种方式发送 ANSI 编码的文本。

p/invoke最好这样写:

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

不需要[in],默认即可。 MarshalAs 毫无意义,因为您指定了 CharSet.Unicode。但是,这两项更改都不会影响语义。

对您在问题中描述的内容的唯一合理解释是:

  • 实际代码与您描述的不一样,或者
  • 调试器有缺陷。

我建议你把非托管函数改成

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

如果消息框显示正确的文本,那么结论似乎是调试器有缺陷。