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

C# 将字符串从.NET传递到本机(64位),c#,c++,64-bit,pinvoke,C#,C++,64 Bit,Pinvoke,我在将一个简单字符串从.NET应用程序(编译为64位)传递到本机DLL(也编译为64位)时遇到问题 C++签名: DllExport void SetFoo(LPWSTR foo); [DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] internal static extern void SetFoo( [In][MarshalAs(Unmanage

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

C++签名:

DllExport void SetFoo(LPWSTR foo);
[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
     [In][MarshalAs(UnmanagedType.LPWStr)] string foo);
C#签名:

DllExport void SetFoo(LPWSTR foo);
[DllImport(Library, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
internal static extern void SetFoo(
     [In][MarshalAs(UnmanagedType.LPWStr)] string foo);
然后我在C#中运行以下代码:

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

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

作为Visual Studio 2015解决方案的最小复制:

复制:

  • 使用WinForms项目创建新的Visual Studio解决方案
  • 将类型为DLL的Win32项目添加到解决方案中
  • 添加以下文件:
Foo.h:

extern "C" {
    __declspec( dllexport ) void SetFoo(LPWSTR);
}
Foo.cpp:

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

DllExport void SetFoo(LPWSTR foo) 
{

}
  • SetFoo
  • 向winforms窗体添加一个按钮
  • 双击它,然后调用
    SetFoo(“abc123”)
  • 执行
    SetFoo
表格1.cs:

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.Cdecl,  CharSet = CharSet.Unicode)]
public static extern void SetFoo(string text);
  • 打开Configuration Manager,将应用程序更改为以64位模式构建
  • 将Win32Project1设置为生成为x64
  • 将WindowsFormApplication1设置为生成为x64,通过拾取平台新建,拾取x64,确定
  • 更改WindowsFormsApplication1的输出目录以匹配其他应用的输出目录
  • 无需调试即可启动
  • 通过将
    Attach to
    设置为
    Managed(v4.5、v4.0)代码、本机代码
    并查找进程,附加调试器(Ctrl+Alt+P)
  • 观察断点处的值

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

p/invoke最好这样写:

[DllImport(Library, CallingConvention = CallingConvention.Cdecl, 
    CharSet = CharSet.Unicode)]
internal static extern void SetFoo(string foo);
不需要
[in]
,它是默认值。由于您指定了
CharSet.Unicode
,所以
MarshalAs
是没有意义的。但是,这两个更改都不会影响语义

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

  • 实际代码与您描述的不同,或者
  • 调试器中有一个缺陷
我建议您将非托管函数更改为

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

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

David,谢谢您的评论,但事实就是这样。我已经复制了一个最小解和。在不调试的情况下运行它,然后在调试器启动后但在您按下按钮之前附加调试器(具有本机支持)。请在问题中包含您的最小复制。非网站链接是不好的。当我在回答中使用代码时,它工作正常。我在问题中添加了一个指向解决方案的链接。有相当多的步骤设置它失败,所以我建议看看解决方案,看到它失败。场外链接不是很有用。我不知道你为什么不在这里创建MCVE。这似乎是调试器的缺陷。