.NET からの COM コンポーネントの呼び出し

Microsoft .NET へのアップグレード

Mike Gunderloy
Lark Group, Inc.

November 2001
日本語版最終更新日 2002 年 1 月 30 日

目的

  • ランタイム呼び出し可能ラッパーの概念を理解する
  • TLBIMP を使って COM コンポーネントからアセンブリを作成する
  • Visual Basic .NET のコードから COM コンポーネントを直接に参照する

前提

このドキュメントを活用するためには、以下の知識が必要となります。

  • Visual Basic プログラミングについて。
  • COM の概念。特に、コンポーネント化されたソフトウェアの利点とアーキテクチャの理解。
  • Visual Basic .NET の使用。
  • 全体的な .NET アーキテクチャについての理解。

目次

相互運用性がもたらす福音
.NET からの COM の呼び出しの練習
要約

相互運用性がもたらす福音

プログラミングにおける革命は、ときとして、それ以前の成果すべてを捨て去るよう求めることがあります。極端な例として、Visual Basic アプリケーションを何年にもわたって作成してきた開発者がいたとしましょう。開発者はその期間にかなりの量のコードを蓄積してきました。そして、さまざまな言語研究者の勧めに従って、コードをコンポーネント化していました。つまり、COM (以前の ActiveX) サーバーとして、アプリケーションを呼び出し可能な機能の集まりとして分割しています。もちろん、他の開発者や企業から、ActiveX コントロールなどのコンポーネントを大量に購入している場合もあるでしょう。

そのような状況で、まったく別のオペレーティング システムに切り替えるという極端な決定を下すとどうなるでしょうか? その時点で、COM に対して行ってきた投資はすべて無価値になります。既存のコードを使用することはできず、新しいプラットフォームをゼロから学ばなくてはなりません。開発者の生産性が大きな打撃を受けるのは間違いないでしょう。

幸いなことに、COM から Microsoft .NET への切り替えにあたっては、このような大々的な生産性の低下は起こりません。次の 2 つの重要なコンセプトが、コードベースや生産性を失うことなく、COM 開発から .NET 開発への移行を可能にしています。

  • .NET コンポーネントは COM コンポーネントを呼び出すことができる。
  • COM コンポーネントは .NET コンポーネントを呼び出すことができる。

この双方向の相互運用性が、COM から.NET への移行の鍵となっています。開発者は、.NET の細部を学びながら、COM コンポーネントを使い続けることができます。この相互運用性はさまざまな状況で役立ちます。

  • 開発者は、.NET に関する細かい知識をただちに身に着けることはできません。.NET のプログラミング概念と実装を学ぶためには時間がかかるので、重要な開発には COM を使い続ける必要が生じるでしょう。
  • Visual Basic 6.0 コードは .NET に移植することができますが、変換プロセスは完璧なものではありません。実装や言語の問題上、.NET に移行できないコンポーネントも存在するかもしれません。

巨大なアプリケーションのコードすべてを、.NET のような新しいシステムでただちに作成することは不可能です。既存のコードとの相互運用を行いながら、コンポーネントを個別に作成し、1 つずつテストする方がはるかに合理的です。

開発者は、ソース コードのないサードパーティの COM コンポーネントを使用している場合があります。

この記事では、.NET クライアントからの COM サーバーの呼び出しについて詳しく説明します。これから公開される記事 「COM コンポーネントからの .NET コンポーネントの呼び出し (Call a .NET Component from a COM Component)」 は、その逆の向き、すなわち COM クライアントから .NET サーバーの呼び出しについて解説しています。

アンマネージ コードとランタイム呼び出し可能ラッパー

Microsoft .NET 共通言語ランタイム (CLR) の中で動作するコードはマネージ コードと呼ばれます。このコードは、言語間にわたる統合、セキュリティとバージョン管理のサポート、およびガベージ コレクションなど、CLR の持つすべてのサービスにアクセスすることができます。CLR の中で動作しないコードはアンマネージ コードと呼ばれます。すべての既存の COM コンポーネントは、定義上、アンマネージ コードです。COM は CLR が登場する前に設計されたものであり、COM コードは CLR が提供するフレームワーク内では動作しないので、CLR サービスを使用することはできません。

マネージ コードとアンマネージ コードを 1 つのアプリケーションの中で混在させたときのエラーは、CLR 環境の中では認識されません。マネージ コード コンポーネントは、自ら CLR に依存するだけでなく、相互作用する他のコンポーネントも CLR に依存しているという前提で動作します。

このジレンマを解決するための手段はプロキシです。一般用語としてのプロキシは、コンポーネントからコマンドを受け取り、それを変更し、別のコンポーネントに転送するソフトウェアです。マネージ コードからアンマネージ コードを呼び出すために使われる具体的なプロキシのタイプはランタイム呼び出し可能ラッパー (RCW) と呼ばれます。図 1 は、RCW がマネージ コードとアンマネージ コードの境界上にどのように位置しているかを示しています。この図には、NetUI.exe という名前の .NET プログラム、BackEnd.dll と Service.dll という名前の 2 つの COM コンポーネント、およびそれらを接続するために必要なテクノロジが描かれています。

Calling unmanaged code with RCWs.

図 1: RCW によるアンマネージ コードの呼び出し

TLBIMP によるメタデータの変換

もちろん、.NET はパブリック インターフェイスを記述するためにメタデータを使用する初めてのシステムではありません。実際、COM のタイプ ライブラリも、COM コンポーネントへのパブリック インターフェイスを記述するメタデータを含んでいます。RCW は、この COM メタデータを .NET メタデータに変換する役割を果たしています。

この変換を実行するためのツールの 1 つが tlbimp (タイプ ライブラリ インポータ) で、.NET Framework Software Developer Kit (SDK) に収録されています。tlbimp は COM タイプ ライブラリからメタデータを読み込み、COM コンポーネントを呼び出すための、対応する CLR アセンブリを作成します。

Visual Basic で ActiveX DLL または ActiveX EXE をビルドすると、タイプ ライブラリは DLL または EXE ファイルに埋め込まれます。

tlbimp はコマンドライン ツールで、結果として得られたアセンブリに暗号キーで署名したり、タイプ ライブラリ内の外部参照を解決するなどの処理のためのオプションが用意されています (コマンド プロンプトで tlbimp /?と入力すると、オプションの一覧が表示されます)。最も重要なオプションは、結果として得られる.NET アセンブリの名前を指定する/out です。たとえば、support.dll という名前の Visual Basic ActiveX DLL を、NETsupport.dll という名前の .NET アセンブリに変換するには、次のコマンド ラインを使用します。

                  
tlbimp support.dll /out:NETsupport.dll

COM コンポーネントの直接の使用

Visual Basic .NET 開発者には、COM コンポーネントを直接に使用するというオプションもあります。ただし、これは外見上そうなるということで、実際にアンマネージ コードでオブジェクトにアクセスするときには、プログラム的に RCW を使用することになります。Visual Basic .NET プロジェクトでは、次のステップで COM コンポーネントへの参照を追加することができます。

  • [プロジェクト] - [参照の追加]を選択します。
  • [参照の追加]ダイアログ ボックスの[COM]タブを選択します。
  • 使用したいタイプ ライブラリをリストから選択し、[選択クエリ]をクリックします。リストに表示されていないコンポーネントは、[参照]ボタンを使って指定します。選択されたコンポーネントが、ダイアログ ボックスの下部のリストビューに追加されます。
  • [OK]をクリックすると、Visual Basic .NET プロジェクトの中に、選択されたタイプ ライブラリのための RCW が作成されます。

この操作を行うと、Visual Basic .NET は、元の COM コンポーネントの名前から派生した名前を持つ DLL を、プロジェクトの/Bin フォルダに作成します。たとえば、この方法で BackEnd.dll version 2.0 を参照すると、Visual Basic .NET は Interop.BackEnd_2_0.dll というファイルの中に RCW を作成します。

この COM コンポーネントを直接に使用するショートカットの大きな欠点は、結果として得られたコードに署名する機会がないことです。このため、コードをグローバル アセンブリ キャッシュ (GAC) に入れることができず、したがって複数の.NET ベース アプリケーションからコンポーネントを共有することは不可能になります。

TLBIMP と直接参照のどちらを選択するか

どちらの方法でも .NET コードから COM コンポーネントの呼び出しを行うことができますが、どちらの方法が有利かは状況によって異なります。

  • 1 つの Visual Basic .NET プロジェクトでしか使用されない、開発者が自ら作成した COM コンポーネントでは、余分な作業を行うよりも直接参照を使用するとよいでしょう。この方法は、共有の必要がない、真にプライベートなコンポーネントにのみ適しています。
  • コンポーネントが複数のプロジェクトで共有される場合には、結果として得られたアセンブリに署名し、グローバル アセンブリ キャッシュに格納できるように、tlbimp を使用します。共有されるコードは署名されていなくてはなりません。またこの方法では、特定の名前を付けて特定の位置に RCW を作成することができます。
  • 作成されるアセンブリの、名前空間やバージョン番号などの細部を制御したい場合には、tlbimp を使用する必要があります。直接参照の方法では、このような細部を制御することはできません。
  • 自ら作成した COM コンポーネントでない場合には、どちらの方法も使用できません。他の開発者が作成したコードに署名することはできないからです。この場合には、コンポーネントの元の開発者から Primary Interop Assembly (PIA) を入手する必要があります。MSDN には、ActiveX コントロールなどの一般的なコンポーネントのための PIA を探すのに役立つリンクがあります。

.NET からの COM の呼び出しの練習

次の例では、COM コンポーネントのプロパティ、メソッド、およびイベントを、.NET のコードから使用します。tlbimp を使って、COM コンポーネントのインターフェイスをアセンブリに変換し、これをマネージ コードの中から通常の .NET コンポーネントと同じように扱えることを確認します。その後、tlbimp を使わずに、Visual Basic .NET プロジェクトから COM コンポーネントを直接に使用する方法も示します。

COM コンポーネントのインストール

この演習で使用する COM コンポーネントは、PhysServer.dll という名前を持っています。これは Temperature という名前の 1 つのクラスを含んでいる ActiveX DLL です。このクラスは、温度を表す内部変数を格納しており、摂氏と華氏の間の変換を行います。表 1 は Temperature インターフェイスのメンバを示しています。

メンバ タイプ 説明
Celsius プロパティ 現在の温度 (摂氏)
Fahrenheit プロパティ 現在の温度 (華氏)
GetCelsius メソッド 現在の温度 (摂氏) を取得する
GetFahrenheit メソッド 現在の温度 (華氏) を取得する
BelowFreezing イベント 氷点以下の温度が設定されたときに発生する
AboveBoiling イベント 沸点以上の温度が設定されたときに発生する

表 1: PhysServer.dll の中の Temperature クラスのインターフェイス

Visual Basic 6.0 がインストールされているコンピュータ上で、この COM コンポーネントを作成するには、次の操作を行います。

  1. Visual Basic 6.0 を起動し、新規の ActiveX DLL プロジェクトを作成します。

  2. [プロジェクト エクスプローラ]ウィンドウで Class1 モジュールをクリックし、[プロパティ]ウィンドウを使ってオブジェクトの名前を Temperature に変更します。

  3. [プロジェクト エクスプローラ]ウィンドウで Project1 プロジェクトをクリックし、[プロパティ]ウィンドウを使ってプロジェクトの名前を PhysServer に変更します。

  4. Temperature クラスに次のコードを追加します。

    
     Option Explicit
    
    Private mdblCelsius As Double
    Private mdblFahrenheit As Double
    
    Public Event BelowFreezing()
    Public Event AboveBoiling()
    
    Public Property Get Celsius() As Double
        Celsius = mdblCelsius
    End Property
    
    Public Property Let Celsius(NewTemperature As Double)
        mdblCelsius = NewTemperature
        mdblFahrenheit = ((NewTemperature * 9) / 5) + 32
        If mdblCelsius < 0 Then
            RaiseEvent BelowFreezing
        End If
        If mdblCelsius > 100 Then
            RaiseEvent AboveBoiling
        End If
    End Property
    
    Public Property Get Fahrenheit() As Double
        Fahrenheit = mdblFahrenheit
    End Property
    
    Public Property Let Fahrenheit(NewTemperature As Double)
        mdblFahrenheit = NewTemperature
        mdblCelsius = ((NewTemperature - 32) * 5) / 9
        If mdblFahrenheit < 32 Then
            RaiseEvent BelowFreezing
        End If
        If mdblFahrenheit > 212 Then
            RaiseEvent AboveBoiling
        End If
    End Property
    
    Public Function GetCelsius() As Double
        GetCelsius = mdblCelsius
    End Function
    
    Public Function GetFahrenheit() As Double
        GetFahrenheit = mdblFahrenheit
    End Function
    
    Private Sub Class_Initialize()
        mdblCelsius = 0
        mdblFahrenheit = 32
    End Sub
    
  5. Visual Basic メニューから[プロジェクト] - [PhysServer のプロパティ]を選択します。[プロジェクト プロパティ]ダイアログ ボックスで、[プロジェクトの説明]を Physical Constants Server に変更します。[OK]をクリックします。

  6. プロジェクトを保存します。

  7. [ファイル] - [PhysServer.dll の作成]を選択して、COM コンポーネントを作成します。

このコンポーネントを Visual StudioR .NET コンピュータにインストールするには、次の操作を行います。

  1. ハード ディスク上に legacy という名前の新規フォルダを作成します。

  2. PhysServer.dll を Legacy フォルダにコピーします。

  3. コマンド プロンプトを開き、次のように入力します。

    
    regsvr32 c:\Legacy\PhysServer.dll
    

図 2 のメッセージが表示されます。

Successful installation of legacy COM component message
図 2: レガシー COM コンポーネントのインストールに成功したことを知らせるメッセージ

TLBIMP によるアセンブリの作成

次に、tlbimp ユーティリティを使って、PhysServer コンポーネントのための RCW を提供するアセンブリを作成します。Windows コントロール パネルから[システム]アプレットを起動します。

  1. [スタート] - [プログラム] - [Microsoft Visual Studio .NET 7.0] - [Visual Studio .NET Tools] - [Visual Studio .NET Command Prompt]を選択して、Visual Studio .NET コマンド プロンプトを開きます。

  2. \Legacy ディレクトリに移動します。

  3. コマンド プロンプトで、次のように入力します。

    
    tlbimp PhysServer.dll /out:NETPhysServer.dll
    

この例では、RCW に署名を付けずに作成することにします。このコンポーネントを複数のクライアントで共有する予定がある場合には、tlbimp の起動時に署名する必要があります。ここでは説明を簡単にするために、このステップを省いています。コード署名とグローバル アセンブリ キャッシュの詳細については、これから公開されるドキュメント 「COM コンポーネントからの .NET コンポーネントの呼び出し (Call a .NET Component from a COM Component)」 を参照してください。

tlbimp は変換作業を実行し、タイプ ライブラリが新しい名前でインポートされたことを確認するメッセージを表示します。

Visual Studio .NET コマンド プロンプトは、tlbimp などの SDK ツールをパス指定なしに呼び出せるように、パスに Framework SDK の/Bin/フォルダを追加します。

テスト プロジェクトの作成

これで、Visual Basic .NET アプリケーションからレガシー COM コンポーネントのオブジェクトを使用する能力のテストを行うことができます。

  1. Visual Studio .NET を開き、スタート ページから[新しいプロジェクト]を選択します。

  2. 画面の左のツリー ビューから[Visual Basic プロジェクト]を選択します。

  3. プロジェクト テンプレートとして Windows アプリケーション を選択します。

  4. アプリケーションの名前を PhysServerTest に設定し、[OK]をクリックしてプロジェクトを作成します。

  5. [ソリューション エクスプローラ]ウィンドウで Form1.vb という名前のフォームを強調表示し、frmTemperature.vb という名前に変更します。

  6. 表 2 に示すコントロールを追加し、そのプロパティを設定して、図 3 のフォームを作成します。

    コントロール タイプ プロパティ
    Label Name lblFahrenheit
      Text Fahrenheit
    TextBox Name txtFahrenheit
      Text (空白)
    Button Name cmdConvertToC
      Text Convert to C
    Label Name lblCelsius
      text Celsius
    Textbox Name txtCelsius
      Text (空白)
    Button Name cmdConvertToF
      Text Convert to F
    Label Name lblAboveBoiling
      Text Above Boiling!
      Font Size 12
      Font Bold True
      Visible False
    Label Name lblBelowFreezing
      Text Below Freezing!
      Font Size 12
      Font Bold True
      Visible False

    表 2: frmTemperature.vb のコントロール

    The test form design.
    図 3: テスト フォームのデザイン

  7. PhysServer COM コンポーネントの RCW を使用するために、[プロジェクト] - [参照の追加]を選択します。[.NET]タブが選択されていることを確認し、[参照]をクリックします。

  8. Legacy フォルダに移動し、NETPhysServer.dll コンポーネントを選択します。[開く]をクリックします。これで、図 4 に示すように、コンポーネントが[選択されたコンポーネント]リストに追加されます。

  9. [OK]をクリックします。ソリューション エクスプローラの References ノードが展開し、NetPhysServer 参照が (Visual Basic プロジェクトが含んでいるデフォルトの参照とともに) 表示されます。

Adding a reference to the RCW component.
図 4: RCW コンポーネントへの参照の追加

COM コンポーネントを使用するためのコードの追加

これで、Temperature クラスのメソッド、プロパティ、およびイベントを使用するコードを書く準備ができました。[表示] - [コード]を選択し、Windows フォーム デザイナが生成したコードの後に次のコードを入力します。

                  
    Private WithEvents moTemp As NETPhysServer.Temperature

    Private Sub cmdConvertToC_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles cmdConvertToC.Click
        lblBelowFreezing.Visible = False
        lblAboveBoiling.Visible = False
        With moTemp
            .Fahrenheit = txtFahrenheit.Text
            txtCelsius.Text = .GetCelsius
        End With
    End Sub

    Private Sub cmdConvertToF_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles cmdConvertToF.Click
        lblBelowFreezing.Visible = False
        lblAboveBoiling.Visible = False
        With moTemp
            .Celsius = txtCelsius.Text
            txtFahrenheit.Text = .GetFahrenheit
        End With
    End Sub

    Private Sub frmTemperature_Load(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles MyBase.Load
        moTemp = New NETPhysServer.Temperature()
    End Sub

    Private Sub moTemp_AboveBoiling() Handles moTemp.AboveBoiling
        lblAboveBoiling.Visible = True
    End Sub

    Private Sub moTemp_BelowFreezing() Handles moTemp.BelowFreezing
        lblBelowFreezing.Visible = True
    End Sub

このコードに関する限り、NETPhysServer.Temperature クラスは普通の .NET クラスと同じであることに注意してください。クラスのインスタンスを宣言し、そのメソッドとプロパティを使用するプロセスは、COM コンポーネントを包むランタイム呼び出し可能ラッパーとネイティブなクラスで共通しています。

動作確認

Temperature クラスの動作を確認するには、次の操作を行います。

  1. F5キーを押してプロジェクトを起動します。
  2. [Fahrenheit]テキスト ボックスに 41 と入力し、[Convert to C]をクリックします。[Celsius]ボックスに値 5 が表示されます。
  3. [Celsius]ボックスに 123 と入力し、[Convert to F]をクリックします。[Fahrenheit]ボックスに値 253.4 が表示され、[Above Boiling!]ラベルが表示されます。
  4. フォームを閉じて、プロジェクトを停止します。

COM コンポーネントの直接の使用

これで tlbimp の使い方が分かったので、次に COM コンポーネントを直接に使用してみましょう。

  1. Visual Studio .NET の第 2 のインスタンスを開き、スタート ページから[New Project]を選択します。
  2. 画面の左のツリー ビューから[Visual Basic プロジェクト]を選択します。
  3. プロジェクト テンプレートとして Windows アプリケーション を選択します。
  4. アプリケーションの名前を PhysServerTest2 に設定し、[OK]をクリックしてプロジェクトを作成します。
  5. [ソリューション エクスプローラ]ウィンドウで Form1.vb という名前のフォームを強調表示し、frmTemperature.vb という名前に変更します。
  6. Visual Studio .NET の元のインスタンスに切り替え、デザイン ビューで frmTemperature を開きます。Ctrl-A キー、Ctrl-C キーを押して、フォーム上のすべてのコントロールを選択し、コピーします。
  7. Visual Studio .NET の第 2 のインスタンスに戻り、デザイン ビューで frmTemperature を選択し、Ctrl-V キーを押してすべてのコントロールを新しいフォームに貼り付けます。

COM コンポーネントを直接に使用するには、[プロジェクト] - [参照の追加]を選択し、[参照の追加]ダイアログ ボックスで[COM]タブを選択します。COM コンポーネントのリストを下にスクロールし、Physical Constants Server を選択し、[選択クエリ]をクリックして[OK]をクリックします。図 5 の警告が表示されます。

Warning when inserting a COM component reference.
図 5: COM コンポーネント参照を挿入したときに表示される警告

このときの対応は、COM コンポーネントの作者が誰なのかによって変わります。そのコンポーネントがベンダか他の開発者が作成したものであれば、[No]をクリックし、そのベンダまたは開発者から Primary Interop Assembly を入手します。この場合、コンポーネントはその開発プロジェクトに対してプライベートなものとなるので、[Yes]をクリックして、Visual Studio .NET にこのコンポーネントのための RCW を生成するように指示します。ソリューション エクスプローラの参照のリストに PhysServer が表示されるはずです。

注: これはオリジナルの PhysServer.dll であり、tlbimp を使って前に生成した RCW ではありません。

COM コンポーネントを使用するためのコードの追加

これで、Temperature クラスのメソッド、プロパティ、およびイベントを使用するコードを書く準備ができました。[View] - [Code]を選択し、Windows フォーム デザイナが生成したコードの後に、次のコードを入力します。

                  
    Private WithEvents moTemp As PhysServer.Temperature

    Private Sub cmdConvertToC_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles cmdConvertToC.Click
        lblBelowFreezing.Visible = False
        lblAboveBoiling.Visible = False
        With moTemp
            .Fahrenheit = txtFahrenheit.Text
            txtCelsius.Text = .GetCelsius
        End With
    End Sub

    Private Sub cmdConvertToF_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles cmdConvertToF.Click
        lblBelowFreezing.Visible = False
        lblAboveBoiling.Visible = False
        With moTemp
            .Celsius = txtCelsius.Text
            txtFahrenheit.Text = .GetFahrenheit
        End With
    End Sub

    Private Sub frmTemperature_Load(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles MyBase.Load
        moTemp = New PhysServer.Temperature()
    End Sub

    Private Sub moTemp_AboveBoiling() Handles moTemp.AboveBoiling
        lblAboveBoiling.Visible = True
    End Sub

    Private Sub moTemp_BelowFreezing() Handles moTemp.BelowFreezing
        lblBelowFreezing.Visible = True
    End Sub

ライブラリ名を除き、これは PhysServerTest プロジェクトの最初のバージョンで使用したのとまったく同じコードです。

動作確認

Temperature クラスの動作を確認するには、次の操作を行います。

  1. F5 キーを押してプロジェクトを起動します。
  2. [Fahrenheit]テキスト ボックスに 77 と入力し、[Convert to C]をクリックします。[Celsius]ボックスに値 25 が表示されます。
  3. [Celsius]ボックスに -17 と入力し、[Convert to F]をクリックします。[Fahrenheit]ボックスに値 1.4 が表示され、[Below Freezing!]ラベルが表示されます。
  4. フォームを閉じて、プロジェクトを停止します。

要約

マネージ コードからの COM コンポーネントの呼び出しは単純ですが、ほとんどのアプリケーションでは、これを永久的なソリューションと見なすべきではありません。長期的には、頻繁に使用しているコンポーネントはネイティブな .NET コードに移行するべきです。これにより、.NET の機能をフルに活用できるだけでなく、RCW 層をなくすことによってコードの高速化を図ることができます。

ただし、開発環境を Visual Basic から Visual Basic .NET に移行する初期の段階では、.NET コードから COM コンポーネントを呼び出す能力はきわめて重要となります。この記事の解説からわかるように、この呼び出しは実装するのも簡単です。このテクニックを、移行戦略の中の重要な一要素として活用してください。