2011-02-08 25 views
5

Tôi đang triển khai một chương trình và codebase là hỗn hợp của C++/CLI và C#. C++/CLI có tất cả các hương vị: bản địa, hỗn hợp (/clr) và an toàn (/clr:safe). Trong môi trường phát triển của tôi, tôi tạo một DLL của tất cả các mã C++/CLI và tham chiếu đến từ mã C# (EXE). Phương pháp này hoạt động hoàn hảo.Lỗi chế độ hỗn hợp C++/CLI: tham nhũng đống trong atexit (đăng ký hủy hoại tĩnh)

Đối với các bản phát hành của tôi mà tôi muốn phát hành một tệp thực thi duy nhất (chỉ cần nói rằng "tại sao không chỉ có một DLL và EXE riêng biệt?" Là không thể chấp nhận được).

Cho đến nay tôi đã thành công trong việc biên dịch EXE với tất cả các nguồn khác nhau. Tuy nhiên, khi tôi chạy nó, tôi nhận được hộp thoại "XXXX đã ngừng hoạt động" với các tùy chọn để Kiểm tra trực tuyến, Đóng và Gỡ lỗi. Các chi tiết vấn đề này như sau:

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 

Nếu tôi gỡ lỗi và gửi cho Visual Studio, nó báo cáo:

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

Chọn kết quả đột phá trong nó dừng lại ở ntdll.dll 77d2dc9b() mà không thông tin thêm. Nếu tôi nói với Visual Studio để tiếp tục, chương trình khởi động tốt và dường như làm việc mà không có sự cố, có lẽ kể từ khi một trình gỡ lỗi được đính kèm.

Bạn làm gì cho điều này? Làm thế nào để tránh tham nhũng heap này? Chương trình này dường như hoạt động tốt ngoại trừ điều này.

kịch bản biên soạn tóm tắt của tôi là như sau (tôi đã bỏ qua lỗi của tôi kiểm tra cho ngắn gọn):

@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 

Cập nhật 1

Sau khi biên dịch này với các biểu tượng gỡ lỗi và cố gắng một lần nữa, Tôi thực sự nhận được nhiều thông tin hơn. Các cuộc gọi stack là:

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] 
... 

các dòng mã của tôi rằng 'gây' này (initializer động cho Bytes::Null) là:

Bytes Bytes::Null; 

Trong tiêu đề đó được khai báo là:

class Bytes { public: static Bytes Null; } 

Tôi cũng đã thử làm một extern toàn cầu trong tiêu đề như vậy:

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

Không thành công trong cùng một cách.

Dường như chức năng CRT atexit có trách nhiệm, vô tình được yêu cầu do bộ khởi tạo tĩnh.


Fix

Như Ben Voigt chỉ ra việc sử dụng bất kỳ chức năng CRT (bao gồm initializers tĩnh bản địa) yêu cầu khởi tạo thích hợp của CRT (mà xảy ra trong mainCRTStartup, WinMainCRTStartup, hoặc _DllMainCRTStartup).Tôi đã thêm một C tập tin ++/CLI hỗn hợp mà có một C++ main hoặc 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); 
} 

Sau khi làm điều này, chương trình bây giờ được xa hơn một chút, nhưng vẫn còn có vấn đề (mà sẽ được giải quyết ở nơi khác):

  • Khi chương trình là hoàn toàn trong C# nó hoạt động tốt, cùng với bất cứ khi nào nó chỉ được gọi ++ phương pháp C/CLI, nhận C++/tài sản CLI, và tạo quản lý C++/CLI đối tượng
  • sự kiện thêm bằng C# vào C++/Mã CLI không bao giờ kích hoạt (mặc dù chúng cần)
  • Một lỗi lạ khác là một ngoại lệ xảy ra là một InvalidCastException nói không thể cast từ X để X (trong đó X là giống như X ...)

Tuy nhiên kể từ khi tham nhũng đống là cố định (bằng cách nhận được CRT khởi tạo) câu hỏi được thực hiện.

+0

@Foole Cảm ơn bạn đã sửa tiêu đề. – coderforlife

Trả lời

5

EDIT: Đã phát hiện sự cố, để lại các bước gỡ lỗi được đề xuất bên dưới trong trường hợp họ trợ giúp bất kỳ ai trong tương lai.

Vấn đề là bạn đã thay đổi điểm vào. Bạn nên sử dụng điểm nhập chuẩn được cung cấp bởi thư viện chuẩn C++/CLI, thiết lập các tài nguyên nội bộ như danh sách onexit.

Xóa khóa /ENTRY và viết hàm main đơn giản gọi là thói quen khởi động mong muốn của bạn.


Mặc dù sử dụng EXE và DLL riêng biệt có thể không được chấp nhận cho sản phẩm cuối, bạn nên kiểm tra cấu hình đơn giản này và xem bạn có gặp vấn đề tương tự không.

Nếu bạn có thể sao chép tham nhũng đống bằng một .DLL riêng biệt, bạn biết nó ở đâu đó trong mã C++ gốc của bạn và sẽ dễ dàng hơn để gỡ lỗi mà không có C# trộn vào cùng một tệp.

Nếu bạn không thể tái tạo vấn đề với DLL và EXE riêng biệt, thì nó có thể liên quan đến quá trình tích hợp (hoặc có thể ít rõ ràng hơn vì bố cục thay đổi tùy thuộc vào những gì được liên kết).

Sau khi bạn tìm và sửa lỗi lỗi tham nhũng, sau đó bạn có thể quay lại đơn .EXE.

Một cách tiếp cận khác là xây dựng cơ sở dữ liệu gỡ lỗi để bạn có thể nhận được dấu vết ngăn xếp tốt hơn khi gặp sự cố. Ngay cả bản phát hành bản phát hành (hoặc có thể đặc biệt là bản phát hành bản dựng) phải được xây dựng với thông tin gỡ lỗi.

+0

Tôi đã thử nghiệm tất cả mọi thứ với một hệ thống DLL/EXE (trong Visual Studio). Tôi đã được biên dịch với một PDB cho mã C, tuy nhiên tôi đã không bao gồm nó trong bước liên kết. Tôi đã đăng kết quả bằng cách sử dụng thông tin gỡ lỗi đầy đủ ở trên. Quan điểm của bạn là gì? – coderforlife

+0

@thaimin: Tôi nghĩ rằng tôi thấy vấn đề. Thay thế hàm điểm nhập CRT là không có không nếu bạn định sử dụng bất kỳ hàm thư viện chuẩn nào, bao gồm 'atexit'. –

+0

Tôi đã không cố tình sử dụng ngoại tuyến! (mặc dù có các chức năng CRT khác được sử dụng sau này). Sửa chữa này gây ra các vấn đề mới để cắt lên! Xem câu hỏi đã chỉnh sửa. – coderforlife

Các vấn đề liên quan