1

MSVS 9 (VS 2008) を使用しています。私のアプリケーションと共有ライブラリ(dll)(アプリケーションとのリンクに使用しています)もc ++環境です。次に、以下のケースを観察します。

  1. 共有ライブラリ/dll がデバッグ モードでビルドされ、アプリケーションもデバッグ モードでビルドされている場合 結果: アプリケーションは正常に実行されました

  2. 共有ライブラリ/dll がリリース モードでビルドされ、アプリケーションもリリース モードでビルドされている場合 結果: アプリケーションは正常に実行されました

  3. 共有ライブラリ/dll がリリース モードでビルドされ、アプリケーションもデバッグ モードでビルドされると、結果: コール スタックからシンボルをロードせずにアプリケーションがクラッシュします。

    コール スタック:

    ntdll.dll!76e94684()
    [以下のフレームは正しくないか、欠落している可能性があります。ntdll.dll のシンボルがロードされていません]

    ntdll.dll!76e7d55f()
    ntdll.dll!76e5fa18()
    ntdll.dll!76e2b3c8()

この問題は、アプリケーションで次の SetName() および GetName() 定義を使用しようとしたときに発生します。

    using namespace std;
    void main()
    { 
        Schema * schemaExp = new Schema();
        schemaExp -> SetName("ExpSchema");
        string srctable;
        srctable=schemaExp->GetName();
        cout <<"\nConnection EXPORT using the target table:" << srctable.c_str()  << endl;
        delete schemaExp;
    }

スキーマ クラスの定義:

    using namespace std;
    class Schema
    {
       public:
       TELAPI_EXPORT void   SetName(char *name); 
       TELAPI_EXPORT string     GetName(); 
      protected: 
       string tableName; 
    };
    void Schema::SetName(char *name)
    { 
       string str(name);
       tableName = str; 
    }
    string Schema::GetName()
    {
      return tableName;
    }

注: 上記は私のアプリケーションの一部であり、私のアプリケーションは #3 でのみクラッシュし、上記の #1 と #2 のケースでは正常に動作しています

この問題を解決するのを手伝ってください。どんな種類の助けも大歓迎です。

前もって感謝します。

4

2 に答える 2

3

共有ライブラリ/dll がリリース モードでビルドされ、アプリケーションもデバッグ モードでビルドされると、結果: コール スタックからシンボルをロードせずにアプリケーションがクラッシュします。

これは、サポートされている構成ではないためです。既定では、デバッグ ターゲットとリリース ターゲットは異なるバージョンの CRT にリンクされます。これらは (特に) メモリの割り当てに異なる戦略を使用し、相互に互換性がありません。

これは、異なるバージョンの CRT にリンクするライブラリを混在させてはならないという、より一般的な規則の単なる拡張です。すべてのプロジェクトが一致する必要があります。そして、すでに見てきたように、そうすると、すべてが正しく機能します。

これには回避策がありますが、正しく行うには多くの作業が必要です。基本的に、すべてのメモリ割り当てが単一の DLL 内で分離されていることを確認して、モジュールの境界を越えるものは何もないようにします。DLL から特定の関数をエクスポートして、メモリを割り当てて解放し、メモリを割り当てるヒープ マネージャがメモリを破棄するヒープ マネージャと同じであることを確認する必要があります。newanddelete演算子を使用する場合、これが当てはまるとは限りません。率直に言って、この場合、これだけの努力をしても何の役にも立たないと思います。

これは、最適化が有効になっているかどうかとは無関係であることに注意してください (最適化はデフォルトでリリース ビルド用であり、デバッグ ビルドにはありません)。その設定は、リンクされている CRT のバージョンと直交しています。たまたま、"Debug" および "Release" ターゲットが複数のオプションを暗示しています。あるプロジェクトの最適化をオンにし、別のプロジェクトの最適化をオフにすることができますが、両方が同じバージョンの CRT にリンクされていることを確認する限り、それは機能するはずです。しかし、繰り返しますが、そうする意味がよくわかりません...一方の最適化を有効にしたいのに、他方の最適化を抑制したいのはなぜですか?

関連:デバッグとリリースのライブラリ/バイナリを混在させる - 悪い習慣?

于 2013-07-10T07:54:22.757 に答える
1

これはクラッシュではありませんが、通常、Windows メモリ マネージャー内でデバッグ ブレークが発生し、プログラムがヒープを破壊していることを警告します。Windows のデバッグ ヒープの機能で、Vista 以降で使用できます。出力ウィンドウでメッセージを確認します。

シンボル サーバーを有効にして、スタック トレースが読みやすく正確になるようにすることも重要です。Tools + Options、Debugging、Symbols でこれを行い、事前定義された msdl.microsoft.com サーバー名の前にあるチェックボックスをオンにします。シンボルの保存に適したスクラッチ ディレクトリを選択します。プログラムを再起動すると、実行を開始する前にしばらくの間、シンボル ファイルのダウンロードが行われます。これは一度だけ発生します。これで、main() メソッドにも戻る非常に読みやすいスタック トレースが得られるはずです。

一般的に、モジュールの境界を越えて C++ クラスを公開するのは難しい作業であることがわかります。ここでの 1 つの障害モードは、スキーマ クラス オブジェクトが DLL 境界の両側で同じサイズでないことです。これは、デバッグ ビルド設定、_HAS_ITERATOR_DEBUGGINGが原因です。#重要事項を定義します。デフォルトでは、デバッグ ビルドではオンになり、リリース ビルドではオフになります。素晴らしいデバッグ機能ですが、それを実装する唯一の方法は、標準の C++ ライブラリ クラスにフィールドを追加することでした。これにより、デバッグ ビルドで std::string が大きくなります。これにより、Scheme クラスが大きくなります。現在、この問題を回避しています。障害モードは、DLL のデバッグ バージョンと EXE のリリース バージョンです。オブジェクトの割り当てが十分に大きくないため、Scheme クラス コンストラクターは文字列を初期化するときにヒープを破損します。

もう 1 つの失敗モードは、プロセスに CRT の 2 つのバージョン (アプリのデバッグ バージョンと DLL のリリース バージョン) があることです。同じヒープを使用して割り当てを行うことはありません。一方を割り当てて他方を解放すると、どちらがうまくいかないでしょう。GetName() メソッドによって返される文字列には、その問題があり、DLL 内の GetName() メソッドで作成され、EXE でのメソッド呼び出し後に破棄されます。間違ったアロケーターによる。これが原因で発生するヒープの破損は、Scheme オブジェクトを削除するなど、ヒープで何かを再度実行するまで検出されません。/MD を使用してコードをビルドしない場合も、この失敗モードが呼び出されます。この問題は VS2012 で解決されましたが、すべての割り当てはデフォルトのプロセス ヒープから行われるようになりました。

モジュールの境界を越えて生き残るには、一貫したビルド設定を使用することが不可欠です。常に一貫して DLL に適切なビルドを使用するように VS ソリューションをセットアップすることは問題ではありません。DLL プロジェクトが EXE プロジェクトと同じソリューションにあることを確認してください。

ただし、将来問題が発生する可能性があることに注意してください。DLL には独自の生活のコツがあり、いつか別のバージョンのコンパイラでビルドされたアプリで使用される可能性があります。それではカブーン。これが起こらないように DLL インターフェイスを設計することは十分に可能ですが、C++ オブジェクトを公開することはあきらめなければなりません。C スタイルのインターフェイスはフォールバックです。COM がこれをオブジェクト モデルに引き上げる方法も良いアプローチです。もちろん、これは非常に厳しいものです。EXE と DLL が常に同時にビルドおよび展開されることを保証できない場合にのみ、これを熟考してください。

于 2013-07-10T09:42:59.797 に答える