Windows XP で分離アプリケーションと Side-by-Side アセンブリをビルドし、サービスを提供する方法

RoseMarie FitzSimons
Henry Borys
Microsoft Corporation

August 24, 2001
日本語版最終更新日 2002 年 11 月 12 日

概要: この資料では、 Microsoft Windows XP で分離アプリケーションと Side-by-Side アセンブリを作成、 配布し、安全にサービスを提供する手順を詳しく説明しています。 このソリューションは、 Windows クライアント アプリケーションでバージョンの競合を取り除くのに役立ちます。

目次

はじめに
Windows XP のソリューション: 概要
分離アプリケーションの作成
Side-by-Side アセンブリの作成
共有アセンブリとアプリケーションへのサービスの提供
プライベート アセンブリを使用する既存のアプリケーションの修正
まとめ
付録: 共有コンポーネントを提供すべきでしょうか ?

はじめに

ダイナミック リンク ライブラリ (DLL) のバージョンの競合は、 Microsoft(R) Windows(R) とっては複雑で、 やっかいな問題 (DLL Hell) でした。 Microsoft(R) Windows(R) XP は、 アプリケーション開発者が分離アプリケーションと Side-by-Side アセンブリをビルドできるようにすることによって、 この問題に対処しています。 この結果、 アプリケーションの開発、配置、および保守時に DLL のバージョンの競合の影響を取り除くことに役立ちます。

Windows XP はアプリケーションを分離できるようにしています。 その結果、 アプリケーションはアプリケーションがテストされたときのバージョンのコンポーネントを一貫して取得できます。 新しいアプリケーションが新しいバージョンのコンポーネントを使用するのに対して、 古いアプリケーションは古いバージョンのコンポーネントを使用し続けます。 アプリケーションの作成者や管理者はマニフェストを使用して、 グローバルまたはアプリケーション単位のいずれかで、 配置後の Side-by-Side アセンブリの共有を管理できます。 分離アプリケーションと Side-by-Side アセンブリを使用して、 Microsoft またはその他のコンポーネント作成者が提供する Win32(R) アセンブリを安全に共有するアプリケーションを開発できます。

分離アプリケーションと Side-by-Side アセンブリを作成し、 サービスを提供するためのソリューションは最初に Microsoft(R) Windows(R) 98 Second Edition と Microsoft(R) Windows(R) 2000 で導入されましたが、 Windows XP は導入当初よりもはるかに完全なソリューションを提供します。 この資料では、 Windows XP が提供する利点を詳しく説明し、 Windows XP で分離アプリケーションと Side-by-Side アセンブリを作成、 配布し、安全にサービスを提供する手順を説明します。

Windows XP のソリューション: 概要

Windows XP ソリューションの目標は、 開発者がシステムに対する変更による影響を受けない分離アプリケーションを作成できるようにすることです。 つまり、DLL の共有を安全な方法で行うことを可能にする一方で、 共有 DLL の価値を利用することです。 さらに、アプリケーション全体を記録する必要なしに、 既存のアプリケーションがこのような共有コンポーネントを使用できるようにすることも目標になります。 Windows XP はこのような目標を実現するために、 以下の機能を組み込みました。

  • Side-by-Side アセンブリのサポート。 単一のバージョンの共有コンポーネントを利用できた以前のバージョンの Windows とは異なり、 Side-by-Side アセンブリ サポートは、 複数のバージョンのアセンブリをインストールし、 同時に実行できます。 このようなアセンブリで構成されるコンポーネントは、 自己記述型で、分離されていて、 単体で使用でき、コンポーネントが互いに競合しないように作成されている必要があります。 Side-by-Side アセンブリはアップデートされることはありません。 代わりに、同じシステム上に新しいバージョンのアセンブリが、 古いバージョンと共にインストールされます。 そのため、アプリケーションはテストに使用したのと同じバージョンを使用していることを信頼できます。 アセンブリをアプリケーションに対してプライベートにできます。 つまり、アプリケーションごとに排他的に使用できます。 また、アセンブリを共有することもできます。
  • 分離アプリケーション。 同様に、共有コンポーネントの悪影響を取り除くために、 アプリケーションを共有コンポーネントから分離します。 開発者は、 アセンブリを無作為に共有するのではなく、 アプリケーションが使用すべきバージョンの Side-by-Side アセンブリを選択します。 アプリケーションをテストしたときに使用した構成を、 任意の配置先コンピュータに複製します。 アプリケーションと特定のバージョンの Side-by-Side アセンブリとの依存関係を、 アプリケーションのマニフェスト ファイルに記述します。 オペレーティング システムはこの情報を使用して、 アプリケーションに適切なバージョンのコンポーネントが提供されることを保証します。 同じアセンブリの異なるバージョンを使ってビルドされた複数のアプリケーションは、 互いの実行に影響することなく同時に実行されます。 システム上の他のアプリケーションのインストール、削除、またはアップグレードは、 分離アプリケーションにはまったく影響しません。 アプリケーションのすべてのコンポーネントが Side-by-Side アセンブリの場合、 そのアプリケーションは完全に分離されます。 アプリケーションの一部のコンポーネントが Side-by-Side アセンブリで、 一部のコンポーネントが Side-by-Side アセンブリではない共有コンポーネントである場合、 そのアプリケーションは部分的に分離されます。 大部分のアプリケーションは部分的に分離されることになるでしょう。
  • 発行元構成とアプリケーション構成。 Windows XP は、DLL を静的にリンクすることとは対照的に、 共有アセンブリや共有アプリケーションにサービスを提供するためのモデルを提供します。 共有アセンブリの発行元は、 発行元構成を発行できます。 発行元構成は、システム上のすべてのアプリケーションを特定のバージョンのアセンブリにリダイレクトできます。 発行元は、 この種の構成を発行しても安全であるとき (つまり、旧バージョンとの互換性のバグを解決した場合)、 またはすべてのアプリケーションが最新のバージョンを使用する必要があるとき (たとえば、コンポーネントに対してセキュリティの修正が行われた場合) だけにこの種の構成を発行することになるでしょう。 どのバージョンを使用するかは、最終的にアプリケーションが決定します。 アプリケーションには発行元構成よりもアプリケーション構成を優先するオプションがあります。 このモデルは、アプリケーションや管理者に、 配置後にアプリケーションを再インストールすることなしに構成を更新する柔軟性も提供します。 そのため、分離アプリケーションは Side-by-Side アセンブリの出荷に拘束されません。

分離アプリケーションは、 アプリケーションが他のアプリケーションに変更を加えることや、 システム アセンブリに対してシステム変更を加えることを防ぐだけでなく、 以下のような利点ももたらします。

  • 開発者は、 他のアプリケーションに影響を与える危険性なしに、 アプリケーションが使用するアセンブリのバグを解決し、 新機能を追加できます。
  • アプリケーションをインストールするときに、 コンピュータを再起動する必要がなくなります。
  • 開発者は、 XCOPY などのインストール メカニズムを使って、 アプリケーションを容易にインストールまたは複製できます。
  • アプリケーションは、 再コンパイルや再インストールの必要なしに、 共有アセンブリのサービス モデルを利用できます。
  • システム上に存在しないバージョンの共有コンポーネントに依存するアプリケーションは、 そのアプリケーションが適切なバージョンにアクセスできるようにすることによって、 その都度臨機応変に解決されます。

分離アプリケーションや Side-by-Side アセンブリに対する Windows XP のアプローチは、 Windows 98 Second Edition や Windows 2000 の時点で導入された Side-bySide 共有や DLL リダイレクションを大きく前進させたものです。 以前の実装は、 開発者がそのコンポーネントの複数バージョンの同時実行をサポートする新しいコンポーネントをビルドすることを推奨していました。 このアプローチは依然としてレジストリに依存しているので、 コンポーネントが完全に分離されたことにはなりませんでした。 レジストリの競合などの事象により、 アプリケーションが機能しなくなることがありました。

Windows XP では、アセンブリはマニフェストにより記述されます。 したがって、COM アクティベーション データの格納やアクセスではレジストリに依存しなくなりました。 その結果、コンポーネントを分離できるようになります。 共有コンポーネントは、 アプリケーションが最新のバージョンを使用するように、 サービスの提供を受けることもできます。 以下の表は、 このことに関して Windows XP が Windows 2000 に優っている点を示しています。 (Windows 2000 の Side-by-Side コンポーネント共有の詳細については、 「アプリケーションで共有する Side-by-Side コンポーネントの実装 (拡張)」を参照してください)。

機能 Windows 2000 Windows XP
COM アクティベーション データでのレジストリの使用 はい、コンポーネントがアップデートの影響を受けることがあります。 いいえ、コンポーネントを分離できるように、 アセンブリはマニフェストによって記述されます。
複数バージョンを共有する能力 いいえ、共有バージョンを 1 つだけ使用するか、 各アプリケーションがプライベート バージョンを持ちます。 はい、同時にインストールされ、グローバルに共有できます。
完全に分離され、単独で使用可能 いいえ。 はい。
共有コンポーネントがグローバルにサービスの提供を受けられる アプリケーションごとにインストールされる場合は、容易ではありません。 はい。

表 1. Windows 2000 と Windows XP の Side-by-Side 共有の比較

分離アプリケーション、アセンブリの使用法、マニフェスト、明示的なバージョン管理、 および構成を使ったサービスの提供などの概念は、 .Net Framework でも完全にサポートされます。 共通言語ランタイムは、 標準の Win32 オペレーティング システムよりも豊富なサービス セットを提供します。 たとえば、Microsoft(R) Visual Studio(R) などの開発環境では、 コード開発の一部としてマニフェストが自動的に作成されます。 Windows XP 実装でも同様に、 既定のバージョンを提供することにより、 または発行元がアセンブリごとにバージョンを優先指定したり、 管理者がシステムごとにバージョンを優先指定することにより、 オペレーティング システムが特定のバージョンに対する要求を強制します。 .NET Framework での Side-by-Side 共有の詳細については、 「Simplifying Deployment and Solving DLL Hell with the .NET Framework 」 (英語) を参照してください。

Windows XP の例

図 1 は、 アプリケーション マニフェストを使ったアプリケーションを示しています。 マニフェストは、 アプリケーションとプライベート アセンブリ windows.samples.draw との依存関係、 およびアプリケーションと特定のバージョンの共有アセンブリとの依存関係を記述します。

図 1. 部分的な分離アプリケーション

この例では説明のために、 複数のバージョンの共有アセンブリが Side-by-Side アセンブリ キャッシュ (Windows フォルダ内の WinSxS フォルダ) に同時にインストールされています。 特定のバージョンとの依存関係を参照するアプリケーションが、 これらのアセンブリを使用できます。 アプリケーションが、 マニフェストで参照されない他の共有コンポーネントを使用することもあります。 これらのコンポーネントは、たとえば system32 フォルダなどにグローバルにインストールされることになるでしょう。

分離アプリケーションの作成

分離アプリケーションを作成するには、5 つの手順が必要になります。

  1. アプリケーションが使用する共有アセンブリのバージョンを決定します。
  2. アプリケーションがプライベートに使用するコンポーネントのプライベート アセンブリを作成します。
  3. アプリケーション マニフェストを作成します。
  4. インストール パッケージを作成します。
  5. テストします。

手順 1: アプリケーションが使用する共有アセンブリのバージョンの決定

アプリケーションが使用する共有アセンブリの一覧と、 そのアプリケーションにとって最も適切なアセンブリのバージョンの一覧を作成します。 オペレーティング システムと共に提供されたアセンブリとの依存関係を指定するときは、 アプリケーションが特定のバージョンのアセンブリをインストールするマージ モジュールを含まない限り、 基礎となるプラットフォームと共に提供されたバージョンのアセンブリを選択することをお勧めします。

たとえば、GDIPlus 1.0.0.0 が Windows XP に同梱されています。 その後出荷された GDIPlus 1.0.1.0 は互換性のバグを解決しています。 (このバージョンは Windows Update で提供されます。) クライアント コンピュータでは QFE (Quick Fix Engineering) バージョンを利用できる場合と利用できない場合があるので、 アプリケーションではバージョン 1.0.0.0 との依存関係を記述することになるでしょう。 その後、発行元構成を使って最新のバージョンがインストールされると、 自動的にそのバージョンを使用することになります。

共有アセンブリが基礎となるプラットフォームの一部として含まれていない場合は、 そのアセンブリをインストールするために、 アプリケーションのインストールに Windows インストーラ マージ モジュールを含める必要があります。 依存関係の情報は、アプリケーション マニフェストに含めます。

手順 2: アプリケーションがプライベートに使用するコンポーネントのプライベート アセンブリの作成

プライベート アセンブリは、 そのアセンブリをインストールするアプリケーションだけが使用します。 このアセンブリは Side-by-Side アセンブリとして作成されるので、 Side-by-Side アセンブリとして適切に機能することを確認するテストを行う必要があります。 同じプライベート アセンブリを複数のアプリケーションが使用する場合は、 アプリケーションごとにプライベートにインストールする必要があります。 それ以外の場合は、 共有を可能にするために、共有アセンブリとして作成し、インストールできます。 プライベート アセンブリを作成することにより、 レジストリへの依存を取り除き、 XCOPY インストールを可能にします。

プライベート アセンブリと共有アセンブリを作成する手順は、 以下の「アセンブリの作成」で説明します。

手順 3: アプリケーション マニフェストの作成

アプリケーション マニフェストは以下の機能を持つ XML ファイルです。

  • アプリケーションを一意に識別します。
  • 特定のバージョンの Side-by-Side アセンブリとの依存関係を記述します。

メモ帳や標準の XML エディタを使って、マニフェストを作成できます。 InstallShield と Wise Solutions は共に今後の製品リリースでアプリケーション マニフェストの作成を完全にサポートする予定です。

アプリケーション マニフェスト名は、 実行ファイルの名前に基づき、 最後に ".manifest" を付加します。 たとえば、mysampleapp.exe のマニフェストは "mysampleapp.exe.manifest" になるでしょう。

アプリケーション マニフェストは、実行ファイルと同じフォルダに独立したファイルとしてインストールできます。 または、.rc ファイル内でリソースとしてアプリケーションに追加することもできます。

リソースとして追加する場合は以下のようになります。

#define MANIFEST_RESOURCE_ID 1 
MANIFEST_RESOURCE_ID RT_MANIFEST "mysampleapp.exe.manifest"

assemblyIdentity

各アプリケーションは、 アプリケーション マニフェスト内で記述される一意な assemblyIdentity を持ちます。 assemblyIdentity は、 アプリケーション名、バージョン、アプリケーションの種類、 およびアプリケーションを実行する予定のプロセッサ アーキテクチャを識別します。 以下に例を示します。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
/>

name: アプリケーションに重複しない名前を付けます。 Organization.Division.Name の形式で名前を付けることをお勧めします。 たとえば、Microsoft.Windows.mysampleapp という名前を付けます。

type: アプリケーションの種類を指定します。 値は win32 で、すべて小文字にする必要があります。

processorArchitecture: 対象となるプラットフォームのプロセッサを指定します。 有効な値は、32 ビット版 Windows では x86、64 ビット版 Windows では ia64 です。

version: アプリケーションのバージョンを指定します。 mmmmm.nnnnn.ooooo.ppppp のように、4 つの部分から構成されるバージョン書式を使用します。 各部分をピリオドで区切り、それぞれ 0 から 65535 までの値を指定できます。 ピリオドで区切られた各部分は、左から右に、メジャー番号、マイナー番号、ビルド番号、 およびリビジョン番号を表します。

description 要素は省略可能で、 アプリケーションの説明を含めます。

dependency セクションには、 ユーザーのアセンブリが使用する特定の Side-by-Side アセンブリの assemblyIdentity を含めます。 たとえば、アセンブリが GDIPlus とプライベート アセンブリ Foo の両方に依存関係を持つときは、 以下のようになります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
/>
<description>アプリケーションの説明をここに記述します。</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.GdiPlus"
            version="1.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Foo"
            version="6.0.0.0"
            processorArchitecture="x86"
        />
    </dependentAssembly>
</dependency>
</assembly>

手順 4: インストール パッケージの作成

アプリケーションのインストールには、 任意の有効なインストール テクノロジを使用できます。 アプリケーション マニフェストは、 アプリケーションの実行ファイルと同じフォルダにインストールする必要があることに注意してください。 共有アセンブリをインストールする場合は、 再配布の使用許諾契約書に従って、 インストール パッケージに Windows インストーラ マージ モジュールを組み込む必要があります。

手順 5: テスト

アプリケーション マニフェストが、 アプリケーション マニフェスト スキーマに準拠していて、 整形式の XML であることを検証します。 マニフェスト検証ツール Manifestchk.vbs (Platform SDK で利用できます) を使用します。

複数のバージョンを実行することにより互いに影響を及ぼさないことを保証するために、 アプリケーションをテストします。 たとえば、 コンポーネントが互いに競合しないことを確認するための Side-by-Side 共有でのシナリオを使って、 2 つのバージョンのアプリケーションを起動し、 各コンポーネントをテストします。

アプリケーションが正しくアセンブリを読み込むことを検証します。 たとえば、 アプリケーションが特定のプライベート アセンブリを使用する必要がある場合は、 アプリケーションがそのアセンブリを使用していることを確認するテストを行います。 Depends.exe (Platform SDK で利用できます) などのツール、 または任意のデバッガを使って、 アセンブリがどこから読み込まれたかを確認します。

失敗した原因を追跡するにはイベント ログを使用します。

**注   ** 場合によっては、 既存のアプリケーションの書き直しや再コンパイルを行わずに、 既存のアプリケーションを部分的な分離アプリケーションに更新できます。 これは、単純にアプリケーション マニフェストを提供し、テストすることによって行われます。 たとえば、多くのアプリケーションを更新された Microsoft(R) Windows コモン コントロールを使って起動でき、 コーディングをやり直すことなく新しい Windows の外観を得ることができます。 更新された Windows コモン コントロールの詳細については、 「Windows XP ビジュアル スタイルの使用」(英語) を参照してください。

Side-by-Side アセンブリの作成

アセンブリは、分離され、単独で使用され、マニフェストによって自己記述されるコードの原子単位です。 アセンブリは、常に一単位としてアプリケーションやユーザーに提供される 1 つ以上のファイルで構成されます。 アセンブリは、名前を付け、バインディングし、バージョン管理を行い、配置するときの単位です。

アセンブリの重要な特性の 1 つは不変であることを思い出してください。 つまり、アセンブリは一旦出荷されると、変更されることはありません。 アセンブリのバグを解決する必要がある場合、 またはアセンブリに新機能を追加する必要がある場合、 新しいバージョンのアセンブリを提供し、 以前のバージョンと共存する形でインストールする必要があります。

複数のバージョンのアセンブリを同時にインストールでき、 同じプロセス内で同時に実行できるようにアセンブリを作成します。 繰り返しになりますが、 アセンブリを作成することで、 COM アクティベーションでのレジストリ依存を取り除きます。 開発者は、 アセンブリが他のバージョンのアセンブリと同時に安全に実行できることを確認する作業を行います。 Windows XP は、このようにバージョンが付けられたアセンブリのインストールと読み込みをサポートするフレームワークを提供します。

Side-by-Side アセンブリには共有とプライベートという 2 つの一般的なカテゴリがあります。 共有アセンブリは、複数のアプリケーションが使用できるアセンブリです。 共有 Side-by-Side アセンブリはシステムにグローバルには登録されませんが、 マニフェストで依存関係を指定したアプリケーションでグローバルに使用できます。

プライベート アセンブリは、そのアセンブリをインストールしたアプリケーションだけが使用します。 同じプライベート アセンブリを複数のアプリケーションで使用する場合は、 アプリケーションごとにプライベートとしてインストールする必要があります。

プライベート アセンブリを作成する手順と共有アセンブリを作成する手順は、 次の 2 つの大きな例外を除いて、同じになります。

  • プライベート アセンブリは暗号化して署名する必要はありません。 したがって、アセンブリ マニフェストの assemblyIdentity に publickeyToken は不要です。
  • プライベート アセンブリはアプリケーション フォルダにインストールされ、 任意のインストール テクノロジでインストールできます (つまり、Windows インストーラを使用する必要はありません)。

**注   ** アセンブリを最終的に共有アセンブリとして提供する予定がある場合でも、 まずアセンブリをプライベート アセンブリとして作成し、テストします。 その結果、アセンブリに署名し、マージ モジュールとして作成する手順を、 アセンブリのテスト後まで回避できます。

Side-by-Side アセンブリを作成するには、7 つの手順が必要になります。

  1. アセンブリに含めるリソースを決定します。
  2. 複数の DLL が安全に実行できるように DLL をデザインし、作成します。
  3. ローカリゼーションの方針を検討します。
  4. アセンブリ マニフェストを作成します。
  5. ユーザーの証明書でアセンブリに署名します。
  6. アセンブリのインストール方法を決定します。
  7. テストします。

手順 1: アセンブリに含めるリソースの決定

一般的なアセンブリは、アセンブリ マニフェストで記述された 1 つの DLL です。 たとえば、Microsoft Windows Common-Controls アセンブリは DLL COMCTL32.dll を保持します。 1 つのアセンブリが複数のファイルを保持することもあります。 1 つのコンポーネントを変更すると他のコンポーネントも変更する必要があるような、 複数のコンポーネントが緊密な相互依存関係を持つ場合には、 同じアセンブリ内にファイルのグループを含める意味があります。 この優れた例が Microsoft Visual C++ Runtime アセンブリです。 このアセンブリは atl.dll、mfc42.dll、mfc42u.dll、および msvcp60.dll を含んでいます。

アセンブリに何を含めるかを判断するときに、 その後アセンブリに含めたものをどのように管理していくか、 およびアプリケーションにどのように使用させるかを検討します。 たとえば、アセンブリに 2 つのファイルを含める場合、 アセンブリの新しいバージョンを出荷するたびに、 両方が変更されたかどうかに関わらず、 両方のファイルを含めることになります。 緊密な相互依存関係を持つ 2 つのファイルがあり、 それらを別のアセンブリとして出荷することを判断した場合は、 ユーザーがバージョンを混在させて使用する場合があることを認識しておく必要があります。 両方のアセンブリを更新した場合、 新しいバージョンと古いバージョンに互換性があるかどうかに関わらず、 ユーザーが一方は新しいバージョンを使用し、 もう一方は古いバージョンを使用することを選択することがあります。

図 2. アセンブリの種類

手順 2: 複数の DLL が安全に実行できるような DLL のデザインと作成

以下に、Side-by-Side アセンブリとして適切に機能する DLL を作成するためのガイドラインを示します。

  1. 複数のバージョンが互いに影響しないで 1 つのプロセス内で同時に実行できるように DLL を作成する必要があります。 多くのアプリケーションは無数のプラグインのホストになります。 プラグインは、同じプロセス内の 1 つのコンポーネントの異なるバージョンに対して必要条件を持つことがあります。 Side-by-Side アセンブリの開発者は、 1 つのプロセス内でコンポーネントの複数のバージョンが同時に実行されるときに、 そのコンポーネントが期待どおりに動作するように作成し、テストしたことを保証する必要があります。

  2. コンポーネントを Windows XP 以外のプラットフォームに共有コンポーネントとして提供する予定がある場合は、 このようなプラットフォームでは引き続きそのコンポーネントを単一インスタンス共有コンポーネントとしてインストールすることになるでしょう。 そのため、コンポーネントの旧バージョンとの互換性を保証する必要があります。 旧バージョンとの互換性を保証しない場合は、 アプリケーションが機能しない危険性があります。

  3. アセンブリの複数のバージョンがシステム上で実行されるときは、 オブジェクトの用途を評価します。 異なるバージョンのアセンブリが、 メモリ マップド ファイル、名前付きパイプ、登録済み Windows メッセージやクラス、共有メモリ、 セマフォ、ミューテックス、およびハードウェア ドライバなど、個別のデータ構造を必要とするかどうかを判断します。 複数バージョンのアセンブリにまたがって使用するすべてのデータ構造は、 旧バージョンとの互換性を持つ必要があります。 複数のバージョンにまたがって使用できるデータ構造と、 あるアセンブリに対してプライベートにする必要のあるデータ構造を決定します。 共有データ構造が、セマフォやミューテックスなど、 個別の同期オブジェクトを必要とするかどうかを判断します。

  4. すべてのデータ構造にバージョンを割り当てます。

  5. DLL は、存在しない可能性のあるバージョンにまたがった共有に依存すべきではありません。 たとえば、異なるバージョンのアセンブリにまたがって共有される共有メモリ セクションに依存すべきではありません。

  6. 既定では、 一部のオブジェクトには、ウィンドウ クラスや Win32 Atoms など、プロセスごとに独自に名前が付けられます。 ウィンドウ クラスなどのオブジェクトは、 マニフェストを使用してアセンブリごとにバージョンを設定する必要があります。 Win32 Atoms などのオブジェクトの場合は、 複数のバージョンにまたがって共有しない限り、 バージョン固有の識別子を使用します。 バージョン固有の識別子を使用することを選択する場合は、 4 つの部分で構成されるバージョン番号を使用します。

  7. すべての DLL に自己登録ではないコードを含めます。

  8. DLL 内で #define ステートメントを使って、バージョン固有の名前をすべて定義します。 この結果、1 か所からすべてのレジストリ キーを変更できるようになります。 アセンブリの新しいバージョンをリリースするときは、 この #define ステートメントを変更する必要があるだけになります。 たとえば、次のように定義します。

    #define MyRegistryKey "MyAssembly1.0.0.0"
    
  9. 永続的に保存しないデータはすべて TEMP ディレクトリに格納します。

  10. ユーザー データはグローバルな場所には格納しません。 アプリケーション データとユーザー データは区別するようにします。

  11. すべての共有ファイルに、 アプリケーション名に依存するファイル バージョンを割り当てます。

  12. 意図的ではないプロセス間共有を防ぐために、 プロセス間でまたがって使用するすべてのメッセージとデータ構造にバージョンを割り当てます。

  13. 元の DLL のバイナリ インターフェイス互換規約に従わない新機能を追加する場合は、 新たな CLSID、ProgId、およびファイル名を割り当てる必要があります。 Side-by-Side アセンブリの将来のバージョンは、 この CLSID、ProgId、およびファイル名を使用する必要があるでしょう。 これにより、併用されない DLL のバージョンが登録されるときに、 競合を防ぐことになります。

  14. 同じ CLSID または ProgId を再利用する場合は、 そのアセンブリが旧バージョンとの互換性があることを保証するためのテストを行います。

さらに、状態の記憶については、以下のガイドラインに従います。

  1. 今後のバージョンおよび旧バージョンとの互換性を維持するための状態記憶をデザインします。 v1、v3、v2 という具合に、ランダムな順序でバージョンが使用されることを予想します。

  2. アセンブリ コード内で、そのアセンブリの既定の設定を初期化し、設定します。 既定の設定はレジストリには保存しません。

  3. 通常、アセンブリは状態情報をレジストリ キーに格納します。 アセンブリの状態を保持するレジストリ キーにバージョンを設定する簡単な方法を提供するために、 ヘッダー ファイルやヘルパ関数のセットを作成します。

  4. 同時に実行される可能性のある複数のバージョンのアセンブリを分離するために、 個別のバージョンごとにレジストリ 設定を書き込む必要があります。 Side-by-Side 共有のシナリオを実行中に、 アセンブリの状態を適切に格納および処理するように、 Side-by-Side アセンブリをデザインします。

  5. レジストリに保存されたすべてのアセンブリ状態情報は、 他のバージョンのアセンブリとは分離される必要があります。 レジストリに格納される状態設定は、 レジストリ内のバージョンごとの個別のセクションに保存される必要があります。 このことは、レジストリの HKLM 部分と HKCU 部分の両方に必要になります。 たとえば、バージョン XXXX のアセンブリの HKCU 状態設定は、 次のレジストリ キーの下に格納します。

    HKCU\MyCompany\MyComponent\VersionXXXX\ 
    
  6. 共有アセンブリがレジストリに格納するすべての状態情報は、 レジストリのバージョンごとの個別のセクションに保存される必要があります。 たとえば、EnableSuperCoolFeature という状態設定は TRUE または FALSE の値を持つことができます。 共有 Side-by-Side アセンブリの値は次のように格納します。

    HKEY_CurrentUser\Software\MyCompany\MyComponent\
    Version01.01\EnableSuperCoolFeature = TRUE
    
  7. レジストリに格納される共有状態設定を、 実行するアセンブリ コンテキストに対してプライベートにします。 GetModuleFileName 関数を使用して仮想ルートをセットアップできます。 仮想ルートのセットアップは、HKLM 分岐と HKCU 分岐に対して行う必要があります。

  8. 理想的には、 アプリケーションが状態を保存する役目を担い、 レジストリを変更しないような保存モデルをサポートする必要があります。 アプリケーションが、コンポーネントのレジストリ エントリを直接処理する必要がないようにすべきです。 代わりに、アセンブリは Side-by-Side 互換である設定を保存まはた復元する API 関数を提供する必要があります。

  9. アセンブリがグローバル状態と相互作用できるように、 アセンブリがレジストリ以外の記憶域に状態設定を保存することがあります。 Side-by-Side アセンブリは、以下の Side-by-Side 互換記憶域を使用することがあります。

    • 保護された記憶域または pstore
    • WinInet キャッシュ
    • Microsoft SQL Server

手順 3: ローカリゼーション方針の検討

ユーザー インターフェイスを備えたコンポーネントを作成している場合、 アセンブリ DLL のリソースを含む、 リソースのみのサテライト DLL を検討することがあります。 リソースのみのサテライト DLL の作成の詳細については、 MSDN のインターナショナル ソフトウェア ページおよびその関連ページを参照してください。

このようなリソース DLL を Side-by-Side アセンブリとして提供し、 アセンブリ マニフェストを使って記述する必要があります。

手順 4: アセンブリ マニフェストの作成

アセンブリはマニフェストを使って記述されるので、 各アセンブリは関連付けられたマニフェスト ファイルを持つ必要があります。 マニフェストは以下の機能を持つ XML ファイルです。

  • アセンブリを一意に識別します。
  • アセンブリに含められるファイルを記述します。
  • Win32 COM アクティベーション情報を記述します。 従来 COM クラス、インターフェイス、およびタイプ ライブラリは、 レジストリに格納されていました。
  • ウィンドウ クラスを記述します。
  • 他の固有のバージョンの Side-by-Side アセンブリの依存関係を指定します。

Side-by-Side アセンブリはシステムには登録されませんが、 マニフェスト ファイルで依存関係を指定するシステム上のアプリケーションまたは他のアセンブリから使用できます。 オペレーティング システムがマニフェストを認識し、 アセンブリのインストール時および実行時にマニフェスト内のメタデータを使用します。

CreateProcess が呼び出されるときに、 マニフェストの存在を確認します。 DLL ローダーは、マニフェスト内のメタデータを使用して、 DLL の読み込みをアセンブリ内に含まれる特定のバージョンにリダイレクトします。 COM サーバー データを使用して、 マニフェストに基づく COM オブジェクトのインスタンスを作成するので、 ウィンドウ クラスにはアセンブリごとにバージョンが設定されることになるでしょう。

メモ帳や標準の XML エディタを使って、マニフェストを作成できます。 Platform SDK で、マニフェストの作成を支援するツールが利用できるようになる予定です。

マニフェストをリソースとしてプライベート アセンブリに含めることもあります。 共有アセンブリの場合は、マニフェストを個別のファイルにする必要があります。

プライベート アセンブリの場合は、 アセンブリ名を assemblyIdentity で指定した名前と同じにする必要があります。 たとえば、次の例を考えます。

<assemblyIdentity
          type = "win32"
          name = "Microsoft.Windows.SampleAssembly"
          version = "2.0.0.0"
          processorArchitecture = "x86" />
<file name = "foo.dll">

この例では、 assemblyIdentity の名前が "Microsoft.Windows.SampleAssembly" なので、 アセンブリ名は "Microsoft.Windows.SampleAssembly" になります。

共有アセンブリ マニフェストでも同様のアプローチを使用できます。 また、任意のファイル名を使用することもできます。 オペレーティング システムがインストール時に自動的にマニフェストの名前を変更します。 変更する名前は assemblyIdentity に基づいて生成されます。

assemblyIdentity

各アセンブリは、アセンブリ マニフェスト内に記述された一意の assemblyIdentity を持ちます。 上記のアプリケーション マニフェストの作成で assemblyIdentity を説明したのと同様に、 アセンブリ マニフェストに記述される assemblyIdentity は、 アセンブリの名前、バージョン、アセンブリの種類、 およびアセンブリを実行する予定のプロセッサ アーキテクチャを識別します。

共有アセンブリをグローバルに共有する場合、 assemblyIdentity は publickeyToken を含む必要があります。 publickeyToken は、アセンブリに署名された公開キーの SHA-1 ハッシュの最後の 8 バイトを表す 16 文字の 16 進数文字列です。

アセンブリに対するすべての参照は assemblyIdentity を使って行われます。 アプリケーション開発者は、 参照として assemblyIdentity を使用することにより、 アプリケーションが使用すべきアセンブリの正確なバージョンを指定できます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleAssembly"
    type="win32"
    publickeyToken = "<共有アセンブリの場合は値を挿入します>"
/>

name: アセンブリに重複しない名前を付けます。 Organization.Division.Name の形式で名前を付けることをお勧めします。 たとえば、Microsoft.Windows.mysampleAssembly という名前を付けます。

type: アセンブリの種類を指定します。 値は win32 で、すべて小文字にする必要があります。 これは、Windows バイナリと、.NET ランタイムなどのその他のテクノロジを区別するために使用します。

processorArchitecture: 対象となるプラットフォームのプロセッサを指定します。 有効な値は、32 ビット版 Windows では x86、64 ビット版 Windows では ia64 です。

version: アセンブリのバージョンを指定します。 mmmmm.nnnnn.ooooo.ppppp のように、4 つの部分から構成されるバージョン書式を使用します。 各部分をピリオドで区切り、それぞれ 0 から 65535 までの値を指定できます。 ピリオドで区切られた各部分は、左から右に、メジャー番号、マイナー番号、ビルド番号、 およびリビジョン番号を表します。 アセンブリのバグ修正の番号付けの手法を採用します。 以前のバージョンと互換性のないバージョンの場合は、 アセンブリのメジャー番号またはマイナー番号を変更します。 アセンブリへの変更が旧バージョンとの互換性を持つ場合は、 ビルド番号部分およびリビジョン番号部分だけを変更します。 たとえば、 開発者はアセンブリ バージョン 1.0.0.0 のバグ修正バージョンをすべてバージョン番号 1.0.0.* で表すような番号付け手法を採用できます。

language: アセンブリの言語を識別します。 アセンブリが言語固有である場合、 DHTML 言語コードを指定します。 そのアセンブリが世界中で使用される (言語中立) 場合、 この属性を省略できます。

publickeyToken: アセンブリに署名される公開キーの SHA-1 ハッシュの最後の 8 バイトを表す 16 文字の 16 進数文字列です。 Pktextract.exe (Platform SDK で提供されるツール) を使用して、 公開キーの証明書ファイルから publickeytoken を抽出します。 これはアセンブリ カタログの署名に使用するのと同じキーである必要があり、 キーは少なくとも 2048 ビット以上である必要があります。

description 要素は省略可能で、アプリケーションの説明を含めます。 dependency セクションには、 ユーザーのアセンブリが使用する特定の Side-by-Side アセンブリの assemblyIdentity を含めます。

たとえば、アセンブリが Microsoft GDIPlus と Microsoft Common-Controls に依存関係を持つときは、 以下のようになります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="Microsoft.Windows.mysampleapp"
    type="win32"
publickeyToken = "<共有アセンブリの場合は値を挿入します>"
/>
<description>アプリケーションの説明をここに記述します。</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.GdiPlus"
            version="1.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls "
            version="6.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>

アセンブリに含まれるファイルをそれぞれ一覧する必要があります。 たとえば、次のように記述します。

    <file name="sampleu.dll"/>
    <file name="bar.dll"/>
       

**注   ** 共有アセンブリの場合は、 以下の手順 5 でファイルに署名するときに、 マニフェストがファイルのハッシュ (ファイルのハッシュを表す 16 進数値) と hashalg 値 (アッシュを作成するために使用するアルゴリズム) を使って更新されます。

ファイルごとに適切な COM データを含めます。 ファイルが COM クラスをエクスポートする場合は、 以下の属性を持つ comClass 要素も含めます。

  • description: クラス名。
  • clsid: クラスを一意に識別する GUID。
  • threadingModel: インプロセス COM クラスが使用するスレッディング モデル。
  • tlbid: この COM コンポーネント用のタイプ ライブラリの GUID。
  • progid: COM コンポーネントに関連付けられたバージョン依存のプログラム ID。

以下に例を示します。

    <file name="sampleu.dll">
        <comClass description="Font Property Page"
    clsid="{0BE35200-8F91-11CE-9DE3-00AA004BB851}"
          threadingModel = "Both"
             tlbid = "{44EC0535-400F-11D0-9DCD-00A0C90391D3}"/>
        <comClass description="Color Property Page"
    clsid="{0BE35201-8F91-11CE-9DE3-00AA004BB851}" 
    progid="ABC.Registrar"/>
    </file>
  

ファイルがタイプ ライブラリ情報を保持する場合は、 以下の属性を持つ typelib 要素を含めます。

  • tlbid: タイプ ライブラリの一意 ID。
  • version: タイプ ライブラリの 2 つの部分で構成されるバージョン番号。
  • helpdir: タイプ ライブラリ内の型についてのヘルプ ファイルが格納されているディレクトリ。
  • resourceid: ロケール ID (LCID) を表す 16 進数文字列。
  • flags: このタイプ ライブラリのタイプ ライブラリ フラグの 16 進数表記。 この値は LIBFLAGS 列挙値で、 ICreateTypeLib::SetLibFlags への uLibFlags パラメータで指定するのと同じフラグです。 これらのフラグには、先頭に 1 つ以上の 0 を付けたり、 0x プレフィックスを付けることはできません。 たとえば、以下のようになります。
   <file name="sampleu.dll">
       <typelib tlbid="{44EC0535-400F-11D0-9DCD-00A0C90391D3}"
       version="1.0" helpdir=""/>
    </file>

IDispatch から派生したインターフェイスなど、 大部分のオートメーション インターフェイスの場合は、 既定のプロキシ スタブ実装が適切です。 ただし、その他すべての外部プロキシ スタブインターフェイスのようなインターフェイス プロキシ スタブは、 最上位レベルの <assembly> タグの下の <comInterfaceExternalProxyStub> 要素に一覧する必要があります。

アセンブリ内のファイルがプロキシ スタブを実装する場合は、 対応する <file> タグが <comInterfaceExternalProxyStub> に対する属性と同じ属性を持つ <comInterfaceProxyStub> 子要素を保持する必要があります。 コンポーネントの comInterfaceExternalProxyStub 依存関係の一部を省略すると、 プロセスとスレッド間のマーシャリング インターフェイスが期待どおりに機能しない場合があります。

属性は以下のようになります。

  • iid: プロキシが宣言されるインターフェイスの IID。
  • proxyStubClsid32: プロキシの CLSID。 これを省略すると、IID を CLSID として使用します。 値は "{clsid}" の形式である必要があります。
  • file: プロキシを実装する外部ファイルへのパス。 この属性は省略可能です。 Windows ディレクトリ内のパスを参照するために環境変数を使用します。 例: "%SystemRoot%\system32\oleaut32.dll"。
  • baseInterface: iid 属性によって記述されるインターフェイスの派生元のインターフェイスの IID。
  • numMethods: インターフェイスが実装するメソッドの数。
  • name: コード内に現れるときのインターフェイスの名前。 たとえば "IViewObject" など。 これはインターフェイスの説明になるような文字列にすべきではありません。
  • tlbid: iid 属性が指定するインターフェイスの説明を保持するタイプ ライブラリ。

以下に例を示します。

<comInterfaceExternalProxyStub 
  name="IAxWinAmbientDispatch" 
    iid="{B6EA2051-048A-11D1-82B9-00C04FB9942E}"
      proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    numMethods="35" 
  baseInterface="{00000000-0000-0000-C000-000000000046}"/>

windowClass

windowClass はバージョンが付けられるウィンドウ クラスの名前です。

<file name="comctl32.dll">
        <windowClass>ToolbarWindow32</windowClass>
        <windowClass>ComboBoxEx32</windowClass>
        <windowClass>msctls_trackbar32</windowClass>
       

リソースのみのサテライト DLL を提供している場合 (グローバライゼーション用に言語依存のリソースを持つ場合にもっとも役に立ちます)、 サテライト アセンブリ (DLL も含みます) として提供し、 マニフェストを使って記述します。

assemblyIdentity はメイン アセンブリ マニフェストと似たものになります。 違いは以下のとおりです。

  • name: ベース アセンブリと同じ名前にし、"Resources" を付加する必要があります。 たとえば、ベース アセンブリ内の名前が "Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries" である場合、 リソース アセンブリの名前は "Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources" になるでしょう。
  • language: リソース アセンブリの言語を識別します。
  • file: リソース DLL ファイルの一覧を含めます。

以下は、Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries リソース アセンブリのマニフェストの例です。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity
        type="win32"
        name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources"
        version="6.0.0.0"
        processorArchitecture="X86"
        language="DE"
        publicKeyToken="6595b64144ccf1df"
        />
    <file name="mfc42deu.dll"/>
</assembly>

ベース アセンブリはリソース ファイルとの依存関係を記述します。 この記述は省略可能です。 したがって次の例では、 ユーザーがロケールとしてドイツ語を指定する Windows を実行している場合、 Microsoft.Tools.VisualCPlusPlus アセンブリを使用するアプリケーションのテキストはドイツ語で表示されます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32" 
    name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries"
    version="6.0.0.0" processorArchitecture="x86"
    publicKeyToken="6595b64144ccf1df"/>

    <dependency optional="yes">
        <dependentAssembly>
            <assemblyIdentity type="win32" 
    name="Microsoft.Tools.VisualCPlusPlus.Runtime-Libraries.Resources" 
    version="6.0.0.0" processorArchitecture="x86" 
    publicKeyToken="6595b64144ccf1df" language="*"/>
        </dependentAssembly>
    </dependency>

**注   ** アセンブリ スキーマについては、Platform SDK を参照してください。

手順 5: ユーザーの証明書でのアセンブリへの署名

**注   ** この手順は、共有アセンブリのみに適用されます。

アセンブリの署名に使用する証明書は、 少なくとも 2048 ビット以上の長さを持つ必要があります。 アセンブリに署名することにより、 アセンブリの整合性を確認するためにアセンブリの周囲にセキュアなエンベロープが生成されます。 オペレーティング システムは、 共有アセンブリをグローバル Side-by-Side ストア (WinSXS) にインストールする前にアセンブリの署名を確認します。 assemblyIdentity 内の publicKeyToken が、 署名に使用した証明書の公開キーと一致する必要があります。 これにより、発行元になりすますことが困難になります。

大部分の作業が、 Platform SDK からの Mt.exe、および CryptoAPI SDK からの MakeCat.exe と signcode.exe という 3 つのツールを使って行われます。 Mt.exe には、 アセンブリ内のファイルごとにハッシュを生成することと、 結果値と使用するアルゴリズムの両方を含めるように対応する <file> タグを更新するという 2 つの主な役割があります。 Mt.exe はカタログ記述ファイル (.cdf) を作成します。 MakeCat.exe がこのファイルを使用して、 アセンブリ マニフェストに基づくセキュリティ カタログを作成します。

以下に、Mt.exe を実行する前の候補マニフェスト ファイルのコンテンツの例を示します。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity 
        type="win32" 
        name="Microsoft.Windows.MySampleAssembly" 
        version="1.0.0.0" 
        processorArchitecture="x86"         
        publicKeyToken="6b95b9abf345378f"/>
    <file name="myfile.dll"/>
</assembly>

publicKeyToken 値が既に含まれていることに注意してください。 assemblyIdentity は完成しており、 アセンブリはファイル myfile.dll を 1 つだけ保持しています。 ツールが正しく機能するためには、 アセンブリ ファイルが作業対象のマニフェストと同じディレクトリに存在する必要があります。 Mt.exe を実行する既定の手法は以下のようになります (詳細については、Platform SDK を参照してください)。

c:\ MySampleAssembly>mt.exe 
        -manifest MySampleAssembly.manifest -hashupdate -makecdfs
Microsoft (R) Side-By-Side Manifest Tool 1.0.0.0
Copyright (C) Microsoft Corporation 2000-2001.  All Rights Reserved.

Mt.exe を実行した後の同じマニフェストは以下のようになります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity 
        type="win32" 
        name=" Microsoft.Windows.MySampleAssembly" 
        version="1.0.0.0" 
        processorArchitecture="x86"         
        publicKeyToken="6b95b9abf345378f"/>
    <file name="myfile.dll"
hash="a1d362d6278557bbe965a684ac7adb4e57427a29" hashalg="SHA1"/>
</assembly>

ファイルの SHA-1 ハッシュが追加されていることに気付きます。 この値は変更しないでください。 "-makecdfs" オプションはファイルを更新すること以外に、 マニフェストを検証するセキュリティ カタログのコンテンツを記述する "MySampleAssembly.manifest.cdf" というファイルを生成します。

次の手順は生成した .CDF で MakeCat.exe (CryptoAPI で利用できます) を以下のように実行して、 アセンブリのセキュリティ カタログを作成することです。

c:\MySampleAssembly>makecat MySampleAssembly.manifest.cdf
Succeeded
c:\MySampleAssembly>

最後の手順は Signcode.exe を使って、 公開キー トークンの生成に使用した証明書でカタログ ファイルに暗号化した署名を行うことです。

**注   ** Signcode.exe は "ソフトウェア発行元証明書" でのみ機能します。 .SPC ファイルの詳細および証明書ファイルからの .SPC ファイルの生成方法については、 CryptoAPI SDK を参照してください。

c:\MySampleAssembly>signcode -spc mycompany.spc -v mycompany.pvk -i 
http://www.mycompany.com/MySampleAssembly -t 
http://timestamp.verisign.com/scripts/timstamp.dll 
MySampleAssembly.cat
Succeeded.

c:\MySampleAssembly>

これら 3 つの手順で、 アセンブリ マニフェストを構成する署名付きアセンブリ、 その検証用カタログ、およびアセンブリ ファイルを生成できます。 アセンブリの生成後にマニフェストのコンテンツを変更すると、 カタログとアセンブリが無効になるでしょう。 カタログ ファイルの作成、署名後にマニフェストを更新する必要がある場合は、 上記の手順を再実行して再度マニフェストを更新する必要があります。 同様に、アセンブリのアセンブリ ファイルを変更する場合は、 上記の手順を再実行する必要があります。

手順 6: アセンブリのインストール方法の決定

共有アセンブリは Windows インストーラ マージ モジュールを使ってインストールする必要があります (オペレーティング システムのインストールの一部としてインストールされない場合)。 共有アセンブリをインストールするには、Windows インストーラ バージョン 2.0 以降が必要になります。 (詳細については、Windows Installer SDK を参照してください。) アセンブリは Windows フォルダ内の新しいフォルダ、Winsxs フォルダにインストールされます。 コンピュータごとにこのフォルダにインストールされるファイルは、 Windows ファイル保護で保護されます。

プライベート アセンブリは任意の有効なインストーラを使ってインストールされます。 プライベート アセンブリはアプリケーションに対してプライベートにインストールする必要があります。

手順 7: テスト

アセンブリが期待どおりにインストールされ、使用できるようになったことを検証します。 たとえば、共有アセンブリを作成し、配置した場合は、 これが使用されるべきバージョンになります。 Depends.exe (Platform SDK で利用できます) などのツールや任意のデバッガを使って、 アセンブリが読み込まれる場所を確認します。

Side-by-Side アセンブリを作成するガイドラインを見直し、 アセンブリが Side-by-Side アセンブリとして適切に動作していることをテストします。 たとえば、アプリケーションで 2 つのインスタンスを起動し、 有効な Side-by-Side アセンブリであることを保証するシナリオを使ってそれぞれのコンポーネントをテストします。 つまり、 複数のバージョンを同時に実行して互いに競合しないことを確認します。

イベント ログを使用して、障害の原因を追跡します。

アセンブリ マニフェストとアセンブリ スキーマに一貫性があることを検証し、 アセンブリ マニフェストが整形式の XML であることを検証します。 これには Platform SDK で提供されるマニフェスト検証ツール Manifestchk.vbs を使用します。

共有アセンブリとアプリケーションへのサービスの提供

Windows XP 構成は DLL の静的リンクとは異なり、 アセンブリの発行元、 アプリケーションの発行元、および管理者が配置後に Side-by-Side アセンブリの依存関係を変更することを可能にします。 Windows XP 構成は、 アセンブリに安全にサービスを提供し、アプリケーションがどの Side-by-Side アセンブリを使用するかを安全に管理するためのモデルを提供します。

アプリケーション マニフェストは、 アプリケーションと特定のバージョンの Side-by-Side アセンブリとの依存関係を記述します。 アセンブリ マニフェストは、 アセンブリと特定のバージョンの Side-by-Side アセンブリとの依存関係を記述します。

オペレーティング システムはアプリケーションを実行するときに、 アプリケーション マニフェストから依存関係チェーンを調べ始め、 依存しているそれぞれのアセンブリ マニフェストを調査することにより、 アプリケーションが必要とするアセンブリを判断します。 アセンブリ マニフェストとアプリケーション マニフェストで指定するアセンブリ バージョンは、 アプリケーションがアクティベーション時に使用すべき既定の構成を備えています。 図 3 はこの構成階層を示しています。

図 3. 構成階層

オペレーティング システムは、 アプリケーションが必要とするバージョンのアセンブリを検索しようとします。 既定では、各アプリケーションがビルド時およびテスト時に使用したバージョンのアセンブリを要求し、それを受け取ります。 アセンブリの正確なバージョンが必要になります。正確にバージョンが一致しないと読み込みに失敗します。

アプリケーションおよび管理者は、 発行元構成またはアプリケーションごとの構成をインストールすることによって、 既定のアセンブリ構成に優先指定できます。

たとえば、読者がアセンブリの発行元で、 アセンブリにセキュリティ上の重大な欠陥を見つけたとしましょう。 読者は使用可能な新しいバージョンのアセンブリを作成し、 すべてのアプリケーションが新しいバージョンを使って開始することを希望します。 新しいバージョンに対してすべてのアプリケーションの再コンパイルを求めずに、 つまり、ユーザーにアプリケーションの再インストール プロセスを実行させずに、 新しいバージョンのアセンブリを発行できます。 同時に、新しいバージョンを使用するようにすべてのアプリケーションをリダイレクトするための発行元構成を発行できます。 発行元構成ファイルは、 アプリケーション マニフェストおよびアセンブリ マニフェストが指定する既定の構成に優先されます。

一般的に、発行元構成はアセンブリ アップデートを含む Service Pack のインストール時にインストールされることになります。 たとえば、 管理者は既定でバージョン 1.0.0.0 のアセンブリを使用するシステム上のすべてのアプリケーションやアセンブリを、 代わりにバージョン 1.1.0.0 を使用するようにリダイレクトして、 アセンブリ アップデートを配置できます。 この場合、 新しいバージョンのアセンブリは旧バージョンとの互換性を持つ必要があります。

図 4. アセンブリにサービスを提供する例

既定で、 あるバージョンの Side-by-Side アセンブリに依存関係を持つすべてのアプリケーションやアセンブリを、 指定した別のバージョンを使用するようにリダイレクトする特定のアセンブリに対して発行元構成を発行する発行元の場合を見てみましょう。 アプリケーションが依存する以前のバージョンのアセンブリにバグが存在する場合、 アプリケーションの作成者またはネットワーク管理者には、 アプリケーションごとに既定の構成および発行元構成に優先させる選択肢があります。 このためには、 特定のアプリケーションの依存関係をあるバージョンの Side-by-Side アセンブリから、 指定した別のバージョンのアセンブリにリダイレクトします。 発行元が互換性のないバージョンにリダイレクトするような事象、 またはアプリケーションが古いバージョンでのみ機能する場合は、 アプリケーション構成がアプリケーションに発行元構成に優先させる方法を提供するときに、 アプリケーション構成を発行することは例外的な方法になるはずです。

**注   ** 発行元構成は共有アセンブリのみに適用されます。

発行元構成の提供

発行元構成を提供するには、4 つの手順が必要になります。

  1. 新しいバージョンの Side-by-Side アセンブリを作成します。
  2. 発行元構成マニフェストを作成します。
  3. 発行元構成に署名します。
  4. インストール パッケージとして提供します。

手順 1: 新しいバージョンの Side-by-Side アセンブリの作成

上記の「アセンブリの作成」で概説した Side-by-Side アセンブリを作成する手順に従って処理を始めます。 新しいアセンブリを作成後、 旧バージョンとの互換性を確認するテストを行います。

次に、アセンブリのバージョン番号を変更します。 アセンブリに対して旧バージョンとの互換性を持った変更を行う場合は、 ビルド番号とリビジョン番号だけを変更することをお勧めします。

最後に、 以前のバージョンのアセンブリに使用したのと同じ証明書で署名します。

手順 2: 発行元構成マニフェストの作成

発行元構成は、 アセンブリごとにリダイレクトすべきバージョンのアセンブリを記述します。 この場合、マニフェストは以下の機能を持つ XML ファイルです。

  • リダイレクトするアセンブリを一意に識別します。
  • リダイレクトするバージョンまたはバージョンの範囲を記述します。
  • WinSxS の Policies フォルダにアセンブリとしてインストールされ、管理されます。 発行元構成ファイルは、 このフォルダにインストールされるときに名前が変更されます (名前はアセンブリ ID に基づいて変更されます)。

メモ帳や標準の XML エディタを使って、マニフェストを作成できます。 任意の有効な名前を持つファイルに保存します。

以下の発行元構成ファイルの例は、 バインディングを Microsoft.Windows.SampleAssembly のバージョン 2.0.0.0 からバージョン 2.0.1.0 にリダイレクトします。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity type="win32-policy" publicKeyToken="75e377300ab7b886"
   name="policy.2.0.Microsoft.Windows.SampleAssembly" version="2.1.0.0"
   processorArchitecture="x86"/>
   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32"
   name="Microsoft.Windows.SampleAssembly"  processorArchitecture="x86"
   publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.0.1.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

assemblyIdentity

複数の発行元構成アセンブリがシステム上に存在することがあります。 オペレーティング システムはアプリケーション マニフェストに基づいて適切なバージョンを選択します。 各発行元構成アセンブリは一意な assemblyIdentity を持ちます。

name: アセンブリに重複しない名前を付けます。 policy.major.minor.name の形式に従う必要があります。 policy で始まり、メジャー バージョンとマイナー バージョンを続け、 リダイレクトされるアセンブリの名前を付加する必要があります。

メジャー バージョンとマイナー バージョンは、 影響を受けるアセンブリのバージョンを表します。 たとえば、x86 Microsoft.Windows.foo 6.0.0.0 インターナショナル版のアセンブリへのバインドに影響する場合は、 マニフェスト内の名前は次のようになるでしょう。

   <assemblyIdentity 
     type="win32-policy" 
     publicKeyToken="75e377300ab7b886" 
     name="policy.6.0.Microsoft.Windows.Foo" 
     version="2.1.0.0" 
     processorArchitecture="x86"/>

繰り返しになりますが、 type はアセンブリの種類を指定します。 値は win32-policy である必要があり、すべて小文字にする必要があります。 publickeyToken、version、language、および processorArchitecture は、 以前に説明したのと同じ規則に従います。

dependency セクションには、 構成を適用する特定の Side-by-Side アセンブリの assemblyIdentity を含めます。 この場合、assemblyIdentity にはバージョン番号を含めないことに注意してください。

   <dependency>
      <dependentAssembly>
         <assemblyIdentity 
type="win32" 
name="Microsoft.Windows.SampleAssembly"  
processorArchitecture="x86"
publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.1.0.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

bindingRedirect

oldVersion: 優先指定され、 リダイレクトされるアセンブリのバージョンを指定します。 単一のバージョンまたはバージョンの範囲を指定できます。 バージョンの範囲はハイフンで連結して指定します。 その際、スペースを含めません。 たとえば、2.14.3.0 または 2.14.3.0?2.16.0.0 のように指定します。

newVersion: 置き換え用アセンブリのバージョンを指定します。

どちらのバージョンもアセンブリに適用する 4 部構成のバージョン構文 nnnnn.nnnnn.nnnnn.nnnnn の記法に従う必要があります。

手順 3: 発行元構成への署名

共有アセンブリの署名に適用したのと同じ手順に従い、 アセンブリに使用したのと同じ証明書を使用します。

手順 4: インストール パッケージとして提供

Windows インストーラ パッケージまたはオペレーティング システム インストール (たとえば、Windows Update または OS Service Pack) のいずれかとしてインストールを提供します。

発行元構成がシステムにグローバルに影響を及ぼす場合は、 発行元構成をマージ モジュールとして提供してはいけません。 代わりに、発行元構成を Windows インストーラ MSI として提供できます。

一般的に、Side-by-Side アセンブリの Microsoft(R) 発行元構成は、 オペレーティング システムの Service Pack または Windows Update の QFE としてインストールされます。 アセンブリは WinSxS フォルダ内の Policies フォルダにインストールされます。 Windows ファイル保護は、 コンピュータごとにこのフォルダにインストールされるファイルを保護します。

アプリケーション構成の提供

アプリケーション構成を提供するには、2 つの手順が必要になります。

  1. アプリケーション構成マニフェストを作成します。
  2. インストール パッケージとして提供します。

手順 1: アプリケーション構成マニフェストの作成

アプリケーション構成は、 発行元構成およびアプリケーションごとの既定のアプリケーション構成に優先します。 マニフェストは以下の機能を持つ XML ファイルです。

  • リダイレクトするアセンブリを一意に識別します。
  • リダイレクトするバージョンまたはバージョンの範囲を記述します。
  • アプリケーション フォルダにインストールされます。

以下のアプリケーション構成ファイルの例は、 バインディングを Microsoft.Windows.SampleAssembly のバージョン 2.1.0.0 からバージョン 2.0.0.0 にリダイレクトします。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
   <windows>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <assemblyIdentity  
processorArchitecture="X86" 
name="Microsoft.Windows.mysampleapp" 
type="win32" 
version="1.0.0.0"/>
         <dependentAssembly>
 <assemblyIdentity 
type="win32" 
processorArchitecture="x86" 
name="Microsoft.Windows.SampleAssembly"
   publicKeyToken="75e377300ab7b886"/>
    <bindingRedirect oldVersion="2.1.0.0" newVersion="2.0.0.0"/>
         </dependentAssembly>
      </assemblyBinding>
   </windows>
</configuration>

構成

windows: リダイレクションを Win32 アセンブリに適用することを識別します。 これは、今後アプリケーションが Win32 と .Net アセンブリで構成されるときに、 Win32 と .Net アセンブリの両方が同じアプリケーション構成マニフェストを使用できるようにデザインされています。 .NET アセンブリは <runtime> 要素の下に含まれることになるでしょう。

assemblyBinding を使用して、 アセンブリ構成リダイレクトを含めます。

assemblyIdentity はアプリケーションを一意に識別し、 アプリケーション構成と一致する必要があります。

dependency セクションには、 アプリケーションの優先構成を適用する特定の Side-by-Side アセンブリの assemblyIdentity を含めます。 この場合、assemblyIdentity にはバージョン番号を含めないことに注意してください。

以下に例を示します。

   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32" 
name="Microsoft.Windows.SampleAssembly"  
processorArchitecture="x86" 
publicKeyToken="75e377300ab7b886"/>
         <bindingRedirect oldVersion="2.0.0.0" newVersion="2.1.0.0"/>
      </dependentAssembly>
   </dependency>
</assembly>

bindingRedirect

この属性の指定は、 発行元構成で指定された属性と一貫性があります。

手順 2: インストール パッケージとして提供

アプリケーション構成は、 実行ファイルと同じフォルダにインストールされます。 アプリケーション構成ファイルの名前は、 この構成を適用するアプリケーションの実行ファイルの名前に .config を付けます。 たとえば、example.exe のアプリケーション構成ファイルは "example.exe.config" になります。

プライベート アセンブリを使用する既存のアプリケーションの修正

シナリオ: 最新バージョンのオペレーティング システムにアップグレードしました。 最新バージョンのオペレーティング システムに同梱される MSVCRT.DLL のアップデート バージョンは旧バージョンと互換性がないので、 MSVCRT.DLL を使用するアプリケーションが機能しなくなりました。 MSVCRT.DLL は Windows 保護ファイルなので、 システム バージョンを置き換えるという選択肢はありません。

新しいバージョンの MSVCRT を使って機能するようにアプリケーションを書き直すのではなく、 MSVCRT のプライベート アセンブリを作成し、 それをアプリケーション フォルダにインストールして、 アプリケーションをテストできます。

**注   ** すべての共有コンポーネントがプライベート Side-by-Side アセンブリの候補として適しているわけではありません。 一部のコンポーネントはそのコンポーネントをインストールすることに関してライセンスが制限されている場合があります。 コンポーネントが Side-by-Side コンポーネントの条件を満たす必要があります。 使用できるアセンブリを提供しているかどうか、 コンポーネントの発行元に問い合わせてください。

Windows XP は、 この DLL のプライベート アセンブリ バージョンを作成し、 アプリケーションで使用できるようにすることを許可しています。 プライベート アセンブリは、 以下のように msvcrt.dll と msvcirt.dll の両方を含むことになります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity type="win32"
      name="Microsoft.Windows.PrivateCPlusPlusRuntime" 
version="6.0.0.0" 
processorArchitecture="x86"/>
    <file name="msvcrt.dll"/>
    <file name="msvcirt.dll"/>
</assembly>

アセンブリ マニフェストとアプリケーション マニフェストの両方のファイルは、 アプリケーションの実行ファイルと同じフォルダにインストールされるでしょう。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
<assemblyIdentity 
    version="1.0.0.0" 
    processorArchitecture="x86" 
    name="APPLICATION" 
    type="win32" 
/> 
<description>アプリケーションの説明</description> 
<dependency> 
    <dependentAssembly> 
       <assemblyIdentity 
           type="win32"
           name="Microsoft.Windows.PrivateCPlusPlusRuntime" 
           version="6.0.0.0" 
           processorArchitecture="x86"/> 
    </dependentAssembly> 
</dependency> 
</assembly>

アプリケーションが実行されるときに、 アプリケーション マニフェストを調べ、 アプリケーションに対してプライベートなバージョンの MSVCRT を読み込みます。

まとめ

Windows XP は、 アプリケーションやコンポーネントが安全に Win32 アセンブリを共有し、 実行時に使用するアセンブリのバージョンを記述する標準化された手段を提供します。 最も重要なことは、オペレーティング システムがこれを行うことです。 それでも、開発者にとっては システムへの変更の影響を受けないアプリケーションをユーザーに提供する工程は時間がかかり、 労力を要する作業になるでしょう。 すべての共有システム コンポーネントを併用して同時に実行することはできませんが (たとえば、カーネルは 2 つのバージョンを持つことはできません)、 多くのコンポーネント作成者は異なるバージョンのアセンブリを管理するための Windows XP サポートを利用して、 コードのバージョン管理に関する非常に多くの問題点に対処する選択肢を持つようになりました。 今後の資料では、 インフラストラクチャが機能する方法、 ローダーがアセンブリやマニフェスト メタデータを使用する方法、 .Net Framework との統合、およびテストの方針などを調査していきたいと考えています。

付録: 共有コンポーネントを提供すべきでしょうか ?

すべての共有コンポーネントが Side-by-Side アセンブリの候補として適しているわけではありません。 共有コンポーネントのプロバイダが、 提供するコンポーネントが Side-by-Side アセンブリの候補として適しているかを判断するときに使用できるいくつかの考慮点を示します。

  1. コンポーネントはいくつかのアプリケーションが使用することを予定する豊富なセットの API を公開しますか ? (たとえば、コンポーネントは MSHTML のようなものですか。)
  2. 既にいくつかのアプリケーションがコンポーネントを使用していますか ? (たとえば、コンポーネントは COMCTL32 のようなものですか。)
  3. コンポーネントはユーザー モード コンポーネントですか (つまり、デバイス ドライバではありませんか) ?
  4. コンポーネントは Windows XP 向けに新しく作成したものですか ?
  5. コンポーネントはアプリケーション間の通信を調整しますか ? Side-by-Side アセンブリの候補にするには、 これを行うべきではありません。 たとえば、通信を調整する OLE32 の一部はシステム上に 2 つのバージョンを存在させたくないので、 適切な Side-by-Side の候補にはなりません。
  6. コンポーネントにはシステムの物理デバイスや仮想デバイスを管理する役割がありますか ? (たとえば、コンポーネントはプリンタ スプーラのようなものですか。) Side-by-Side アセンブリの候補にするには、 これような役割を持つべきではありません。

質問 1 から 4 までに「はい」と答え、 質問 5 と 6 に「いいえ」と答えたコンポーネント プロバイダは、 そのコンポーネントを共有 Side-by-Side アセンブリとして提供することをお勧めします。 このような条件を満たさないコンポーネントは、 その一部を Side-by-Side にできるように再設計することになる可能性があります。

謝辞

貴重な支援を提供してくれた Windows XP チームのメンバと、 この資料の原案の準備を助けてくれた Gary Spradling に深く感謝します。