来自C#的mingw DLL:为什么我必须覆盖new / delete?

时间:2018-04-23 14:25:00

标签: c# c++ windows dll mingw

我正试图在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的情况下解决此问题。

以下是重现错误的最小示例,包括解决方法(如果定义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位的所有内容也解决了问题(这对我来说再也没有选择)

1 个答案:

答案 0 :(得分:1)

TL; DR

必须混合编译器之间的分配/解除分配!

您遇到的问题非常棘手,实际上您的程序每次都会崩溃,无论是否有void* operator new[](size_t){...}定义。

如果您调试程序,它实际上应该在删除test变量时崩溃。此变量是使用mingw的新运算符创建的,但使用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#c​​ode:

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

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

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

但是,如果使用Visual Studio编译它,则(dll和exe)都使用相同的运行时,因此分配/取消分配在同一个内存空间管理器中完成。 但是仍然是UB,你会遇到问题!例如:如果您使用msvc10创建库并希望将此库与msvc14一起使用,则可以在此处执行相同的操作!我记得一些问题,代码来自一个内存也被管理错误的bug;我们使用了用msvc11创建的库,但我们的代码是用msvc12编译的......