来自C#的mingw DLL:为什么我必须覆盖新建/删除?

mingw DLL from C#: why do I have to override new/delete?

本文关键字:覆盖 新建 删除 mingw DLL 为什么 来自      更新时间:2023-10-16

我正在尝试在Windows 10上从C#调用最小的C函数。我使用 mingw/g++ 将 C 代码编译成.dll

事实证明,我必须使用Visual Studio定义opterator new[]或编译.dll。否则,我的 C# 程序崩溃并显示以下错误:

The program '[14740] Test.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.

我真的很想了解这里到底发生了什么,以及如何在不覆盖所有新/删除运算符但仍使用 mingw 的情况下解决此问题。

以下是重现错误的最小示例,包括解决方法(如果定义了AddNewOperatoroperator new[]将定义,并且生成的.dll将正常工作):

Test.cs(使用 Visual Studio 2017 编译/运行):

using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("libTest", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
public static extern int TestFunction();
static void Main(string[] args)
{
Console.WriteLine("!!" + TestFunction());
}
}

测试.cpp用 mingw 编译(见下文):

#include <new>
#include <cstdlib>
#ifdef AddNewOperator // This will fix the issue
void* operator new[](std::size_t sz){
return std::malloc(sz);
}
#end
extern "C" {
int __stdcall __declspec(dllexport) TestFunction() {
int* test = new int[3]; // removing this line will make everything work when building
return test[2];
}

下面是构建脚本:

# Remove the following # and the compiled dll will work just fine
g++ -g -s -Wall -c -fmessage-length=0 Test.cpp  #-DAddNewOperator
g++ -g -shared -o libTest.dll *.o -Wl,--subsystem,windows

编辑:为 x86 而不是 64 位编译所有内容也可以解决问题(这对我来说又没有选择)

TL;博士

您不能在编译器之间混合分配/取消分配!

您面临的问题非常棘手,实际上您的程序每次都应该崩溃,无论是否使用void* operator new[](size_t){...}定义。

如果调试程序,它实际上应该在删除test变量时崩溃。此变量是使用 mingw 的新运算符创建的,但使用 MSVC 删除运算符删除,并且它们不可互操作。所以你必须使用mingw的delete函数。

对于一个简单的测试,您可以只做:

C++ 代码:

int* test = nullptr;
int __stdcall __declspec(dllexport) TestFunction() {
test = new int[3]; // note test is global
return test[2];
}
void __stdcall _declspec(dllexport) CleanUp() {
delete[] test;
}

C# 代码:

public static extern int TestFunction();
public static extern int CleanUp();
static void Main(string[] args)
{
Console.WriteLine("!!" + TestFunction());
CleanUp();
}

如果重新定义新运算符,为什么程序不会崩溃?!

我实际上不确定,但我认为,mingw 的 malloc 实现使用传统的 C 运行时,该运行时使用 HeapAlloc 进行分配,使用 HeapFree 删除test变量。简而言之,您只是幸运/不幸,当您自定义定义operator new并在内部使用malloc时,它不会崩溃......

但是,如果使用 Visual Studio 编译它,则(dll 和 exe)使用相同的运行时,因此分配/释放是在同一内存空间管理器内完成的。但它仍然是UB,你会遇到问题!例如:如果您使用 msvc10 创建库并希望将此库与 msvc14 一起使用,则这里也会发生同样的情况!我记得代码的一些问题来自内存管理错误的错误;我们使用了使用 msvc11 创建的库,但我们的代码是使用 msvc12 编译的......