c ++ - mingw DLL fra C #: Hvorfor skal jeg tilsidesætte ny/slette?

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg forsøger at kalde en minimal C-funktion fra C # på Windows 10. Jeg bruger mingw/g ++ til at kompilere C-koden i en .dll


Det viser sig, at jeg skal definere opterator new[] eller kompilere .dll ved hjælp af Visual Studio. Ellers styrter mit C # -program med følgende fejl:


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


Jeg vil virkelig gerne forstå, hvad der sker lige her, og hvordan jeg kan løse dette problem uden at overstyre alle de nye/slette operatører, men stadig bruger mingw.


Her er det minimale eksempel, der gengiver fejlen, herunder en løsning (hvis AddNewOperator er defineret operator new[], vil blive defineret, og den resulterende .dll vil fungere fint):


Test.cs (kompileret/køre med 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());
    }
}


Test.cpp kompileret med mingw (se nedenfor):


#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];
}


Og her er build scriptet:


# 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


 kompilere alt for x86 i stedet for 64 bit løser også problemet (hvilket igen ikke er nogen mulighed for mig)

Bedste reference


TL; DR


Du må ikke blande tildeling/deallokering mellem kompilatorer!



Det problem, du står over for, er ret vanskelig og faktisk skal dit program kollidere hver gang, med eller uden definitionen void* operator new[](size\_t){...}.


Hvis du fejler dit program, skal det faktisk gå i stykker, mens du sletter din test -variabel. Denne variabel er oprettet ved hjælp af mingws nye operatør, men slettet ved hjælp af MSVC sletter operatør, og de er ikke interoperable. Så du skal bruge mingws delete funktion. [16]


For en simpel test kan du bare gøre:


c + + kode:


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 # kode:


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





Hvorfor kolliderer dit program ikke, hvis du omdefinerer den nye operatør?!



Jeg ved faktisk ikke sikkert, men jeg tror, ​​at malloc-implementeringen af ​​mingw bruger arv C runtime, der bruger HeapAlloc til tildeling og HeapFree til at slette din test -variabel. Kort sagt, du er simpelthen heldig/uheldig, det er ikke krasje, når du brugerdefinerede din operator new og brugte malloc indenfor ...


Men hvis du kompilerer det ved hjælp af Visual Studio, bruger både (dll og exe) samme runtime, så allokering/deallokering sker inden for samme hukommelsesplads arrangør. MEN stadig er det UB, og du vil løbe ind i problemer! For eksempel: Hvis du opretter din libary med msvc10 og vil bruge dette bibliotek med msvc14, kan det samme ske her! Jeg kan huske nogle problemer med kode der kommer fra en fejl, hvor hukommelsen også blev forvaltet forkert; Vi brugte et bibliotek oprettet med msvc11, men vores kode blev kompileret med msvc12 ...