3

モジュールは事前に知られていないため、実行時に動的に dll としてロードする必要があります。モジュールはクラス インターフェイスに準拠しているというだけです。私が気付いたのは、(メイン スレッドのメイン プログラムで) dll によってスローされた例外をキャッチした後、適切なデストラクタが呼び出され、モジュールが破棄され、dll がアンロードされた後、catch ブロックの最後の } として行ごとにステップ実行するときにVisual Studio C++デバッガーが到達すると、別の例外が発生し、プログラムがクラッシュします

xxxxx.exe の 0x68ad2377 (msvcr90d.dll) での初回例外: 0xC0000005: アクセス違反の読み取り場所 0x02958f14。

例外で中断を有効にすると、この 2 番目の例外で中断すると、場所が次のように表示されます。

msvcr90d.dll!__DestructExceptionObject(EHExceptionRecord * pExcept=0x0017ee4c, unsigned char fThrowNotAllowed=0) 行 1803 + 0xf バイト

フレームスタックが壊れている可能性があります。この例外がスローされる理由がわかりません。

私のコード構造の簡略化されたバージョンは次のとおりです。

プログラムの非常に単純化された構造:

//shared header:
class Module
{
public:
    virtual void Foo(void) = 0;
};


//dll:
class SomeSpecificModule : public Module
{
public:
    virtual void Foo(void);
};

void SomeSpecificModule::Foo(void)
{
    throw 1;
}

extern "C" __declspec(dllexport) Module* GetModule()
{
    return new SomeSpecificModule;
}


//program:
typedef ptrGetModule* (*GetModule)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    ptrGetModule GetModule = (ptrGetModule)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}
4

7 に答える 7

4

覚えておくべきことは、C ランタイム ライブラリの各コピーには独自の状態があるということです。SomeSpecificModule.dll が C ランタイム ライブラリに静的にリンクしている場合、この種の問題が発生する可能性があります。その場合は、C ランタイム ライブラリの DLL 版とリンクしてみてください。また、メイン モジュールとまったく同じ方法で SomeSpecificModule.dll がコンパイルおよびリンクされていることを確認する必要があります。

あなたはDLLがアンロードされ、正しいデストラクタが呼び出されたと述べました.実際のプログラムは、投稿したサンプルよりも多くのことが行われているように聞こえました. try ブロックで SomeSpecificModule.dll をアンロードした場合は、SomeSpecificModule::Foo() の例外レコードをアンロードしたことになります。m でクラッシュしたのはそのためだと思います。svcr90d.dll!__DestructExceptionObject(EHExceptionRecord * ...

ただし、一般に、DLL の境界を越えて例外をスローすると、問題が発生します。非PODオブジェクトをスローしている場合、異なるヒープ、異なるコンパイラ設定、STLバージョンで異なるCランタイムライブラリによって割り当てられたメモリで問題が発生する可能性があります...要点がわかります。

DLL の境界を越えないようにコードを変更します。ある日、チームの誰かがコンパイラの設定を変更したり、サードパーティのヘッダー #define が変更されたりして、プログラムがクラッシュし始めた場合、根本原因を突き止めるのに非常に苦労することになります。

とにかく、実際のコードを見ずに、何がうまくいかないのかを推測しようとしています. それが役に立てば幸い。

于 2009-06-09T05:25:48.783 に答える
3

DLL が例外をスローしたときに呼び出す必要があるスタック アンワインド コードの多くは、DLL 内にあります。DLL をアンロードした場合、そのコードはどのように呼び出されるのでしょうか?

動的にリンクされたモジュールの境界を越えて例外をスローしないでください。

于 2009-06-09T16:39:27.557 に答える
1

実際のコードで値によって例外を処理していますか? この場合、コピーされた例外オブジェクトのデストラクタで、catch ブロックの最後に例外が発生する可能性があります。

于 2009-06-09T03:38:16.427 に答える
1

このコードでは、DLL がアンロードされている場所がわかりません (あなたが言うように)。関連するコードを投稿していただけますか?

DLLにはオブジェクトの破棄、スタックの巻き戻しなどに必要なコードが含まれており、DLLがアンロードされる時点で投稿した内容から明らかではないため、DLLのアンロードが重要になる場合があります。

于 2009-06-09T04:08:25.313 に答える
0

プロジェクトの設定を確認してください。アプリケーションがマルチスレッドの場合は、マルチスレッド DLL にリンクする必要があります。

于 2009-06-09T04:54:43.033 に答える
0

これは暗闇でのショットかもしれませんが、チェックする価値があります。

msvcr90d.dll にエラーが表示されるため、アプリケーションは DEBUG でコンパイルされているようです。使用している dll も DEBUG でコンパイルされていますか? msvcr90.dll を使用してメモリを作成し、msvcr90d.dll を使用してメモリを解放する、またはその逆は、dll を使用する場合によくある問題です。

あなたの関数ポインタ typedef は少し怪しいと思います。私は次のように書きます:

typedef Module* (*moduleFnType)();

int main(void)
{
    HANDLE hMod = LoadLibrary("SomeSpecificModule.dll");
    moduleFnType GetModule = (moduleFnType)GetProcAddress(hMod, "GetModule");
    try
    {
        Module *d = GetModule();
        d->Foo();
    }
    catch (...)
    {
        cout << '!' << endl;
    }
    return 0;
}

あなたの typedef は、関数 GetModule の戻り値の型について何も述べていません。

于 2009-06-09T07:21:34.717 に答える
0

Canopus: 例外として int をスローすると、同じことが起こります。

TK___: すべてのプロジェクトでマルチスレッド dll にリンクしています。

Assaf と Shing Yip: dll は実際に FreeLibrary() によってラッパーのデストラクタでアンロードされます。これは、ラッパー オブジェクトが tr1::shared_ptr のベクトルにプッシュされるためです (ラッパー自体はリソース ホルダーとしてコピーできないため)。 STL ベクトルに入れることはできません) try{} スコープにのみ存在します。それは正しいことのように思えたので、エラー状況が発生したときに DLL のアンロードを含むクリーンアップを確実に行うことができ、RAII スタイルの設計を好む傾向があります。これが問題の原因である場合、どのような設計を使用すれば正しく動作し、ソフトウェア エンジニアリングの観点から見栄えがよくなるのだろうかと考えています。ただし、これが問題ではないのではないかと思われるのは、例外がスローされたときに発生するデストラクタ呼び出しをステップ実行すると、FreeLibrary() がエラーなしで実行されることです。

Magnus Skog: リリース モードでも、例外をキャッチして通常どおり実行を継続するのではなく、クラッシュが発生します。動的メモリは、1) 場合によっては operator new、2) tr1::shared_ptr、および 3) アラインメントが必要な場合は _mm_malloc/_mm_free で処理されます。

于 2009-06-09T16:32:28.327 に答える