2011-02-08 19 views
5

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.

+0

@Foole Dziękujemy za poprawienie tytułu. – coderforlife

Odpowiedz

5

EDYCJA: Zauważono problem, pozostawiając sugerowane kroki debugowania poniżej, na wypadek gdyby pomogły one komukolwiek w przyszłości.

Problem polega na tym, że zmieniłeś punkt wejścia. Powinieneś używać punktu wejściowego standardowej biblioteki C++/CLI, który ustawia zasoby wewnętrzne, takie jak lista onexit.

Usuń przełącznik /ENTRY i napisz prostą funkcję main, która wywołuje żądaną procedurę uruchamiania.


Chociaż przy użyciu oddzielnego EXE i DLL może nie być do zaakceptowania dla produktu końcowego, dobrze byłoby, aby przetestować tę prostszą konfigurację i sprawdzić, czy masz ten sam problem.

Jeśli możesz odtworzyć uszkodzenie sterty przy pomocy oddzielnego pliku .DLL, wiesz, że jest on gdzieś w twoim natywnym kodzie C++ i będzie dużo łatwiej debugować bez C# zmieszanego z tym samym plikiem.

Jeśli nie możesz odtworzyć problemu z oddzielnymi plikami DLL i EXE, może to być związane z procesem integracji (lub może być mniej oczywiste, ponieważ układ zmienia się w zależności od tego, co zostanie połączone).

Po znalezieniu i zmiażdżeniu błędu korupcji sterty, można wrócić do pojedynczego pliku .EXE.

Innym podejściem byłoby zbudowanie bazy danych debugowania, aby umożliwić lepsze śledzenie stosów podczas awarii. Nawet kompilacje wydań (a może przede wszystkim wydania wydań) powinny być budowane z informacjami debugowania.

+0

Testowałem wszystko z systemem DLL/EXE (w Visual Studio). Kompilowałem z PDB dla kodu C, jednak nie uwzględniłem go podczas etapu łączenia. Opublikowalem wyniki uzywajac powyzej pelnych informacji debugowania. Jakie są Twoje myśli? – coderforlife

+0

@thaimin: Myślę, że widzę problem. Zastąpienie funkcji punktu wejścia CRT jest nie-no, jeśli zamierzasz używać również standardowych funkcji bibliotecznych, w tym 'atexit'. –

+0

Nie użyłem celowo atexit! (chociaż są inne funkcje CRT używane później). Ta poprawka powoduje pojawianie się nowych problemów! Zobacz edytowane pytanie. – coderforlife