来自 C# 的 mingw DLL:为啥我必须覆盖新/删除?
Posted
技术标签:
【中文标题】来自 C# 的 mingw DLL:为啥我必须覆盖新/删除?【英文标题】:mingw DLL from C#: why do I have to override new/delete?来自 C# 的 mingw DLL:为什么我必须覆盖新/删除? 【发布时间】:2018-10-03 15:30:20 【问题描述】:我正在尝试在 Windows 10 上从 C# 调用最小的 C 函数。我使用 mingw/g++ 将 C 代码编译成 .dll
事实证明,我必须定义 opterator new[]
或使用 Visual Studio 编译 .dll。否则我的 C# 程序会因以下错误而崩溃:
The program '[14740] Test.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.
我真的很想了解这里到底发生了什么,以及如何在不覆盖所有新/删除运算符但仍使用 mingw 的情况下解决此问题。
这是重现错误的最小示例,包括解决方法(如果定义了 AddNewOperator
,则将定义 operator 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());
使用mingw编译的Test.cpp(见下文):
#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 位编译所有内容也解决了这个问题(这对我来说再次没有选择)
【问题讨论】:
我的意思是如果我想使用new[]
。因此,如果我想使用new[]
,我要么必须覆盖operator new[]
,要么必须使用Visual Studio 编译.dll。我更改了这句话以进行澄清。
【参考方案1】:
TL;DR
您不得在编译器之间混合分配/释放!
您面临的问题非常棘手,实际上您的程序每次都应该崩溃,不管有没有void* operator new[](size_t)...
定义。
如果您调试您的程序,它实际上应该在删除您的 test
变量时崩溃。这个变量是使用 mingw 的 new 运算符创建的,但是使用 MSVC 删除运算符删除的,它们不是interoperable。所以你必须使用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();
为什么重新定义 new 操作符后程序不会崩溃?!
我实际上并不确定,但我认为,mingw 的 malloc 实现使用 legacy C runtime,它使用 HeapAlloc 进行分配,使用 HeapFree 删除您的 test
变量。简而言之,当您自定义定义 operator new
并在内部使用 malloc
时,您只是幸运/不幸,它没有崩溃......
但是,如果您使用 Visual Studio 编译它,则(dll 和 exe)都使用相同的运行时,因此分配/解除分配是在同一个内存空间管理器中完成的。 但仍然是 UB,你会遇到问题!例如:如果您使用 msvc10 创建您的库并希望将此库与 msvc14 一起使用,那么这里也会发生同样的情况!我记得一些来自内存管理错误的错误的代码问题;我们使用了一个用 msvc11 创建的库,但我们的代码是用 msvc12 编译的...
【讨论】:
我很清楚混合来自不同编译器的 new/delete 是危险的,但是当我只使用 new[] 运算符而根本不使用 delete 时,为什么我的示例代码会崩溃?此外,当我根本没有定义任何 new/delete 运算符时,我希望使用来自同一个编译器的实现,因此不会崩溃...... @kunzej UB 是 UB。如果您的代码尝试执行标准中未涵盖的操作,那么您就无法说明会发生什么。以上是关于来自 C# 的 mingw DLL:为啥我必须覆盖新/删除?的主要内容,如果未能解决你的问题,请参考以下文章