5

私はプログラムのデプロイに取り組んでおり、コードベースはC ++ / CLIとC#の混合物です。/clrC ++ / CLIには、ネイティブ、混合( )、セーフ( )のすべての種類があります/clr:safe。私の開発環境では、すべてのC ++ / CLIコードのDLLを作成し、それをC#コード(EXE)から参照します。この方法は問題なく機能します。

単一の実行可能ファイルをリリースしたい私のリリースの場合(単に「DLLとEXEを別々にしないのはなぜですか?」というだけでは受け入れられません)。

これまでのところ、すべての異なるソースを使用してEXEをコンパイルすることに成功しました。ただし、実行すると、[オンラインで確認]、[閉じる]、[デバッグ]のオプションが表示された[XXXXが動作を停止しました]ダイアログが表示されます。問題の詳細は次のとおりです。

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

デバッグしてVisualStudioに送信すると、次のように報告されます。

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

breakを選択すると、追加情報なしでntdll.dll!77d2dc9b()で停止します。Visual Studioに続行するように指示すると、プログラムは正常に起動し、問題なく動作しているように見えます。これは、おそらくデバッガーが接続されているためです。

これをどう思いますか?このヒープの破損を回避するにはどうすればよいですか?プログラムはこれを除いて正常に動作しているようです。

私の要約されたコンパイルスクリプトは次のとおりです(簡潔さのためにエラーチェックを省略しました):

@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

アップデート1

これをデバッグシンボルでコンパイルして再試行すると、実際にはより多くの情報が得られます。コールスタックは次のとおりです。

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

これ(の動的初期化子)を'引き起こす'私のコードの行Bytes::Nullは次のとおりです。

Bytes Bytes::Null;

次のように宣言されているヘッダー内:

class Bytes { public: static Bytes Null; }

また、次のようにヘッダーでグローバル外部変数を実行してみました。

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

同じように失敗しました。

atexit静的初期化子のために不注意に必要とされているCRT関数が原因であるようです。


修理

Ben Voigtが指摘したように、CRT関数(ネイティブの静的初期化子を含む)を使用するには、CRTの適切な初期化が必要です(これは、、、またはで発生しmainCRTStartupますWinMainCRTStartup_DllMainCRTStartupmainC++またはWinMain:を含む混合C ++/CLIファイルを追加しました。

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);
}

これを行った後、プログラムはもう少し進んでいますが、まだ問題があります(これは他の場所で対処されます):

  • プログラムがC#のみの場合は、C ++ / CLIメソッドを呼び出したり、C ++ / CLIプロパティを取得したり、マネージC ++/CLIオブジェクトを作成したりするだけで問題なく動作します。
  • C#によってC ++ / CLIコードに追加されたイベントは、発生することはありません(発生するはずですが)
  • もう1つの奇妙なエラーは、例外が発生することです。これは、XからXにキャストできないというInvalidCastExceptionです(XはXと同じです...)

ただし、ヒープの破損は(CRTを初期化することで)修正されるため、質問は行われます。

4

1 に答える 1

5

編集:問題を発見し、将来誰かを助ける場合に備えて、以下の推奨デバッグ手順を残します。

問題は、エントリポイントを変更したことです。onexitリストなどの内部リソースを設定するC++/CLI標準ライブラリが提供するエントリポイントを使用する必要があります。

/ENTRYスイッチを取り外し、main目的のスタートアップルーチンを呼び出す簡単な関数を作成します。


個別のEXEとDLLを使用することは最終製品では受け入れられない場合がありますが、この単純な構成をテストして、同じ問題が発生するかどうかを確認することをお勧めします。

別の.DLLを使用してヒープの破損を再現できる場合は、それがネイティブC ++コードのどこかにあることがわかり、同じファイルにC#を混在させなくてもデバッグがはるかに簡単になります。

DLLとEXEを別々に使用して問題を再現できない場合は、統合プロセスに関連している可能性があります(または、リンクされる内容に応じてレイアウトが変わるため、問題がわかりにくくなる可能性があります)。

ヒープ破損のバグを見つけて潰した後、単一の.EXEに戻ることができます。

もう1つのアプローチは、デバッグデータベースを構築して、クラッシュしたときにスタックトレースを改善できるようにすることです。リリースビルド(または特にリリースビルド)でさえ、デバッグ情報を使用してビルドする必要があります。

于 2011-02-08T03:07:22.170 に答える