混在アセンブリの初期化Initialization of Mixed Assemblies

中にコードを実行するときに、Windows 開発者のローダー ロックの必ずDllMainします。Windows developers must always be wary of loader lock when running code during DllMain. ただし、C + を扱うときに影響するいくつか追加の考慮事項がある/cli clr 混合モードのアセンブリ。However, there are some additional considerations that come into play when dealing with C++/clr mixed-mode assemblies.

DllMain 内のコードは、CLR にはアクセスできません。Code within DllMain must not access the CLR. つまり、 DllMain は、直接的にも間接的にもマネージ関数を呼び出すことができません。 DllMainではマネージド コードを宣言または実装しないでください。また、 DllMain内では、ガベージ コレクションや自動ライブラリ読み込みは行われません。This means that DllMain should make no calls to managed functions, directly or indirectly; no managed code should be declared or implemented in DllMain; and no garbage collection or automatic library loading should take place within DllMain.

ローダー ロックの原因Causes of Loader Lock

.NET プラットフォームの導入に伴い、実行モジュール (EXE または DLL) を読み込むためのメカニズムが 2 つになりました。1 つは、Windows 向けで、アンマネージ モジュールに使用されます。もう 1 つは、.NET 共通言語ランタイム (CLR: Common Language Runtime) 向けで、.NET アセンブリを読み込みます。With the introduction of the .NET platform there are two distinct mechanisms for loading an execution module (EXE or DLL): one for Windows, which is used for unmanaged modules, and one for the .NET Common Language Runtime (CLR) which loads .NET assemblies. 混在モード DLL 読み込み時の問題は、Microsoft Windows OS ローダーを中心に発生します。The mixed DLL loading problem centers around the Microsoft Windows OS loader.

.NET のコンストラクトだけを含むアセンブリをプロセスに読み込んだ場合、CLR ローダーは、必要なすべての読み込みタスクおよび初期化タスクを自分で実行できます。When an assembly containing only .NET constructs is loaded into a process, the CLR loader can perform all of the necessary loading and initialization tasks itself. しかし、混在アセンブリの場合は、ネイティブ コードやデータが含まれている可能性があるため、Windows ローダーも使用する必要があります。However, for mixed assemblies, because they can contain native code and data, the Windows loader must be used as well.

Windows ローダーは、DLL が初期化されるまで、その DLL 内のコードやデータにコードがアクセスできないようにします。また、DLL が一部しか初期化されていない間は、コードが DLL を重複して読み込むことができないようにします。The Windows loader guarantees that no code can access code or data in that DLL before it has been initialized, and that no code can redundantly load the DLL while it is partially initialized. そのため、Windows ローダーでは、モジュールの初期化中に安全ではないアクセスを防止する、プロセス グローバルのクリティカル セクション (多くの場合 "ローダー ロック" と呼ばれています) が使用されます。To do this, the Windows loader uses a process-global critical section (often called the "loader lock") that prevents unsafe access during module initialization. 結果として、読み込みプロセスでは、典型的な多くのデッドロックのシナリオが発生しやすくなります。As a result, the loading process is vulnerable to many classic deadlock scenarios. 混在アセンブリの場合、次の 2 つのシナリオで、デッドロックの危険性が高くなります。For mixed assemblies, the following two scenarios increase the risk of deadlock:

  • 最初のシナリオでは、 DllMain または静的初期化子などでローダー ロックが保持されている場合に、Microsoft Intermediate Language (MSIL) にコンパイルされた関数を実行しようとすると、デッドロックが発生します。First, if users attempt to execute functions compiled to Microsoft intermediate language (MSIL) when the loader lock is held (from DllMain or in static initializers, for example), this can cause deadlock. ここで、MSIL 関数が、読み込まれていないアセンブリ内の型を参照する場合を考えてみてください。Consider the case in which the MSIL function references a type in an assembly that has not been loaded. CLR は、そのアセンブリを自動的に読み込もうとします。これにより、Windows ローダーはローダー ロックをブロックすることが必要になる場合があります。The CLR will attempt to automatically load that assembly, which may require the Windows loader to block on the loader lock. 呼び出しシーケンスのより早い時点でコードが既にローダー ロックを保持しているため、デッドロックが発生します。Since the loader lock is already held by code earlier in the call sequence, a deadlock results. ただし、ローダー ロック中に MSIL を実行しても、必ずデッドロックが発生するわけではありません。そのために、このシナリオを診断して修復することが困難になります。However, executing MSIL under loader lock does not guarantee that a deadlock will occur, making this scenario difficult to diagnose and fix. 参照されている型の DLL にネイティブ コンストラクトが含まれておらず、そのすべての依存関係にネイティブ コンストラクトが含まれていない場合など、状況によっては、Windows ローダーは参照されている型の .NET アセンブリを読み込む必要がありません。In some circumstances, such as where the DLL of the referenced type contains no native constructs and all of its dependencies contain no native constructs, the Windows loader is not required to load the .NET assembly of the referenced type. さらに、必要なアセンブリやその混在するネイティブまたは .NET の依存関係は、既に他のコードによって読み込まれている可能性があります。Additionally, the required assembly or its mixed native/.NET dependencies may have already been loaded by other code. その結果、デッドロックの発生を予測することが難しくなります。また、デッドロック状態が、対象となるコンピューターの構成によって異なる場合もあります。Consequently, the deadlocking can be difficult to predict, and can vary depending on the configuration of the target machine.

  • 2 番目のシナリオでは、.NET Framework Version 1.0 および 1.1 の DLL を読み込むときに、CLR はローダー ロックが保持されていないと想定し、ローダー ロックの状況下では無効な処理をいくつか実行しました。Second, when loading DLLs in versions 1.0 and 1.1 of the .NET Framework, the CLR assumed that the loader lock was not held and performed several actions that are invalid under loader lock. ローダー ロックが保持されていないという想定は、純粋な .NET DLL に対しては妥当な想定ですが、混在モード DLL はネイティブな初期化ルーチンを実行するため、ネイティブな Windows ローダーが必要となり、その結果、ローダー ロックが発生します。Assuming the loader lock is not held is a valid assumption for purely .NET DLLs, but, because mixed DLLs execute native initialization routines, they require the native Windows loader and therefore the loader lock. したがって、開発者が DLL の初期化中に MSIL 関数を実行しない場合でも、.NET Framework Version 1.0 および 1.1 では、デッドロックが確定的でない場合にデッドロックが発生する可能性がわずかに残っていました。Consequently, even if the developer was not attempting to execute any MSIL functions during DLL initialization, there was still a small possibility of nondeterministic deadlock with versions 1.0 and 1.1 of the .NET Framework.

混在モード DLL の読み込みプロセスで、このような確定的でない場合の問題はなくなりました。All non-determinism has been removed from the mixed DLL loading process. これは、以下の変更によって実現されました。This was accomplished with these changes:

  • CLR が、混在モード DLL の読み込み時に誤った想定を行わなくなりました。The CLR no longer makes false assumptions when loading mixed DLLs.

  • アンマネージド初期化とマネージド初期化が、2 つの独立した段階で実行されます。Unmanaged and managed initialization is performed in two separate and distinct stages. 最初にアンマネージド初期化が DllMain によって実行され、その後、.NET でサポートされている .cctorというコンストラクトによってマネージド初期化が実行されます。Unmanaged initialization takes place first (via DllMain), and managed initialization takes place afterwards, through a .NET-supported construct called a .cctor. 後者は、 /Zl または /NODEFAULTLIB を使用している場合を除いて、ユーザーが意識する必要はありません。The latter is completely transparent to the user unless /Zl or /NODEFAULTLIB are used. 詳細については、「/NODEFAULTLIB (Ignore Libraries) 」と「 /Zl (Omit Default Library Name) 」を参照してください。See/NODEFAULTLIB (Ignore Libraries) and /Zl (Omit Default Library Name) for more information.

ローダー ロックは依然として発生することがありますが、再現性があり、検出されるようになりました。Loader lock can still occur, but now it occurs reproducibly, and is detected. 場合DllMainの MSIL 命令が含まれています。 コンパイラは警告を生成コンパイラの警告 (レベル 1) C4747If DllMain contains MSIL instructions, the compiler generates warning Compiler Warning (level 1) C4747. さらに、ローダー ロックの状況下で MSIL が実行されようとしている場合、CRT または CLR は検出とレポートを試みます。Furthermore, either the CRT or the CLR will try to detect and report attempts to execute MSIL under loader lock. CRT による検出の結果、実行時の診断として C ランタイム エラー R6033 が発生します。CRT detection results in runtime diagnostic C Run-Time Error R6033.

このドキュメントの残りの部分では、MSIL がローダー ロックの状況で実行できる他のシナリオ、各シナリオでの問題に対する解決策、デバッグ技術について説明します。The remainder of this document describes the remaining scenarios for which MSIL can execute under the loader lock, resolutions for the issue under each of those scenarios, and debugging techniques.

シナリオと回避策Scenarios and Workarounds

ローダー ロックが発生している場合でも、ユーザー コードが MSIL を実行できる状況がいくつかあります。There are several different situations under which user code can execute MSIL under loader lock. 開発者は、そのような状況のそれぞれについて、ユーザー コードの実装が MSIL 命令を実行しないようにする必要があります。The developer must ensure that the user code implementation does not attempt to execute MSIL instructions under each of these circumstances. 以下では、最も一般的な事例で問題を解決する方法を考えつつ、すべての可能性について説明します。The following subsections describe all possibilities with a discussion of how to resolve issues in the most common cases.

DllMainDllMain

DllMain 関数は、DLL 用のユーザー定義のエントリ ポイントです。The DllMain function is a user defined entry point for a DLL. ユーザーがそれ以外の関数を指定しない限り、プロセスやスレッドを DLL にアタッチするか、プロセスやスレッドを DLL からデタッチするたびに、 DllMain が呼び出されます。Unless the user specifies otherwise, DllMain is invoked every time a process or thread attaches to or detaches from the containing DLL. この呼び出しは、ローダー ロックが保持されているときに行われる可能性もあるため、ユーザー指定の DllMain 関数は MSIL にコンパイルしないでください。Since this invocation can occur while the loader lock is held, no user-supplied DllMain function should be compiled to MSIL. さらに、コール ツリー内で DllMain をルートにしている関数も、MSIL にコンパイルできません。Furthermore, no function in the call tree rooted at DllMain can be compiled to MSIL. この問題を解決するには、 DllMain が定義されているコード ブロックを、#pragma unmanagedで修飾する必要があります。To resolve issues here, the code block that defines DllMain should be modified with #pragma unmanaged. DllMain によって呼び出されるすべての関数にも、同じ処理を行う必要があります。The same should be done for every function that DllMain calls.

このような関数で、他の呼び出しコンテキスト用に MSIL 実装を必要とする関数を呼び出す必要がある場合には、同じ関数の .NET バージョンとネイティブ バージョンを重複して作成する方法を使用できます。In cases where these functions must call a function that requires an MSIL implementation for other calling contexts, a duplication strategy can be used where both a .NET and a native version of the same function are created.

また、 DllMain が不要な場合またはローダー ロックの状況でこれを実行する必要がない場合は、ユーザー指定の DllMain の実装を削除できます。そうすることで、問題が発生しなくなります。Alternatively, if DllMain is not required or if it does not need to be executed under loader lock, the user-provided DllMain implementation can be removed, which will eliminate the problem.

DllMain で直接 MSIL を実行しようとすると、 Compiler Warning (level 1) C4747 が発生します。If DllMain attempts to execute MSIL directly, Compiler Warning (level 1) C4747 will result. ただし、コンパイラは、DllMain が別のモジュール内の関数を呼び出し、その関数が MSIL を実行するような事例は検出できません。However, the compiler cannot detect cases where DllMain calls a function in another module that in turn attempts to execute MSIL.

このシナリオの詳細については、「診断に対する障害」を参照してください。Please see "Impediments to Diagnosis" for more information on this scenario.

静的オブジェクトの初期化Initializing Static Objects

静的オブジェクトを初期化すると、動的初期化子が必要な場合にデッドロックが発生することがあります。Initializing static objects can result in deadlock if a dynamic initializer is required. コンパイル時に認識される値に静的変数が割り当てられる場合など、単純な事例では動的な初期化が不要なため、デッドロックの危険性はありません。For simple cases, such as when a static variable is simply assigned to a value known at compile time, no dynamic initialization is required, so there is no risk of deadlock. ただし、関数呼び出し、コンストラクター呼び出し、またはコンパイル時に評価できない式によって初期化される静的変数は、モジュールの初期化中にコードを実行する必要があります。However, static variables initialized by function calls, constructor invocations, or expressions that cannot be evaluated at compile time all require code to execute during module initialization.

動的な初期化を必要とする静的初期化子の例 (関数呼び出し、オブジェクト構築、およびポインター初期化) を次のコードに示します。The code below shows examples of static initializers that require dynamic initialization: a function call, object construction, and a pointer initialization. これらの例は静的ではありませんが、グローバル スコープで定義されることが想定されるため、同じ結果になります。(These examples aren't static, but are assumed to be defined in the global scope, which has the same effect.)

// dynamic initializer function generated
int a = init();
CObject o(arg1, arg2);
CObject* op = new CObject(arg1, arg2);

このデッドロックの危険性は、該当モジュールが /clr を指定してコンパイルされているかどうかと MSIL が実行されるかどうかによって決まります。This risk of deadlock depends on whether the containing module is compiled with /clr and whether MSIL will be executed. 具体的には、静的変数を /clr を指定せずにコンパイルする場合 (または静的変数が #pragma unmanaged ブロックに存在する場合) に、その変数の初期化に必要な動的初期化子によって MSIL 命令が実行されると、デッドロックが発生することがあります。Specifically, if the static variable is compiled without /clr (or resides in a #pragma unmanaged block), and the dynamic initializer required to initialize it results in the execution of MSIL instructions, deadlock may occur. この原因は、 /clrを指定せずにコンパイルされたモジュールでは、DllMain によって静的変数の初期化が実行されることにあります。This is because, for modules compiled without /clr, the initialization of static variables is performed by DllMain. これに対して、 /clr を指定してコンパイルされた静的変数は、アンマネージ初期化段階が完了してローダー ロックが解除された後に、.cctor によって初期化されます。In contrast, static variables compiled with /clr are initialized by the .cctor, after the unmanaged initialization stage has completed and the loader lock has been released.

静的変数の動的な初期化が原因で発生するデッドロックに対する解決策は多数あります (問題解決にかかる時間が短い順に記載しています)。There are a number of solutions to deadlock caused by the dynamic initialization of static variables (arranged roughly in order of time required to fix the problem):

  • 静的変数を含むソース ファイルは、 /clrを指定してコンパイルします。The source file containing the static variable can be compiled with /clr.

  • 静的変数によって呼び出されるすべての関数は、#pragma unmanaged ディレクティブを使用してネイティブ コードにコンパイルします。All functions called by the static variable can be compiled to native code using the #pragma unmanaged directive.

  • 静的変数が依存するコードを手動で複製して、.NET バージョンとネイティブ バージョンを作成し、それぞれに異なる名前を指定します。Manually clone the code that the static variable depends upon, providing both a .NET and a native version with different names. その後、開発者は、ネイティブな静的初期化子からネイティブ バージョンを呼び出し、それ以外の場所から .NET バージョンを呼び出します。Developers can then call the native version from native static initializers and call the .NET version elsewhere.

起動に影響を与える、ユーザーが指定した関数User-Supplied Functions Affecting Startup

起動時の初期化でライブラリが依存する、ユーザー指定の関数がいくつかあります。There are several user-supplied functions on which libraries depend for initialization during startup. たとえば、グローバルになど、C++ の演算子をオーバー ロードするとき、newdelete演算子、ユーザー指定のバージョンは C++ 標準ライブラリの初期化や破棄など、どこでも使用されます。For example, when globally overloading operators in C++ such as the new and delete operators, the user-provided versions are used everywhere, including in C++ Standard Library initialization and destruction. その結果、C++ 標準ライブラリとユーザー指定の静的初期化子はこれらの演算子の任意のユーザーが指定したバージョンを呼び出します。As a result, C++ Standard Library and user-provided static initializers will invoke any user-provided versions of these operators.

ユーザー指定バージョンが MSIL にコンパイルされると、これらの初期化子は、ローダー ロックが保持されているときに MSIL 命令を実行しようとします。If the user-provided versions are compiled to MSIL, then these initializers will be attempting to execute MSIL instructions while the loader lock is held. ユーザーが指定したmalloc同じ影響します。A user-supplied malloc has the same consequences. この問題を解決するには、これらのオーバーロードまたはユーザー指定定義を、#pragma unmanaged ディレクティブを使用してネイティブ コードとして実装する必要があります。To resolve this problem, any of these overloads or user-supplied definitions must be implemented as native code using the #pragma unmanaged directive.

このシナリオの詳細については、「診断に対する障害」を参照してください。Please see "Impediments to Diagnosis" for more information on this scenario.

カスタム ロケールCustom Locales

ユーザーがグローバルなカスタム ロケールを指定すると、このロケールは、今後、すべての入出力ストリームの初期化に使用されます。静的に初期化されるストリームも対象になります。If the user provides a custom global locale, this locale will be used for initializing all future I/O streams, including those that are statically initialized. このグローバルなロケール オブジェクトを MSIL にコンパイルすると、MSIL にコンパイルされたロケール オブジェクト メンバー関数が、ローダー ロックが保持されているときに呼び出されることがあります。If this global locale object is compiled to MSIL, then locale-object member functions compiled to MSIL may be invoked while the loader lock is held.

この問題を解決するためのオプションが 3 つあります。There are three options for solving this problem:

グローバル入出力ストリームの定義をすべて含むソース ファイルを、 /clr オプションを使用してコンパイルします。The source files containing all global I/O stream definitions can be compiled using the /clr option. これにより、ローダー ロックの状態では静的初期化子が実行されなくなります。This will prevent their static initializers from being executed under loader lock.

カスタム ロケールの関数定義は、#pragma unmanaged ディレクティブを使用することによって、ネイティブ コードにコンパイルできます。The custom locale function definitions can be compiled to native code by using the #pragma unmanaged directive.

ローダー ロックが解除されるまで、カスタム ロケールをグローバル ロケールとして設定しないようにします。Refrain from setting the custom locale as the global locale until after the loader lock is released. その後、初期化中に作成された入出力ストリームをカスタム ロケールで明示的に構成します。Then explicitly configure I/O streams created during initialization with the custom locale.

診断に対する障害Impediments to Diagnosis

場合によっては、デッドロックの原因を検出することが困難になります。In some cases it is difficult to detect the source of deadlocks. 以下では、そのようなシナリオとそれらの問題の解決策について説明します。The following subsections discuss these scenarios and ways to work around these issues.

ヘッダーでの実装Implementation in Headers

特殊なケースで、ヘッダー ファイル内に関数を実装すると、診断が困難になる場合があります。In select cases, function implementations inside header files can complicate diagnosis. インライン関数とテンプレート コードの両方で、その関数をヘッダー ファイルに指定する必要があります。Inline functions and template code both require that functions be specified in a header file. C++ 言語では、単一定義規則を指定します。単一定義規則を指定すると、同じ名前で実装されているすべての関数が、強制的に同じ意味にされます。The C++ language specifies the One Definition Rule, which forces all implementations of functions with the same name to be semantically equivalent. その結果、C++ リンカーでは、特定の関数を重複して実装しているオブジェクト ファイルをマージする際に特別に注意する必要がなくなります。Consequently, the C++ linker need not make any special considerations when merging object files that have duplicate implementations of a given function.

Visual Studio 2005 では、前に、リンカーは単に最大規模のさまざまなソース ファイルを異なる最適化オプションを使用する場合は、事前宣言およびシナリオを対応するためにこれらと同じ意味の定義は選択します。Prior to Visual Studio 2005, the linker simply chooses the largest of these semantically equivalent definitions, to accommodate forward declarations and scenarios when different optimization options are used for different source files. これにより、ネイティブ DLL と .NET DLL が混在する問題が発生します。This creates a problem for mixed native/.NET DLLs.

同じヘッダーがあるのででの C++ ファイルの両方に /clr有効/無効、または #include #pragma 内にラップできますunmanagedブロックすると、MSIL とネイティブ バージョンの提供する関数の両方があることができますヘッダーで実装します。Because the same header may be included both by C++ files with /clr enabled and disabled, or a #include can be wrapped inside a #pragma unmanaged block, it is possible to have both MSIL and native versions of functions that provide implementations in headers. MSIL の実装とネイティブの実装は、ローダー ロックの状態での初期化については異なる意味を持ちます。これは、事実上、単一定義規則に違反しています。MSIL and native implementations have different semantics with respect to initialization under the loader lock, which effectively violates the one definition rule. その結果、リンカーが最大の実装を選択すると、他の場所で #pragma unmanaged ディレクティブを使用して明示的にネイティブ コードにコンパイルした場合でも、関数の MSIL バージョンが選択されます。Consequently, when the linker chooses the largest implementation, it may choose the MSIL version of a function, even if it was explicitly compiled to native code elsewhere using the #pragma unmanaged directive. MSIL バージョンのテンプレートまたはインライン関数がローダー ロック中に呼び出されないようにするには、ローダー ロック中に呼び出されるこのような各関数のすべての定義を、#pragma unmanaged ディレクティブで修飾する必要があります。To ensure that an MSIL version of a template or inline function is never called under loader lock, every definition of every such function called under loader lock must be modified with the #pragma unmanaged directive. サード パーティのヘッダー ファイルの場合、これを実現するための最も簡単な方法は、問題のヘッダー ファイルの #include ディレクティブの周囲で #pragma unmanaged ディレクティブをプッシュしてポップすることです。If the header file is from a third party, the easiest way to achieve this is to push and pop the #pragma unmanaged directive around the #include directive for the offending header file. (を参照してくださいマネージ、アンマネージ例についてはします)。ただし、この方法は、直接 .NET API を呼び出す必要のある他のコードを含むヘッダーには有効ではありません。(See managed, unmanaged for an example.) However, this strategy will not work for headers that contain other code that must directly call .NET APIs.

ローダー ロックを扱うユーザーの負担を減らすため、マネージドとネイティブの両方の実装が存在する場合、リンカーはネイティブの実装を選択するようになっています。As a convenience for users dealing with loader lock, the linker will choose the native implementation over the managed when presented with both. これにより、上記の問題は回避されます。This avoids the above issues. ただし、このリリースではコンパイラに未解決の問題が 2 つ残っているため、この規則には、次の 2 つの例外があります。However, there are two exceptions to this rule in this release due to two unresolved issues with the compiler:

  • グローバル静的関数ポインターを介したインライン関数呼び出しである場合。The call is to an inline function is through a global static function pointer. 仮想関数はグローバル関数ポインターを介して呼び出されるため、このシナリオには特に注意する必要があります。This scenario is particularly notable because virtual functions are called through global function pointers. 例えば以下のようにします。For example,
#include "definesmyObject.h"
#include "definesclassC.h"

typedef void (*function_pointer_t)();

function_pointer_t myObject_p = &myObject;

#pragma unmanaged
void DuringLoaderlock(C & c)
{
    // Either of these calls could resolve to a managed implementation,
    // at link-time, even if a native implementation also exists.
    c.VirtualMember();
    myObject_p();
}

デバッグ モードでの診断Diagnosing in Debug Mode

ローダー ロックに関する問題の診断はすべて、デバッグ ビルドで行う必要があります。All diagnoses of loader lock problems should be done with Debug builds. リリース ビルドは診断を生成しない場合があります。また、リリース モードで実行される最適化によって、ローダー ロックのシナリオで MSIL の一部が隠されることがあります。Release builds may not produce diagnostics, and the optimizations performed in Release mode may mask some of the MSIL under loader lock scenarios.

ローダー ロックに関する問題をデバッグする方法How to Debug Loader Lock Issues

MSIL 関数が呼び出されたときに、CLR が生成する診断は、CLR の実行を中断します。The diagnostic that the CLR generates when an MSIL function is invoked causes the CLR to suspend execution. さらに、このことが原因で、デバッグ対象をインプロセスで実行している場合は Visual C++ の混合モード デバッガーも中断されます。In turn, this causes the Visual C++ mixed-mode debugger to be suspended as well when running the debuggee in-process. ただし、プロセスにアタッチした場合、混合デバッガーを使用してデバッグ対象のマネージド コールスタックを取得することはできません。However, when attaching to the process, it is not possible to obtain a managed callstack for the debuggee using the mixed debugger.

ローダー ロック中に呼び出された特定の MSIL 関数を識別するには、開発者が次の手順を実行する必要があります。To identify the specific MSIL function that was called under loader lock, developers should complete the following steps:

  1. mscoree.dll と mscorwks.dll のシンボルを使用できるようにします。Ensure that symbols for mscoree.dll and mscorwks.dll are available.

    これは、次の 2 つの方法で行うことができます。This can be done in two ways. 1 つ目の方法として、mscoree.dll と mscorwks.dll の PDB をシンボル検索パスに追加します。First, the PDBs for mscoree.dll and mscorwks.dll can be added to the symbol search path. そのためには、シンボル検索パスのオプションのダイアログ ボックスを開きます。To do this, open the symbol search path options dialog. (から、ツール] メニューの [選択オプションします。(From the Tools menu, choose Options. 左側のウィンドウで、オプションダイアログ ボックスで、デバッグノード選択シンボル)。mscoree.dll PDB ファイルと mscorwks.dll PDB ファイルのパスを検索一覧に追加します。In the left pane of the Options dialog box, open the Debugging node and choose Symbols.) Add the path to the mscoree.dll and mscorwks.dll PDB files to the search list. これらの PDB は、%VSINSTALLDIR%\SDK\v2.0\symbols にインストールされます。These PDBs are installed to the %VSINSTALLDIR%\SDK\v2.0\symbols. [OK] をクリックします。Choose OK.

    2 つ目の方法として、mscoree.dll と mscorwks.dll の PDB を Microsoft Symbol Server からダウンロードします。Second, the PDBs for mscoree.dll and mscorwks.dll can be downloaded from the Microsoft Symbol Server. Symbol Server を構成するには、シンボル検索パスのオプションのダイアログ ボックスを開きます。To configure Symbol Server, open the symbol search path options dialog. (から、ツール] メニューの [選択オプションします。(From the Tools menu, choose Options. 左側のウィンドウで、オプションダイアログ ボックスで、デバッグノード選択シンボル)。次の検索パスを検索リストに追加: http://msdl.microsoft.com/download/symbols します。In the left pane of the Options dialog box, open the Debugging node and choose Symbols.) Add the following search path to the search list: http://msdl.microsoft.com/download/symbols. シンボルのキャッシュ ディレクトリをシンボル サーバーのキャッシュのテキスト ボックスに追加します。Add a symbol cache directory to the symbol server cache text box. [OK] をクリックします。Choose OK.

  2. デバッガーのモードをネイティブのみに設定します。Set debugger mode to native-only mode.

    これを行うには、開く、プロパティソリューションのスタートアップ プロジェクトのグリッド。To do this, open the Properties grid for the startup project in the solution. 選択構成プロパティ > デバッグします。Select Configuration Properties > Debugging. 設定、デバッガーの種類ネイティブのみします。Set the Debugger Type to Native-Only.

  3. (F5 キーを押して) デバッガーを起動します。Start the debugger (F5).

  4. ときに、 /clr診断が生成されたら、選択再試行選び、中断します。When the /clr diagnostic is generated, choose Retry and then choose Break.

  5. [呼び出し履歴] ウィンドウを開きます。Open the call stack window. (メニュー バーで、デバッグ > Windows > 呼び出し履歴)。問題のあるDllMainまたは静的初期化子は緑色の矢印で識別されます。(On the menu bar, choose Debug > Windows > Call Stack.) The offending DllMain or static initializer is identified with a green arrow. 問題のある関数が示されない場合は、以下の手順を実行して検索する必要があります。If the offending function is not identified, the following steps must be taken to find it.

  6. 開く、イミディ エイトウィンドウ (メニュー バーで、デバッグ > Windows > イミディ エイト)。Open the Immediate window (On the menu bar, choose Debug > Windows > Immediate.)

  7. 入力に .load sos.dll、イミディ エイトウィンドウ、SOS デバッグ サービスを読み込めません。Type .load sos.dll into the Immediate window to load the SOS debugging service.

  8. 型! dumpstack に、イミディ エイト、内部の完全な一覧を取得するにはウィンドウ /clrスタック。Type !dumpstack into the Immediate window to obtain a complete listing of the internal /clr stack.

  9. _CorDllMain の (スタックの一番下に最も近い) 最初のインスタンスを探します (場合DllMain問題の原因) もの、または GetTargetForVTableEntry (静的初期化子、問題の原因) 場合。Look for the first instance (closest to the bottom of the stack) of either _CorDllMain (if DllMain causes the issue) or _VTableBootstrapThunkInitHelperStub or GetTargetForVTableEntry (if a static initializer causes the issue). この呼び出しのすぐ下にあるスタック エントリが、ローダー ロック中に実行しようとした、MSIL 実装の関数の呼び出しです。The stack entry just below this call is the invocation of the MSIL implemented function that attempted to execute under loader lock.

  10. ソース ファイルに移動し、前の手順で特定し適切な数のシナリオとソリューションのシナリオ」セクションで説明されているを使用して、問題の行。Go to the source file and line number identified in the previous step and correct the problem using the scenarios and solutions described in the Scenarios section.

Example

説明Description

次の例からのコードを移動することによってローダー ロックを回避する方法を示しています。DllMainグローバル オブジェクトのコンス トラクターにします。The following sample shows how to avoid loader lock by moving code from DllMain into the constructor of a global object.

このサンプルでは、コンス トラクターには、元のマネージ オブジェクトが含まれています。 グローバル マネージ オブジェクトはDllMainします。In this sample, there is a global managed object whose constructor contains the managed object that was originally in DllMain. このサンプルの 2 番目の部分は、マネージド オブジェクトのインスタンスを作成して、初期化を実行するモジュール コンストラクターを呼び出し、アセンブリを参照します。The second part of this sample references the assembly, creating an instance of the managed object to invoke the module constructor which does the initialization.

コードCode

// initializing_mixed_assemblies.cpp
// compile with: /clr /LD
#pragma once
#include <stdio.h>
#include <windows.h>
struct __declspec(dllexport) A {
   A() {
      System::Console::WriteLine("Module ctor initializing based on global instance of class.\n");
   }

   void Test() {
      printf_s("Test called so linker does not throw away unused object.\n");
   }
};

#pragma unmanaged
// Global instance of object
A obj;

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
   // Remove all managed code from here and put it in constructor of A.
   return true;
}

この例では、混在アセンブリの初期化の問題を示しています。This example demonstrates issues in initialization of mixed assemblies:

// initializing_mixed_assemblies_2.cpp
// compile with: /clr initializing_mixed_assemblies.lib
#include <windows.h>
using namespace System;
#include <stdio.h>
#using "initializing_mixed_assemblies.dll"
struct __declspec(dllimport) A {
   void Test();
};

int main() {
   A obj;
   obj.Test();
}

このコードを実行すると、次の出力が生成されます。This code produces the following output:

Module ctor initializing based on global instance of class.

Test called so linker does not throw away unused object.

関連項目See also

混在 (ネイティブおよびマネージド) アセンブリMixed (Native and Managed) Assemblies