.NET 4.0 字符* 到字符串互操作崩溃 x64 0xc0000374



我目前正在将我们公司的 x86 软件迁移到 x64,但遇到了一个障碍。当解决方案平台切换到 x64 时,我们来自非托管 dll 的方法返回char*崩溃并0xc0000374,我想知道为什么。

C++原生代码:

#include "stdafx.h"
#include <windows.h>
#include "windef.h"
#define VERSION "3.3.12"
extern "C"
{
__declspec(dllexport) char* WINAPI GetMyVersion()
{
return (char*)VERSION;
}
}

C# DllImport:

using System;
using System.Runtime.InteropServices;
using System.Security;
namespace InteropTest.Adapter
{
[SuppressUnmanagedCodeSecurity]
public unsafe class MyAdapter
{
private const string DLLName = "VersionNumber";
private const string EntryPointName = "GetMyVersion";
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern IntPtr GetMyVersionPtr();
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern string GetMyVersionString();
}
}

GetMyVersionString()仅适用于 x86 平台,在 x64 平台上崩溃。但是,我能够获取IntPtr并使用Marshal类将其转换为 x86 和 x64 上的托管string,如下所示:

using System.Runtime.InteropServices;
using System.Windows;
using InteropTest.Adapter;
namespace InteropTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var ptr = MyAdapter.GetMyVersionPtr();
var convertedString = Marshal.PtrToStringAnsi(ptr);
// This crashes in x64 build
var directString = MyAdapter.GetMyVersionString();
VersionTextBlock.Text = convertedString;
}
}
}

我还尝试显式指定返回值的MarshalAs属性,但没有成功。如果您没有显式使用Marshal类,则似乎将 64 位char*用作 32 位指针。这只是 .NET 中的错误还是我做错了什么?从我的角度来看,使用Marshal类进行转换应该等效于 使用MarshalAs属性。

编辑:

这是我为复制错误而制作的示例项目: https://github.com/chenhuang444/dotNet40InteropCrash

编辑2:

该程序和退出时代码0xc0000374没有本机调试,并且如果我启用了本机调试,则仅声明"InteropTest.exe触发了断点",堆栈跟踪为:

ntdll.dll!00007ffc0fdc4cfa()    Unknown
ntdll.dll!00007ffc0fdcc806()    Unknown
ntdll.dll!00007ffc0fdccad1()    Unknown
ntdll.dll!00007ffc0fd69a55()    Unknown
ntdll.dll!00007ffc0fd77347()    Unknown
mscorlib.ni.dll!00007ffbd566759e()  Unknown
[Managed to Native Transition]  
>   InteropTest.exe!InteropTest.MainWindow.OnLoaded(object sender, System.Windows.RoutedEventArgs e) Line 23    C#
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised)    Unknown

编辑3:

非托管C++代码是我们解决方案中的一个项目,并设置了 x64/x86 配置,类似于上面链接的示例项目。

好吧,你的版本不是一个"字符串",它确实是一个指向原始内存的指针。如果您告诉它它是一个字符串,.NET 框架将尝试释放此指针。

因此,您有两种选择:

  • 像你一样使用 IntPtr,这很好。
  • 使用 COM 分配器分配 .NET 可以理解的实际字符串,因此在您的情况下,您的C++代码可以替换为以下内容:

.

__declspec(dllexport) char* WINAPI GetMyVersion() {
char* p = (char*)CoTaskMemAlloc(7);
CopyMemory(p, VERSION, 7);
return p;
}

注意:您也可以使用 BSTR。

最新更新