24

私は Windows プログラミングに不慣れで、誰もが認識しているように思われるバグを探して 2 時間を「失いました」: DLL でヒープ上にオブジェクトを作成し、別の DLL (またはメイン プログラム) でそれを破棄することはできません。 .

Linux/Unix では、これが当てはまらないことはほぼ確実です (そうである場合は、それを教えてください。ただし、何千回も問題なく実行したと確信しています...)。

この時点で、いくつか質問があります。

1) 静的にリンクされた DLL は、メイン プログラムとは異なるヒープを使用しますか?

2) 静的にリンクされた DLL は、メイン プログラムの同じプロセス空間にマップされていますか? (ここでの答えは大きな YES であると確信しています。そうでなければ、メイン プログラムの関数から DLL の関数にポインタを渡す意味がありません)。

COM/ATL サービスではなく、プレーン/通常の DLL について話している

編集:「静的にリンクされた」とは、LoadLibrary を使用して DLL をロードしないが、スタブ ライブラリとリンクすることを意味します。

4

3 に答える 3

23

DLL / exeは、Cランタイムライブラリの実装にリンクする必要があります。

C Windowsランタイムライブラリの場合、以下にリンクする場合は、指定するオプションがあります。

  1. シングルスレッドCランタイムライブラリ(シングルスレッドライブラリのサポートは終了しました)
  2. マルチスレッドDLL/マルチスレッドデバッグDLL
  3. 静的ランタイムライブラリ。
  4. もう少し(リンクを確認できます)

それぞれが異なるヒープを参照するため、あるランタイムライブラリのヒープから取得したアドレスを別のヒープに渡すことはできません。

さて、それはあなたが話しているDLLがどのCランタイムライブラリにリンクされているかに依存します。たとえば、使用しているDLLが静的Cランタイムライブラリにリンクされており、アプリケーションコード(メイン関数を含む)がマルチスレッドCランタイムDLLにリンクされているとすると、ポインタをメモリに割り当てられたメモリに渡すと、メインプログラムにDLLを作成し、そこで解放しようとすると、その逆の場合、未定義の動作が発生する可能性があります。したがって、基本的な根本原因はCランタイムライブラリです。慎重に選んでください。

ここここでサポートされているCランタイムライブラリの詳細をご覧ください。

MSDNからの引用:

注意 ランタイムライブラリの静的バージョンと動的バージョンを混在させないでください。1つのコピーの静的データが他のコピーと共有されないため、プロセスにランタイムライブラリの複数のコピーがあると問題が発生する可能性があります。リンカを使用すると、1つの.exeファイル内で静的バージョンと動的バージョンの両方にリンクできなくなりますが、ランタイムライブラリのコピーが2つ(またはそれ以上)になる可能性があります。たとえば、ランタイムライブラリの静的(非DLL)バージョンにリンクされたダイナミックリンクライブラリは、ランタイムライブラリの動的(DLL)バージョンにリンクされた.exeファイルで使用すると問題を引き起こす可能性があります。(1つのプロセスでライブラリのデバッグバージョンと非デバッグバージョンを混在させないようにする必要もあります。)

于 2012-05-30T16:15:29.970 に答える
9

まず、アプリケーション/DLL に関する Windows OS のヒープ割り当てとスタックを理解しましょう。従来、オペレーティング システムとランタイム ライブラリには、ヒープの実装が付属しています。

  1. プロセスの開始時に、OS はプロセス ヒープと呼ばれる既定のヒープを作成します。Process ヒープは、他のヒープが使用されていない場合、ブロックの割り当てに使用されます。
  2. 言語ランタイムは、プロセス内に個別のヒープを作成することもできます。(たとえば、C ランタイムは独自のヒープを作成します。)
  3. これらの専用ヒープに加えて、アプリケーション プログラムまたはロードされた多くのダイナミック リンク ライブラリ (DLL) の 1 つが、プライベート ヒープと呼ばれる個別のヒープを作成して使用する場合があります。
  4. これらのヒープは、すべての仮想メモリ システムで、オペレーティング システムの仮想メモリ マネージャーの上にあります。
  5. CRT と関連するヒープについて詳しく説明しましょう。
    1. C/C++ ランタイム (CRT) アロケーター: malloc() と free()、および new と delete 演算子を提供します。
    2. CRT は、初期化の一部として、すべての割り当てに対してこのような追加のヒープを作成します (この CRT ヒープのハンドルは、CRT ライブラリの _crtheap というグローバル変数に内部的に格納されます)。
    3. CRT は、Windows ヒープの上に存在する独自のプライベート ヒープを作成します。
    4. Windows ヒープは、Windows ランタイム アロケータ (NTDLL) を囲む薄い層です。
    5. Windows ランタイム アロケーターは、OS によって使用されるページを予約およびコミットする仮想メモリ アロケーターと対話します。

DLL と exe は、マルチスレッドの静的 CRT ライブラリにリンクしています。作成した各 DLL と exe には、_crtheap などの独自のヒープがあります。割り当てと割り当て解除は、それぞれのヒープから行う必要があります。DLL から動的に割り当てられ、実行可能ファイルから割り当てを解除することはできず、その逆も同様です。

何ができる?/MD または /MDd を使用して DLL および exe でコードをコンパイルし、ランタイム ライブラリのマルチスレッド固有および DLL 固有のバージョンを使用します。したがって、DLL と exe の両方が同じ C ランタイム ライブラリにリンクされているため、1 つの _crtheap にリンクされています。割り当ては、1 つのモジュール内で常に割り当て解除と対になっています。

于 2014-04-04T09:41:33.273 に答える
4

.exe としてコンパイルされるアプリケーションがあり、ライブラリを使用したい場合、.lib ファイルからそのライブラリを静的にリンクするか、.dll ファイルからそのライブラリを動的にリンクできます。

リンクされた各モジュール (つまり、各 .exe または .dll) は、C または C++ ランタイムの実装にリンクされます。ランタイム自体は、静的または動的にリンクできるライブラリであり、さまざまなスレッド構成で提供されます。

静的にリンクされた dll と言うのは、アプリケーション .exe がライブラリ .dll に動的にリンクし、そのライブラリが内部的にランタイムに静的にリンクするセットアップを説明しているのでしょうか? これがあなたの言いたいことだと思います。

また、すべてのモジュール (.exe または .dll) には独自の静的スコープがあることにも注意してください。つまり、.exe 内のグローバル静的は、.dll 内の同じ名前のグローバル静的と同じインスタンスにはなりません。

したがって、一般的なケースでは、異なるモジュール内で実行されているコード行がランタイムの同じ実装を使用しているとは想定できません。さらに、それらは静的状態の同じインスタンスを使用していません。

したがって、モジュールの境界をまたぐオブジェクトまたはポインターを扱うときは、特定の規則に従う必要があります。割り当てと割り当て解除は、任意のアドレスに対して同じモジュールで発生する必要があります。そうしないと、ヒープが一致せず、動作が定義されません。

COM はこれを参照カウントを使用して解決します。参照カウントがゼロになると、オブジェクトは自身を削除します。これは、一致した場所の問題を解決するために使用される一般的なパターンです。

他の問題が存在する可能性があります。たとえば、ウィンドウは特定のアクションを定義します。たとえば、割り当ての失敗をモジュールごとではなくスレッドごとに処理する方法などです。これは、モジュール B によってセットアップされたスレッドのモジュール A で実行されているコードも、予期しない動作に陥る可能性があることを意味します。

于 2012-05-30T16:37:27.580 に答える