Pracuję nad wdrożeniem programu, a baza kodów jest mieszanką C++/CLI i C#. C++/CLI ma wszystkie smaki: natywny, mieszany (/clr
) i bezpieczny (/clr:safe
). W moim środowisku programistycznym tworzę bibliotekę DLL ze wszystkimi kodami C++/CLI i odwołuję się do tego z kodu C# (EXE). Ta metoda działa bezbłędnie.Tryb mieszany Uszkodzenie C++/CLI: uszkodzenie sterty w ateksycie (statyczna rejestracja destruktora)
Dla moich wydań chcę wydać pojedynczy plik wykonywalny (po prostu stwierdzenie, że "dlaczego nie ma tylko DLL i EXE oddzielne?" Jest nie do przyjęcia).
Do tej pory udało mi się skompilować plik EXE z różnych źródeł. Jednak po uruchomieniu programu pojawia się okno dialogowe "XXXX przestało działać" z opcjami Sprawdzanie online, zamykanie i debugowanie. Dane problemowe przedstawiają się następująco:
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
Jeśli debugować i wysłać go do Visual Studio, zgłasza:
Unhandled exception at 0x77d2dc9b in XXX.exe: A heap has been corrupted
Wybór wyników przerwa w to zatrzymując się Ntdll.dll 77d2dc9b() bez Dodatkowe informacje. Jeśli poproszę Visual Studio, aby kontynuował, program uruchamia się dobrze i wygląda na to, że działa bez żadnego incydentu, prawdopodobnie dlatego, że debugger jest teraz dołączony.
Co o tym sądzisz? Jak uniknąć uszkodzenia sterty? Program wydaje się działać dobrze z wyjątkiem tego.
My skrócona skrypt kompilacji jest następujący (mam pominąć mój sprawdzanie błędów dla zwięzłość):
@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
Aktualizacja 1
Po kompilacji to z symboli debugowania i próbuje ponownie, W rzeczywistości dostaję więcej informacji. Stos wywołań wynosi:
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]
...
Linia mojego kodu, że 'przyczyny' to (dynamiczny inicjator dla Bytes::Null
) wynosi:
Bytes Bytes::Null;
W nagłówku, który jest zadeklarowany jako:
class Bytes { public: static Bytes Null; }
Próbowałem również wykonać globalne rozszerzenie w nagłówku, tak:
extern Bytes Null; // header
Bytes Null; // cpp file
Co nie powiodło się w ten sam sposób.
Wygląda na to, że funkcja CRT atexit
jest odpowiedzialna, ponieważ jest niezamierzona z powodu statycznego inicjalizatora.
Fix
Jako Ben Voigt podkreślił użycia jakichkolwiek funkcji CRT (w tym rodzimych inicjalizatorów statycznych) wymaga odpowiedniego inicjalizacji CRT (co zdarza się w mainCRTStartup
, WinMainCRTStartup
lub _DllMainCRTStartup
).Dodałem mieszany C plik ++/CLI, który ma C++ main
lub 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);
}
Po wykonaniu tej czynności, program pobiera teraz trochę dalej, ale nadal ma problemy (które zostaną uwzględnione w innym miejscu):
- Gdy program jest wyłącznie w języku C# to działa dobrze, wraz z, gdy jest to po prostu wywołanie C++ metod/CLI, coraz C++/właściwości CLI oraz tworzenie managed C++/CLI obiektów
- zdarzenia dodane przez C# w C++/Kod CLI nigdy nie uruchamia się (nawet jeśli powinien)
- Jeden inny błąd dziwne jest to, że wyjątek dzieje jest InvalidCastException mówiąc nie można rzutować od X do X (gdzie X jest taki sam jak X ...)
Jednak ponieważ uszkodzenie stosu jest stała (przez po zainicjowaniu CRT) pytanie jest gotowe.
@Foole Dziękujemy za poprawienie tytułu. – coderforlife