Crashing C ++/CLI in modalità mista: corruzione di heap in atexit (registrazione statica del distruttore)

StackOverflow https://stackoverflow.com/questions/4928763

Domanda

Sto lavorando alla distribuzione di un programma e la base di codice è una miscela di C ++/CLI e C#. Il C ++/CLI è disponibile in tutti i sapori: nativo, miscelato (/clr) e sicuro (/clr:safe). Nel mio ambiente di sviluppo creo una DLL di tutto il codice C ++/CLI e mi riferisco dal codice C# (EXE). Questo metodo funziona perfettamente.

Per le mie versioni che voglio rilasciare un singolo eseguibile (semplicemente affermando che "perché non avere solo una DLL ed exe separata?" Non è accettabile).

Finora sono riuscito a compilare l'EXE con tutte le diverse fonti. Tuttavia, quando lo eseguo ottengo il dialogo "XXXX ha smesso di funzionare" con le opzioni per controllare online, chiudere e debug. I dettagli del problema sono i seguenti:

Problem Event Name:       APPCRASH
Fault Module Name:        StackHash_8d25
Fault Module Version:     6.1.7600.16559
Fault Module Timestamp:   4ba9b29c
Exception Code:           c0000374
Exception Offset:         000cdc9b
OS Version:               6.1.7600.2.0.0.256.48
Locale ID:                1033
Additional Information 1: 8d25
Additional Information 2: 8d25552d834e8c143c43cf1d7f83abb8
Additional Information 3: 7450
Additional Information 4: 74509ce510cd821216ce477edd86119c

Se il debug e lo mando a Visual Studio, riporta:

Unhandled exception at 0x77d2dc9b in XXX.exe: A heap has been corrupted

Scegliere i risultati di interruzione in esso fermandosi su ntdll.dll! 77d2dc9b () senza ulteriori informazioni. Se dico a Visual Studio di continuare, il programma si avvia bene e sembra funzionare senza incidenti, probabilmente poiché un debugger è ora allegato.

Cosa ne pensi di questo? Come posso evitare questa corruzione di heap? Il programma sembra funzionare bene tranne questo.

Il mio script di compilazione abbreviato è il seguente (ho omesso il mio errore controllando la brevità):

@set TARGET=x86
@set TARGETX=x86
@set OUT=%TARGETX%
@call "%VS90COMNTOOLS%\..\..\VC\vcvarsall.bat" %TARGET%

@set WIMGAPI=C:\Program Files\Windows AIK\SDKs\WIMGAPI\%TARGET%

set CL=/Zi /nologo /W4 /O2 /GS /EHa /MD /MP /D NDEBUG /D _UNICODE /D UNICODE /D INTEGRATED /Fd%OUT%\ /Fo%OUT%\
set INCLUDE=%WIMGAPI%;%INCLUDE%
set LINK=/nologo /LTCG /CLRIMAGETYPE:IJW /MANIFEST:NO /MACHINE:%TARGETX% /SUBSYSTEM:WINDOWS,6.0 /OPT:REF /OPT:ICF /DEFAULTLIB:msvcmrt.lib
set LIB=%WIMGAPI%;%LIB%
set CSC=/nologo /w:4 /d:INTEGRATED /o+ /target:module

:: Compiling resources omitted

@set CL_NATIVE=/c /FI"stdafx-native.h"
@set CL_MIXED=/c /clr /LN /FI"stdafx-mixed.h"
@set CL_PURE=/c /clr:safe /LN /GL /FI"stdafx-pure.h"

@set NATIVE=...
@set MIXED=...
@set PURE=...

cl %CL_NATIVE% %NATIVE%
cl %CL_MIXED% %MIXED%
cl %CL_PURE% %PURE%
link /LTCG /NOASSEMBLY /DLL /OUT:%OUT%\core.netmodule %OUT%\*.obj

csc %CSC% /addmodule:%OUT%\core.netmodule /out:%OUT%\GUI.netmodule /recurse:*.cs

link /FIXED /ENTRY:GUI.Program.Main /OUT:%OUT%\XXX.exe ^
/ASSEMBLYRESOURCE:%OUT%\core.resources,XXX.resources,PRIVATE /ASSEMBLYRESOURCE:%OUT%\GUI.resources,GUI.resources,PRIVATE ^
/ASSEMBLYMODULE:%OUT%\core.netmodule %OUT%\gui.res %OUT%\*.obj %OUT%\GUI.netmodule

Aggiornamento 1

Dopo aver compilato questo con simboli di debug e riprovando, in effetti ottengo maggiori informazioni. Lo stack di chiamata è:

msvcr90d.dll!_msize_dbg(void * pUserData, int nBlockUse)  Line 1511 + 0x30 bytes
msvcr90d.dll!_dllonexit_nolock(int (void)* func, void (void)* * * pbegin, void (void)* * * pend)  Line 295 + 0xd bytes
msvcr90d.dll!__dllonexit(int (void)* func, void (void)* * * pbegin, void (void)* * * pend)  Line 273 + 0x11 bytes
XXX.exe!_onexit(int (void)* func)  Line 110 + 0x1b bytes
XXX.exe!atexit(void (void)* func)  Line 127 + 0x9 bytes
XXX.exe!`dynamic initializer for 'Bytes::Null''()  Line 7 + 0xa bytes
mscorwks.dll!6cbd1b5c()
[Frames below may be incorrect and/or missing, no symbols loaded for mscorwks.dll]
...

La riga del mio codice che "causa" questo (inizializzatore dinamico per Bytes::Null) è:

Bytes Bytes::Null;

Nell'intestazione che viene dichiarato come:

class Bytes { public: static Bytes Null; }

Ho anche provato a fare un esterno globale nell'intestazione in questo modo:

extern Bytes Null; // header
Bytes Null; // cpp file

Che fallì allo stesso modo.

Sembra che il CRT atexit La funzione è responsabile, essendo inavvertitamente richiesto a causa dell'inizializzatore statico.


Aggiustare

Come ha sottolineato Ben Voigt, l'uso di eventuali funzioni CRT (compresi gli inizializzatori statici nativi) richiede una corretta inizializzazione del CRT (che si verifica in mainCRTStartup, WinMainCRTStartup, o _DllMainCRTStartup). Ho aggiunto un file m misto C ++/CLI che ha un C ++ main o WinMain:

using namespace System;
[STAThread] // required if using an STA COM objects (such as drag-n-drop or file dialogs)
int main() { // or "int __stdcall WinMain(void*, void*, wchar_t**, int)" for GUI applications
    array<String^> ^args_orig = Environment::GetCommandLineArgs();
    int l = args_orig->Length - 1; // required to remove first argument (program name)
    array<String^> ^args = gcnew array<String^>(l);
    if (l > 0) Array::Copy(args_orig, 1, args, 0, l);
    return XXX::CUI::Program::Main(args); // return XXX::GUI::Program::Main(args);
}

Dopo averlo fatto, il programma ora ottiene un po 'oltre, ma ha ancora problemi (che saranno affrontati altrove):

  • Quando il programma è esclusivamente in C# funziona bene, insieme a ogni volta che chiama i metodi C ++/CLI, ottenendo proprietà C ++/CLI e creando oggetti C ++/CLI gestiti
  • Gli eventi aggiunti da C# nel codice C ++/CLI non sparano mai (anche se dovrebbero)
  • Un altro strano errore è che si verifichi un'eccezione è un detto invalidcastException non può essere lanciato da x a x (dove x è uguale a x ...)

Tuttavia, poiché la corruzione del heap è fissa (ottenendo la CRT inizializzata), la domanda viene eseguita.

Nessuna soluzione corretta

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top