Share via


COM コンポーネントのRegistration-Freeアクティブ化: チュートリアル

 

Steve White
開発者向けの Premier サポート(Microsoft UK)

レスリー ミュラー
グローバルIT研究開発 & 、クレディスイスファーストボストン

2005 年 7 月

概要: Microsoft Platform SDK は、 分離されたアプリケーションとサイド バイ サイド アセンブリのトピックを文書化する優れた作業を行います。 ただし、すべてのユーザーがこのトピックを COM コンポーネントの登録不要のアクティブ化と同一視しているわけではありません。 登録不要の COM は、ロックダウンされたサーバーとアプリケーションが共有インフラストラクチャ上に分離されている企業にとって非常に興味深いプラットフォーム機能です。 この記事では、ネイティブ クライアントによるネイティブ COM コンポーネントの登録不要なアクティブ化と、マネージド クライアントによる COM 相互運用機能の使用例について説明します。 (18ページ印刷)

適用対象:
   Microsoft Windows Server 2003
   Microsoft Windows XP
   Microsoft .NET Framework バージョン 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Visual Studio 6.0

この記事に付属するサンプルをダウンロード します。MSDNRegFreeCOM.msi

内容

はじめに
Registration-Free COM の用語
サンプルの実行
Visual C++ COM サーバーのビルド
C++/.NET クライアントのビルド
Registration-Free アクティブ化
Visual Basic 6.0 COM サーバーとクライアント
互換性のないアパートメント
アクティブ化コンテキスト API の使用
トラブルシューティング
まとめ
もっと読む

はじめに

登録不要 COM は、Microsoft Windows XP (SP2 for .NET ベースのコンポーネント) と Microsoft Windows Server 2003 プラットフォーム。 名前が示すように、メカニズムを使用すると、COM コンポーネントを登録しなくても、コンピューターに COM コンポーネントを簡単に (たとえば XCOPY を使用して) 展開できます。

ターゲット プラットフォームでは、プロセスとその依存モジュールを初期化する段階の 1 つは、関連付けられている マニフェスト ファイルアクティブ化コンテキストと呼ばれるメモリ構造に読み込みます。 対応するレジストリ エントリがない場合は、COM の実行時に必要なバインディングとアクティブ化の情報を提供するアクティブ化コンテキストです。 アクティブ 化コンテキスト API を使用してアクティブ化コンテキストを自分で作成してファイルの使用を無効にしない限り、COM サーバーまたはクライアントで特別なコードは必要ありません。

このチュートリアルでは、単純なネイティブ COM コンポーネントを構築し、ネイティブ クライアントとマネージド クライアントから登録と登録解除の両方で使用します。 コンポーネントとネイティブ クライアントは、Visual C++ と Visual Basic 6.0 の両方で表示されます。マネージド クライアントは、C# と Visual Basic .NET の両方で表示されます。 ソース コードとサンプルをダウンロードしてすぐに動作を確認することも、チュートリアルに従ってステップ バイ ステップでビルドすることもできます。

Registration-Free COM の用語

.NET Frameworkテクノロジに精通しているユーザーは、アセンブリという用語に慣れ、1 つのモジュールとしてデプロイ、名前付け、バージョン管理される 1 つ以上のモジュールのセットを表し、1 つのモジュールにはセットを定義するマニフェストが含まれます。 登録不要の COM では、 アセンブリマニフェスト という用語は、概念的には似ていますが、.NET に対応するアイデアと同じではないアイデアに対して借用されます。

登録不要の COM では 、アセンブリ を使用して、1 つのユニットとしてデプロイ、名前付け、バージョン管理された 1 つ以上の PE モジュール (つまり、ネイティブ または マネージド) のセットを意味します。 登録不要の COM では 、マニフェスト を使用して、XML を含む .manifest 拡張子を持つテキスト ファイルを参照します。これは、 アセンブリ の ID (アセンブリ マニフェスト) とそのクラスのバインドとアクティブ化の詳細を定義するか、 アプリケーション (アプリケーション マニフェスト) の ID を 1 つ以上のアセンブリ ID 参照と共に定義します。 アセンブリ マニフェスト ファイルの名前はアセンブリに対して、アプリケーション マニフェスト ファイルの名前はアプリケーションに対して指定されます。

サイド バイ サイド (SxS) アセンブリ という用語は、マニフェスト ファイルを使用して同じ COM コンポーネントの異なるバージョンの構成を指し、登録する必要なく異なるスレッドで同時に読み込むことができます。 SxS は、 登録不要の COM を有効にし、ほぼ同義です。

サンプルの実行

サンプル コードをダウンロードして抽出すると、 \deployed という名前のフォルダーが見つかります。 ここでは、クライアント アプリケーションの Visual C++ バージョン (client.exe)、そのマニフェスト (client.exe.manifest)、COM サーバーの Visual C++ バージョン (SideBySide.dll) とそのマニフェスト (SideBySide.X.manifest) を示します。 先に進み 、client.exeを実行します。 予想される結果は 、client.exeSideBySideClass ( SideBySide.dllで実装) のインスタンスをアクティブ化し、 Version メソッドを呼び出した結果を表示します。これは"1.0.0-CPP" のようになります。

Visual C++ COM サーバーのビルド

手順 1

最初の手順は、COM サーバーを構築することです。 Visual Studio で新しい Visual C++ ATL プロジェクトを作成し、 SideBySide と呼びます。 ATL プロジェクト ウィザードの [アプリケーション設定] タブで、[属性付きチェック] ボックスの選択を解除し、[プロキシ/スタブ コードのマージを許可チェック] ボックスを選択します。

ソリューション エクスプローラーでプロジェクト ノードを右クリックし、追加 |クラスの追加....[ATL Simple Object]\(ATL シンプル オブジェクト\) を選択し、[開く] を選択します。 クラスに SideBySideClass という短い名前を付け、[完了] をクリック します

クラス ビューで、ISideBySideClass ノードを右クリックし、追加 |メソッドの追加....メソッドの追加ウィザードで、メソッド名として「Version」と入力し、BSTR* のパラメーターの種類を選択し、パラメーター名として「pVer」と入力し、[retval チェック] ボックスを選択して、[追加] をクリックし、[完了] をクリックします。

クラス ビューで、 CSideBySideClass ノードを展開し、 Version メソッドをダブルクリックします。 // TODO 行を次のように置き換えます。

*pVer = SysAllocString(L"1.0.0-CPP");

リリース ビルドを生成し、 \release\SideBySide.dllを \deployed にコピーします。

C++/.NET クライアントのビルド

次の手順では、クライアントをビルドします。 チュートリアルのこの部分では、Visual C++ または Visual C++ COM サーバー用の .NET クライアントをビルドするオプションがあります。 言うまでもなく、Visual C++、Visual Basic 6.0、.NET で記述されたクライアントとサーバーを混在させ、照合することができます。 これを行う場合は、サンプルが連携するように修正するのは簡単です。 クライアントとサーバーのセットは、そのまま動作するコードを提示するために、このチュートリアルのように編成されます。

手順 2 (オプション A: Visual C++)

SideBySide プロジェクトのフォルダーを基準とした兄弟フォルダーに、client という名前の新しい Visual C++ Win32 コンソール プロジェクトを作成します。 Win32 アプリケーション ウィザードの [アプリケーション設定] タブで、[ATL チェックのサポートの追加] ボックスをチェックします。

stdafx.h を編集し、 の直後のファイルの先頭に次の行を#pragma once追加します。

#define _WIN32_DCOM

stdafx.h でも、ファイルの下部に次の行を追加します。

#import "..\deployed\SideBySide.dll" no_namespace

client.cpp の内容を次のコードに置き換えます。

#include "stdafx.h"
#include <iostream>
using namespace std;

void ErrorDescription(HRESULT hr)
{
    TCHAR* szErrMsg;
    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
      FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
      (LPTSTR)&szErrMsg, 0, NULL) != 0)
   {
        cout << szErrMsg << endl;
        LocalFree(szErrMsg);
    }
   else
        cout << "Could not find a description for error 0x" 
          << hex << hr << dec << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
   CoInitializeEx(0, COINIT_MULTITHREADED);

   {
      ISideBySideClassPtr ptr;
      HRESULT hr = ptr.CreateInstance(__uuidof(SideBySideClass));
      if (SUCCEEDED(hr))
      {
         cout << ptr->Version() << endl;
      }
      ErrorDescription(hr);

      char c;
      cin >> c;
   }

   CoUninitialize();

   return 0;
}

リリース ビルドを生成し、 \release\client.exeを \deployed にコピーします。

手順 2 (オプション B: .NET Framework)

Visual Studio .NET 2003 では、SideBySide プロジェクトのフォルダーに対する相対的な兄弟フォルダーに、client という名前の新しい C# または Visual Basic .NET コンソール アプリケーションを作成します。 [参照の追加] ダイアログの [COM] タブから SideBySide 1.0 タイプ ライブラリへの参照を追加します。

Main メソッド内に次のコードを貼り付けます。

C# コード

   SideBySideLib.ISideBySideClass obj = 
        new SideBySideLib.SideBySideClassClass();
   Console.WriteLine(obj.Version());
   Console.ReadLine();

Visual Basic .NET コード

    Dim obj As SideBySideLib.ISideBySideClass = 
      New SideBySideLib.SideBySideClassClass
    Console.WriteLine(obj.Version())
    Console.ReadLine()

リリース ビルドを生成し、 client.exeを \deployed にコピーします。

手順 3.

現時点では、 \deployed フォルダーには、一部の中間ファイルとは別に、 client.exeSideBySide.dllのみが含まれている必要があり、後者はビルド プロセスによって登録されます。 このような通常の状況でサーバーとクライアントが連携していることをチェックするには、\deployed\client.exeを実行し、予想される出力 "1.0.0-CPP" をメモします。

手順 4

このチュートリアルでは 、登録不要 の COM について説明するため、 SideBySide アセンブリの登録を解除する必要があります。 コマンド プロンプトで、\deployed フォルダーに移動し、 コマンドを実行します。 regsvr32 /u SideBySide.dll

手順 5.

前の手順でどのような影響があるかを確認するには、 \deployed\client.exe をもう一度実行すると、"Class not registered" というメッセージが表示されます。 この段階では、COM ランタイムがレジストリで必要な情報を見つけることに不満を感じていましたが、代替手段で情報を利用できるようにはまだありません。 これを次の手順で解決します。

Registration-Free アクティブ化

手順 6

\deployed フォルダーで、client.exeアプリケーションのアプリケーション マニフェスト ファイル (テキスト ファイル) を作成しそれをclient.exe.manifest と呼びます。 ファイルに次のコードを貼り付けます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

手順 7.

\deployed フォルダーで、SideBySide.dll コンポーネントのプライベート アセンブリ マニフェスト ファイル (テキスト ファイル) を作成し、SideBySide.X.manifest と呼びます。 ファイルに次のコードを貼り付けます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

プロジェクトに特有の GUID 値をプレースホルダーの形式で記述しました。 これらのプレースホルダーのそれぞれの値は、 SideBySide プロジェクトの SideBySide.idl ファイル内、または OLE/COM ObjectViewer (Oleview.exe) ツールで SideBySide.dll を開くと、次のようになります。

[
   object,
   uuid([IID_ISideBySideClass]),
   dual,
   nonextensible,
   helpstring("ISideBySideClass Interface"),
   pointer_default(unique)
]
interface ISideBySideClass : IDispatch{
   [id(1), helpstring("method Version")] HRESULT 
        Version([out,retval] BSTR* pVer);
};
[
   uuid([LIBID_SideBySide]),
   version(1.0),
   helpstring("SideBySide 1.0 Type Library")
]
library SideBySideLib
{
   importlib("stdole2.tlb");
   [
      uuid([CLSID_SideBySideClass]),
      helpstring("SideBySideClass Class")
   ]
   coclass SideBySideClass
   {
      [default] interface ISideBySideClass;
   };
};

手順 8.

この時点で、アセンブリ マニフェスト ファイルを Win32 リソースとして埋め込むという主題を明らかにする必要があります。 開発者が COM コンポーネントを再構築できる場合は、アセンブリ マニフェスト (前の手順で作成したマニフェストと同様) を、型がRT_MANIFESTの Win32 リソース ( windows.h で定義) として COM DLL に埋め込むことが推奨されます。 これが不可能な場合は、COM DLL のファイル名とは異なる名前をアセンブリ (その結果、アセンブリ マニフェスト) に付けてください。 そのため、上記の場合、COM DLL は SideBySide と呼ばれますが、アセンブリは SideBySide.X と呼ばれます。 この制約の理由に関心がある場合は、「 トラブルシューティング 」セクションで説明されています。 このチュートリアルでは、アセンブリ マニフェストは埋め込まれず、実際には実現できない多くのケースを反映しています。

手順 9.

マニフェスト ファイルの提供により、クライアントはもう一度 SideBySideClass クラスをアクティブ化し、 \deployed\client.exe 実行し、期待される出力 "1.0.0-CPP" に注意してください。

Visual Basic 6.0 COM サーバーとクライアント

手順 1

最初の手順は、COM サーバーを構築することです。 新しい Visual Basic 6.0 ActiveX DLL プロジェクトを作成します。 [プロジェクト] エクスプローラー [Project1] ノードを選択し、[プロパティ] ウィンドウでその名前を SideBySide に変更します。 [プロジェクト] エクスプローラー [Class1] ノードを選択し、[プロパティ] ウィンドウでその名前を SideBySideClass に変更します。

次のサブルーチンをコード ウィンドウに貼り付けます。

Public Function Version()
Version = "1.0.0-VB6"
End Function

[ファイル] を選択する |SideBySide.dllします...\deployed フォルダーに移動し、[OK] をクリックします。 dll を作成するたびに新しい GUID が生成されないようにするには、 Project |SideBySide プロパティ...、 [ コンポーネント ] タブをクリックし、 [バージョン互換性] グループで [ バイナリ互換性 ] ラジオ ボタンを選択します。

ステップ 2

新しい Visual Basic 6.0 Standard EXE プロジェクトを作成します。 [プロジェクト] エクスプローラー [Project1] ノードを選択し、[プロパティ] ウィンドウでその名前をクライアントに変更します。 [ファイル] を選択する |プロジェクトを名前を付けて保存し、フォーム ファイルとプロジェクト ファイルを SideBySide プロジェクトのフォルダーを基準にした兄弟フォルダーに保存します。 [プロジェクト] を選択する |[参照] で、[SideBySide] の横にある [チェック] ボックスを選択し、[OK] をクリックします

フォーム デザイナーでメイン フォームをダブルクリックし、Sub Form_Load()内に次のコードを貼り付けます。

    Dim obj As New SideBySideClass
    MsgBox obj.Version()

[ファイル] を選択する |client.exeします...\deployed フォルダーに移動し、[OK] を選択します

手順 3.

現時点では、 \deployed フォルダーには、一部の中間ファイルとは別に、 client.exeとSideBySide.dll のみが含 まれている 必要があります。後者はビルド プロセスによって登録されます。 このような通常の状況でサーバーとクライアントが連携していることをチェックするには、\deployed\client.exeを実行し、期待される出力 "1.0.0-VB6" をメモします。

手順 4

このチュートリアルでは 、登録不要 の COM について説明するため、 SideBySide アセンブリの登録を解除する必要があります。 コマンド プロンプトで、\deployed フォルダーに移動し、 コマンドを実行します。 regsvr32 /u SideBySide.dll

手順 5.

前の手順でどのような影響があるかを確認するには、 \deployed\client.exe をもう一度実行すると、"実行時エラー '429': ActiveX コンポーネントでオブジェクトを作成できません" というメッセージが表示されます。 この段階では、COM ランタイムがレジストリで必要な情報を見つけ出すのに不満を感じていましたが、代替手段で情報を利用できるようにはまだありません。 これを次の手順で解決します。

手順 6.

\deployed フォルダーで、client.exeアプリケーションのアプリケーション マニフェスト ファイル (テキスト ファイル) を作成しそれをclient.exe.manifest と呼びます。 ファイルに次のコードを貼り付けます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

手順 7.

\deployed フォルダーで、SideBySide.dll コンポーネントのプライベート アセンブリ マニフェスト ファイル (テキスト ファイル) を作成し、SideBySide.X.manifest と呼びます。 ファイルに次のコードを貼り付けます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="_SideBySideClass" 
    iid="{[IID__SideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

プロジェクトに特有の GUID 値をプレースホルダーの形式で記述しました。 これらのプレースホルダーのそれぞれの値は、OLE/COM ObjectViewer (Oleview.exe) ツールで SideBySide.dll を開くことで確認できます。

[
  uuid([LIBID_SideBySide]),
  version(1.0),
  custom(50867B00-BB69-11D0-A8FF-00A0C9110059, 8169)

]
library SideBySide
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-
C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _SideBySideClass;

    [
      odl,
      uuid([IID__SideBySideClass]),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _SideBySideClass : IDispatch {
        [id(0x60030000)]
        HRESULT Version([out, retval] VARIANT* );
    };

    [
      uuid([CLSID_SideBySideClass]),
      version(1.0)
    ]
    coclass SideBySideClass {
        [default] interface _SideBySideClass;
    };
};

手順 8.

マニフェスト ファイルの提供により、クライアントはもう一度 SideBySideClass クラスをアクティブ化し、 \deployed\client.exe を実行し、期待される出力 "1.0.0-VB6" に注意してください。

互換性のないアパートメント

このチュートリアルのすべての COM サーバーは、Single-Threadedアパートメントで実行するように構築されています (つまり、STA またはアパートメント スレッドのコンポーネント)。 すべてのクライアントは、MTA (マルチスレッド アパートメントで実行されるスレッド) である C++ クライアントを除き、STA です。 そのため、クライアントとサーバーが互換性のないアパートメントに住んでいるケースが 1 つあります。この場合、COM 呼び出しのクロスアパートメント マーシャリングはプロキシとスタブの間で行われる必要があります。 この場合、アセンブリ マニフェスト ファイルの次のセクションを使用します。

...
<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />
...

これらの要素は、それ以外の場合はレジストリに存在する情報を提供します。 comInterfaceExternalProxyStub 要素は、タイプ ライブラリマーシャリングを実行するのに十分な情報を提供し、IDispatch から派生する COM インターフェイス (すべてのオートメーション インターフェイスを含む) に適しています。 このような場合 ole32.dll は、使用される外部プロキシ スタブ (つまり、アセンブリ内のファイルの 外部 ) を提供します。 COM コンポーネントで ディスパッチ インターフェイスまたは デュアル インターフェイスのみを実装する場合は、これが使用する必要がある要素です。

カスタム インターフェイスはもう少しまれで、これらの場合はもう少し作業を行う必要があります。 IUnknown から直接派生し、オートメーション互換ではない ISxSCustom というインターフェイスを考えてみましょう。 SideBySideClassISxSCustom を実装し、クライアントが登録不要のシナリオでそのメソッドを呼び出すには、comInterfaceProxyStub 要素をアセンブリ マニフェストに追加する必要があります。 要素名で提案されているように、今回プロキシ スタブは外部ではありません。これは、SideBySide.dll (ATL プロジェクト ウィザードの [プロキシ/スタブ コードのマージを許可する] チェック チェック ボックスをオンにした場合) または SideBySidePS.dllのいずれかによって提供されます。 プロキシスタブがマージされた場合、新しい要素は既存の ファイル 要素の子になります。

<file name = "SideBySide.dll">
   ...
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

それ以外の場合は、プロキシスタブdllを宣言する さらに ファイル要素を追加する必要があります。

<file name = "SideBySide.dll"> ... </file>
<file name = "SideBySidePS.dll">
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

上記の要素を説明する必要がなかった場合は、クライアントの MTA でコクラスがアクティブになるように、Visual C++ COM サーバーを 両方にスレッド化 するように構築しました。 クライアントとサーバーが同じアパートメントに存在する場合、先ほど説明した要素は無視されます。 ただし、スレッド モデルに一貫性がある場合でも、コンポーネントがクライアントとは異なるアパートメントでアクティブ化される状況が制御外にある可能性があるため、この状況に依存しないようにお勧めします。 必要な比較的小さな労力を考慮すると、マニフェスト に常 にプロキシスタブ構成を含めるのが推奨されると思います。

アクティブ化コンテキスト API の使用

概要 」セクションでは、この記事が適用されるプラットフォームでアプリケーションのプロセスを初期化する段階の 1 つは、アプリケーション マニフェスト ファイルを検索することです。 アプリケーション マニフェスト ファイルには、アプリケーションに依存関係がある アセンブリ への参照が含まれています。 ネイティブ COM コンポーネントの登録不要なアクティブ化の場合、アセンブリによって意味されるのは、共通の ID とバージョンで収集される 1 つ以上の COM DLL のセットです。

アプリケーションで、その有効期間中に直接または間接的にアクティブ化される可能性のあるコクラスのセットが 事前 にわかっている場合は、アプリケーション マニフェストが便利です。 しかし、比較的まれなアプリケーション (グリッド サーバーなど) があり、設計上、ランタイムの前に、読み込まれるモジュールを知りません。 この場合、プロセスの初期化後にアセンブリ マニフェスト ファイルを参照する方法が呼び出されます。 これは、 アクティブ化コンテキスト API によって満たされます。この API を使用すると、アプリケーション マニフェスト ファイルが隠されます。 最も簡単な方法は、アセンブリ マニフェスト ファイルの場所を使用して ACTCTX 構造体を初期化し、そこから アクティブ化コンテキスト を作成してアクティブ化することです。 次の手順では、手順 2 (オプション A) で説明されている Visual C++ クライアント アプリケーションが既にビルドされていることを前提としています。

stdafx.h を編集し、 の定義の直後に次の_WIN32_DCOM行を追加します。

#define _WIN32_FUSION 0x0100 // this causes activation context 
structs and APIs to be included.

client.cpp_tmain関数を見ると、COM の初期化呼び出しと初期化解除呼び出しの間に、SideBySideClass をアクティブ化して呼び出す複合ステートメントが表示されます。 次のようにアクティブ化コンテキストを初期化するコードの新しいセクション 内で 、この複合ステートメント (中かっこの間のすべて) を移動する必要があります。

   ACTCTX actCtx;
   memset((void*)&actCtx, 0, sizeof(ACTCTX));
   actCtx.cbSize = sizeof(ACTCTX);
   actCtx.lpSource = "SideBySide.X.manifest";

   HANDLE hCtx = ::CreateActCtx(&actCtx);
   if (hCtx == INVALID_HANDLE_VALUE)
      cout << "CreateActCtx returned: INVALID_HANDLE_VALUE" 
             << endl;
   else
   {
      ULONG_PTR cookie;
      if (::ActivateActCtx(hCtx, &cookie))
      {
         // previous compound statement goes here...
         ::DeactivateActCtx(0, cookie);
      }
   }

上記のコードは、コクラスがアクティブになる前に実行されます。 このコードでは、アセンブリ マニフェスト ファイル (この例では名前はハードコーディングされていますが、動的に読み込むアセンブリに対応する必要があります) をアクティブ化したアクティブ化コンテキスト (つまり、現在のアセンブリ) に読み込みます。 この時点から、コクラスのアクティブ化は以前と同様に行われます。 client.exe.manifest を破棄できるようになりました。

直接アクティブ化コンテキスト API の代わりに、Windows Server 2003 でのみ使用できるのが、 Microsoft.Windows.ActCtx オブジェクトです。

言うまでもなく、アクティブ化コンテキスト API には、ここで示したよりもはるかに多くの情報があり、完全な API ドキュメントへのリンクについては、「 詳細な読み取り 」セクションを参照してください。

トラブルシューティング

これまで見てきたように、COM コンポーネントの登録不要なアクティブ化には、サーバーまたはクライアントに特別なコードは必要ありません。 必要なのは、一致するマニフェスト ファイルのペアです。

このチュートリアルの方法で、独自の登録不要の開発に取り組むことをお勧めします。 具体的には、まず、登録済みサーバーでクライアントが動作していることを確認して、既知の状態に到達します。次に、サーバーの登録を解除し、エラー メッセージが予期した内容であることを確認します。最後に、マニフェスト ファイルを作成して配置することで、状況を解決します。 これにより、登録不要のアクティブ化に関するトラブルシューティング作業は、マニフェスト ファイルの構造 (およびアセンブリ マニフェストを正しく埋め込む場合) に限定されます。

登録不要の COM の問題をトラブルシューティングする場合、Windows Server 2003 のイベント ビューアーはフレンドです。 Windows XP または Windows Server 2003 で構成エラーが検出されると、通常、起動したアプリケーションのエラー メッセージ ボックスが表示され、"アプリケーションの構成が正しくないため、このアプリケーションを起動できませんでした。 アプリケーションを再インストールすると、この問題が解決する可能性があります。このメッセージが表示されるたびに、Windows Server 2003 で問題を再現し、システム イベント ログを参照し、 SideBySide ソースからイベントを探すようお勧めします。 私がこのような場合にWindows XPイベントログを見るように提案しない理由は、"[path]\[application filename]に対してアクティブ化コンテキストの生成に失敗しました"などのメッセージが常に含まれているということです。マニフェスト。 参照エラー メッセージ: 操作が正常に完了しました。これは、問題の特定には役立ちません。

さまざまなマニフェスト ファイルのスキーマは、プラットフォーム SDK のマニフェスト ファイルリファレンスという見出しの下に記載されており、スキーマ検証ツール Manifestchk.vbs を使用できるため、ここではチュートリアルに関連するいくつかの点のみを説明します。 まず、アセンブリ マニフェスト ファイルを調べてみましょう。 例については、手順 7 に戻ります。

登録不要の COM の意味では、アセンブリは、アセンブリマニフェスト ファイルの内容を使用して 1 つ以上の物理ファイルを関連付ける抽象的なアイデアであることを思い出します。 アセンブリの名前は 3 つの場所に表示され、それぞれで同じである必要があります。 アセンブリ マニフェスト ファイルの assemblyIdentity 要素の name 属性内。アプリケーション マニフェスト ファイルの dependentAssembly/assemblyIdentity 要素の name 属性、および .manifest 拡張子を除くアセンブリ マニフェスト ファイル自体の名前。 マニフェスト ファイル名がアプリケーション マニフェストの名前と一致しない場合は、Windows Server 2003 システム イベント ログに次のメッセージが表示されます。"依存アセンブリ [アプリケーション マニフェスト内の name 属性の値] が見つからず、最後のエラーがシステムにインストールされていません。アセンブリ マニフェストの name 要素が正しくない場合は、Windows Server 2003 システム イベント ログに"マニフェストで見つかったコンポーネント ID が要求されたコンポーネントの ID と一致しません" というメッセージが表示されます。

SideBySide.dllが.NET Frameworkベースのコンポーネントであった場合、アセンブリ マニフェスト ファイルを Win32 リソースとして SideBySide アセンブリに埋め込む必要がありました (また、.NET アセンブリの後にマニフェスト ファイル (つまり、SideBySide.manifest) という名前を付けました)。 ただし、アセンブリ ローダーの検索シーケンスにより、ネイティブ COM コンポーネントの場合は、マニフェストをモジュールに埋め込む 必要があります 。 アセンブリ ローダーが [AssemblyName].manifest を検索する前に、[ AssemblyName].dll を探し、その中で RT_MANIFEST 型の Win32 リソースを検索します。 リソース内の構成には、[AssemblyName] に一致する AssemblyIdentity 要素と、アプリケーション マニフェストの AssemblyIdentity 参照内の他の属性が必要です。

ただし、[AssemblyName].dllが見つかったが、一致するマニフェストが含まれていない場合、アセンブリ読み込みメカニズムは停止し、[AssemblyName].manifest の検索は続行されません。 執筆時点では、これは Windows XP のアセンブリ ローダーに当てはまります (この状況では、"アプリケーションの構成が正しくありません"という通常のメッセージが表示されます)。ただし、Windows Server 2003 では表示されません。 Windows Server 2003 では 、モジュールの 名前と一致した場合でも、検索は続行され、マニフェスト ファイルが検索 されます 。 ただし、この動作に依存しないようにお勧めします。 むしろ、両方のプラットフォームを一貫してサポートするために、可能な場合は常にアセンブリ マニフェストをRT_MANIFEST リソースとしてアセンブリに埋め込むことをお勧めします。 実現できない場合は、アセンブリ マニフェスト ファイルに、同じフォルダー内のモジュールとは異なる名前を付ける必要があります。 または、COM コンポーネントをアセンブリ マニフェストのフォルダーの子フォルダーに配置し、アセンブリ マニフェストの ファイル 要素でこの子パスを参照します。

assemblyIdentity 要素は、アセンブリの ID を定義します。 ネイティブ COM コンポーネントの 場合、名前バージョン 属性も物理ファイルの名前と一致する必要はありませんが、何らかの一貫性を適用することをお勧めします。

file 要素は、comClasstypelib、および comInterfaceProxyStub 要素の親です。 その目的は、アセンブリを構成する物理ファイルを検索 することですファイル要素の name 属性がファイル システム内のファイルを正しく参照していない場合、CoCreateInstance は "Class not registered" または "The specified module could not found" に対応する HRESULT を返します。 異なるバージョンの COM コンポーネントを異なるフォルダーにインストールできるように、 name 属性にパスを含めることができます。 Windows XP SP2 では、パスは相対パスまたは絶対パスであり、ファイル システム内の任意の場所でフォルダーを参照できます。 Windows Server 2003 では、アプリケーション ルートまたは子フォルダーを参照するパスが必要です。この規則に違反しようとすると、Windows Server 2003 システム イベント ログに次のメッセージが表示されます。"マニフェストまたはポリシー ファイルの構文エラー [アセンブリ マニフェスト ファイル名] [...]値 [...] が無効です。

comClass 要素には、clsid という必須属性が 1 つだけ含まれています。 CLSID がアセンブリ マニフェストの comClass 要素にリストされていない未登録のコクラスをアプリケーションがアクティブ化しようとすると、 CoCreateInstance は値 REGDB_E_CLASSNOTREG (0x80040154) を持つ HRESULT を返します。メッセージ テキストは "Class not registered" です。

私が言ったように、 typelib comInterface[External]ProxyStub 要素は、クライアントとサーバーが異なるアパートメントに存在する場合に必要であるため、これらの要素が処理されたときにのみ次のエラーが表示されます。 これらの要素の構成エラーにより、 CoCreateInstance はメッセージ "Library not registered"、"Error loading type library/DLL"、または "no such interface supported" に対応する HRESULT を返します。 これらのメッセージのいずれかが表示される場合は、GUID をダブルチェックし、すべての必須属性が存在することを確認します。 マニフェスト ファイル のスキーマは、プラットフォーム SDK にあります。

次に、アプリケーション マニフェスト ファイルに注目してみましょう。 例については、手順 6 に戻ります。 アプリケーション マニフェストには、[application filename].manifest という形式で名前を付ける 必要があります 。 そのため、チュートリアルでは client.exe.manifest という名前を付けて、プロセスに読み込まれる client.exeたびに読み取る必要があることを明確にしました。 これが正しく行われなかった場合、CoCreateInstance は値 REGDB_E_CLASSNOTREG (0x80040154) を持つ HRESULT を返します。メッセージ テキストは "Class not registered" です。

アプリケーション マニフェストで最も重要な要素は、 dependentAssembly/assemblyIdentity 要素です。 この要素はアセンブリ マニフェスト内の同等の要素への参照であり、2 つは 正確に一致する必要があります。 これを確実に行う良い方法は、アセンブリ マニフェストから要素をコピーし、ここに貼り付けることです。 何らかの違いがある場合は、Windows Server 2003 システム イベント ログに"マニフェストで見つかったコンポーネント ID が、要求されたコンポーネントの ID と一致しません" というメッセージが表示されます。

まとめ

登録不要の COM は、WINDOWS レジストリへの依存関係から COM コンポーネントを解放し、その結果、専用サーバーを要求するアプリケーションを解放するテクノロジです。 これにより、同じ COM コンポーネントの異なるバージョンに依存するアプリケーションがインフラストラクチャを共有し、それらのさまざまな COM コンポーネントのバージョンを、.NET Frameworkのバージョン管理と展開メカニズムのエコーで並べて読み込むことができます。

この記事では、Visual C++ と Visual Basic 6.0 の両方で記述されたネイティブ クライアント アプリケーションとマネージド クライアントによるネイティブ COM コンポーネントの登録不要なアクティブ化のデモについて説明します。 メカニズムのしくみの一部について説明し、考えられる構成エラーとそのトラブルシューティング方法について説明します。

もっと読む

 

筆者について

Steve White は、Microsoft UK の Premier Support for Developers チームで働くアプリケーション開発コンサルタントです。 Visual C#、Windows フォーム、ASP.NET を使用した開発のお客様をサポートしています。 彼の ブログ には、音楽、視覚化、プログラミングに関する彼の興味に関する詳細情報があります。

レスリー・ミュラー は、クレディ・スイス・ファースト・ボストンの研究開発 & チームの技術者です。 レスリーは、金融サービス、テクノロジースタートアップ、産業オートメーション、防衛などの環境で働く開発者および技術アーキテクトとして12年の経験を持っています。 プログラミングや研究をしていないとき、彼はスキー、アイスホッケーを楽しみ、アイスランドやロッキーズのような極端な環境で電動車で少し狂ったことを行うことができます。