ダイナミック リンク ライブラリのリダイレクト

"DLL ローダー" は、オペレーティング システム (OS) の一部であり、DLL への参照を解決して読み込み、リンクします。 ダイナミック リンク ライブラリ (DLL) のリダイレクトは、"DLL ローダー" の動作に影響を与え、いくつかの候補 DLL から実際に読み込む 1 つを制御できる手法の 1 つです。

この機能の他の名前には、.localDot LocalDotLocalDot Local Debugging などがあります。

DLL のバージョン管理に関する問題

アプリケーションが共有 DLL の特定のバージョンに依存していて、別のアプリケーションがその DLL の新しいか古いバージョンとともにインストールされている場合は、互換性の問題と不安定な状態が発生する可能性があります。これにより、アプリが失敗し始める可能性があります。

DLL ローダーは、他のファイル システムの場所を検索する前に、呼び出し元のプロセスが読み込まれたフォルダー (実行可能ファイルのフォルダー) を検索します。 そのため、回避策の 1 つは、アプリに必要な DLL を実行可能ファイルのフォルダーにインストールすることです。 これにより、DLL が実質的にプライベートになります。

しかし、これでは COM の問題は解決しません。 互換性のない 2 つのバージョンの COM サーバーをインストールして登録できますが (異なるファイル システムの場所でも)、COM サーバーを登録する場所は 1 つのみです。 そのため、登録されている最新の COM サーバーのみがアクティブになります。

リダイレクトを使用して、これらの問題を解決できます。

プライベート バイナリを読み込んでテストする

DLL ローダーが従う規則により、システム DLL は確実に Windows システムの場所 (システム フォルダー (%SystemRoot%\system32) など) から読み込まれます。 これらのルールは、敵対者が作成したコードを書き込み可能な場所に置き、いくつかのプロセスにその読み込みと実行を指示するという、攻撃の植え付けを回避します。 しかし、ローダーのルールにより、OS コンポーネントの操作がさらに困難になります。それらを実行するにはシステムを更新することが必要なためであり、これは非常に影響が大きい変更です。

しかし、リダイレクトを使用して DLL のプライベート コピーを読み込むことができます (テストや、コード変更のパフォーマンスへの影響の測定などのため)。

パブリック WindowsAppSDK GitHub リポジトリのソース コードに投稿する場合、変更をテストすると思われます。 そして、これが、Windows アプリ SDKに付属するバージョンではなく、DLL のプライベート コピーを読み込むためにリダイレクトを使用できるシナリオです。

オプション

実は、アプリで使用する DLL のバージョンを確実に使用するには、次の 2 つの方法があります。

ヒント

開発者または管理者は、既存のアプリケーションに DLL リダイレクトを使用する必要があります。 これは、アプリ自体に変更を加える必要がないためです。 ただし、新しいアプリを作成する場合、または既存のアプリを更新していて、潜在的な問題からアプリを分離する場合は、サイド バイ サイド コンポーネントを作成します。

オプション: レジストリを構成する

マシン全体で DLL リダイレクトを有効にするには、新しいレジストリ値を作成する必要があります。 キー HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options の下に、DevOverrideEnable という名前の新しい DWORD 値を作成します。 値を 1 に設定し、コンピューターを再起動します。 または、以下のコマンドを使用します (コンピューターを再起動します)。

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" /v DevOverrideEnable /t REG_DWORD /d 1

そのレジストリ値を設定すると、アプリにアプリケーション マニフェストがある場合でも、DotLocal DLL リダイレクトが優先されます。

リダイレクト ファイルまたはフォルダーを作成する

DLL リダイレクトを使用するには、このトピックの後のセクションで説明するように、(使用しているアプリの種類に応じて) "リダイレクト ファイル" または "リダイレクト フォルダー" を作成します。

パッケージ アプリの DLL をリダイレクトする方法

パッケージ アプリには、DLL リダイレクト用の特別なフォルダー構造が必要です。 次のパスは、リダイレクトが有効になっているときにローダーが検索する場所です。

<Drive>:\<path_to_package>\microsoft.system.package.metadata\application.local\

.vcxproj ファイルを編集できる場合、パッケージにその特別なフォルダーを作成してデプロイする便利な方法は、次のように .vcxproj 内のビルドに追加の手順をいくつか追加することです。

<ItemDefinitionGroup>
    <PreBuildEvent>
        <Command>
            del $(FinalAppxManifestName) 2&gt;nul
            <!-- [[Using_.local_(DotLocal)_with_a_packaged_app]] This makes the extra DLL deployed via F5 get loaded instead of the system one. -->
            if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
            if EXIST "&lt;A.dll&gt;" copy /y "&lt;A.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
            if EXIST "&lt;B.dll&gt;" copy /y "&lt;B.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
        </Command>
    </PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
    <!-- Include any locally built system experience -->
    <Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**">
        <Link>microsoft.system.package.metadata\application.local</Link>
    </Media>
</ItemGroup>

その構成で実行される内容をいくつか見てみましょう。

  1. Visual Studio の [デバッグなしで開始] (または [デバッグの開始]) エクスペリエンスに対する PreBuildEvent を設定します。

    <ItemDefinitionGroup>
      <PreBuildEvent>
    
  2. 中間ディレクトリに正しいフォルダー構造があることを確認します。

    <!-- [[Using_.local_(DotLocal)_with_modern_apps]] This makes the extra DLL deployed via Start get loaded instead of the system one. -->
    if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
    
  3. ローカルにビルドした DLL (およびシステム デプロイされた DLL より優先して使用するもの) を application.local ディレクトリにコピーします。 DLL は、どこからでも取得できます (.vcxproj に対して使用可能なマクロを使用することをお勧めします)。 このプロジェクトより前に、これらの DLL がビルドされていることを確認してください。それ以外の場合は、欠落します。 ここには 2 つの "テンプレート" のコピー コマンドが表示されています。必要な数だけテンプレートを使用し、<path-to-local-dll> プレースホルダーを編集してください。

      if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
      if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
      </Command>
    </PreBuildEvent>
    
  4. 最後に、デプロイされるパッケージに特別なディレクトリとその内容を含める必要があることを指定します。

    <ItemGroup>
      <!-- Include any locally built system experience -->
      <Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**">
        <Link>microsoft.system.package.metadata\application.local</Link>
      </Media>
    </ItemGroup>
    

ここで説明した方法 (中間ディレクトリを使用) は、ソース コード管理の参加リストを簡潔に保ち、コンパイル済みバイナリを誤ってコミットする可能性を減らします。

次に実行する必要がある作業は、プロジェクトの (再) デプロイのみです。 簡潔で完全な (再) デプロイを行うには、ターゲット デバイス上の既存のデプロイをアンインストール/削除することも必要な場合があります。

バイナリを手動でコピーする

上記の方法で .vcxproj を使用できない場合は、いくつかの簡単な手順を使用してターゲット デバイス上で手動で同じ目的を達成できます。

  1. パッケージのインストール フォルダーを決定します。 PowerShell でこれを行うには、コマンド Get-AppxPackage を発行し、返される InstallLocation を探します。

  2. その InstallLocation を使用して ACL を変更し、フォルダーの作成/ファイルのコピーを自分ができるようにします。 次のように、このスクリプトの <InstallLocation> プレースホルダーを編集し、スクリプトを実行します。

    cd <InstallLocation>\Microsoft.system.package.metadata
    takeown /F . /A
    icacls  . /grant Administrators:F
    md <InstallLocation>\Microsoft.system.package.metadata\application.local
    
  3. 最後に、ローカルにビルドした DLL (およびシステム デプロイされた DLL より優先して使用するもの) を application.local ディレクトリに手動でコピーし、アプリを (再) 起動します。

すべてが成功したことを確認する

実行時に正しい DLL が読み込まれていることを確認するために、デバッガーがアタッチされた状態で Visual Studio を使用できます。

  1. [モジュール] ウィンドウを開きます ([デバッグ]>[Windows]>[モジュール])。
  2. DLL を見つけ、パスが、システム デプロイ バージョンではなく、リダイレクトされたコピーを示していることを確認します。
  3. 特定の DLL のコピーが 1 つだけ読み込まれていることを確認します。

非パッケージ アプリの DLL をリダイレクトする方法

リダイレクト ファイルの名前を <your_app_name>.local にする必要があります。 つまり、アプリの名前が Editor.exe の場合、リダイレクト ファイルの名前は Editor.exe.local です。 リダイレクト ファイルを、実行可能ファイルのフォルダーにインストールする必要があります。 DLL も実行可能ファイルのフォルダーにインストールする必要があります。

リダイレクト ファイルの "内容" は無視されます。存在するだけで、DLL ローダーは DLL を読み込むたびに実行可能ファイルのフォルダーをまず確認します。 COM の問題を軽減するために、そのリダイレクトは絶対パスと部分名の読み込みの両方に適用されます。 そのため、リダイレクトは、COM の場合にも、LoadLibraryLoadLibraryEx に指定されたパスにも関係なく、発生します。 DLL が実行可能ファイルのフォルダーに見つからない場合、読み込みは通常の検索順序に従います。 たとえば、アプリ C:\myapp\myapp.exe が次のパスを使用して LoadLibrary を呼び出すとします。

C:\Program Files\Common Files\System\mydll.dll

そして、C:\myapp\myapp.exe.localC:\myapp\mydll.dll の両方が存在する場合、LoadLibraryC:\myapp\mydll.dll を読み込みます。 それ以外の場合、LoadLibraryC:\Program Files\Common Files\System\mydll.dll を読み込みます。

また、C:\myapp\myapp.exe.local という名前のフォルダーが存在し、それに mydll.dll が含まれている場合、LoadLibraryC:\myapp\myapp.exe.local\mydll.dll を読み込みます。

DLL リダイレクトを使用していて、アプリが検索順序のいずれのドライブとディレクトリにもアクセスできない場合、LoadLibrary はアクセスが拒否されるとすぐに検索を停止します。 DLL リダイレクトを使用して "いない" 場合、LoadLibrary はアクセスできないディレクトリをスキップし、検索を続行します。

DLL リダイレクトを使用しない場合でも、アプリを含む同じフォルダーにアプリの DLL をインストールすることをお勧めします。 このようにすると、アプリをインストールしても DLL の他のコピーの上書き (これによる、他のアプリの失敗) は発生しません。 また、このお勧めの方法に従うと、他のアプリによる DLL のコピーの上書き (およびアプリの失敗) も発生しません。