我目前正在将我们公司的 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。