いろんなものはつながっている

COMプログラミング とにかくさわってみる

エクセルからあるアプリケーションを操作することが必要となり調べてみるとCOM経由でできることがわかった。しかしそもそもCOMプログラミングをしたことない。仕組みもしらない。ネットで調べてみても、IUnknownやらQueryInterfaceやら自分にとっては小難しい説明から始まり全体像がよくわからない。その中でもこのサイトの説明は全体像をつかみとりあえずプログラミングする手順が書いてあって自分のような初心者にはわかりやすかった。

COMとは、インターフェースプログラミングであると説明されている。インターフェースプログラミングとは、あるルールに基づいてインターフェースを定義して、そのインターフェイス定義を元にプログラミングを行うこと。

COMでは OSF(Open Software Foundation)が DCE(Distributed Computing Environment)で使用する RPC(Remote Procedure Call)のために規定した、IDL(Interface Definition Language)をCOM用に拡張したもの(単に IDL と呼ばれる)をインターフェイス定義専用言語として利用するとある。(引用元)

共通のインターフェースを定義して利用することで、
1.違うプログラミング言語で作った別々のプログラムであっても相互に連携できる
2.プログラム間を相互に連携できる
3.OSが連携を部分を担当してくれれば連携部分は自分で実装しなくてもよい。WindowsはCOMの連携部分は対応済み。(引用元)
といったことが可能になる。

上記のサイトには、実際にCOMインターフェースを定義してC++やVBで実装する手順がステップバイステップで書かれている。

インターフェースの定義
ルールに従ってインターフェースを定義する。

import "unknwn.idl";  // 型/識別子の定義をインポート
[uuid(36fafa84-91be-4d6b-a916-bc6667647b65), object] // UUIDを設定
interface IPatient : IUnknown  // インターフェースの名前
{
  HRESULT GetHealthInsurance([out, retval] BSTR *ret);  // メソッド
  HRESULT Pay([in] long HowMuch, [out, retval] long *ret);
  HRESULT TellHowIFeelBad([out, retval] BSTR *ret);
};

クラスの定義
定義したインターフェイスをどのクラスに実装するかクラスを定義する。

[uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]  // UUIDを設定
coclass SmallClinic
{
  interface IPatient;
  interface IClerk;
}

実装
VC++で実装する場合、Visual Studioのコマンドプロンプトを立ち上げて

midl /Oicf ファイル名.idl

を実行する。コンパイルすると、C++インターフェイス/クラス定義のファイルが出力される。それをインクルードしてC++のクラスを実装する。

#include "hospitalitf.h" // インターフェースをコンパイルして作成したヘッダファイル

class CSmallClinic : public IDoctor, IClerk
{
protected:
	ULONG        m_cRef;
public:
	CSmallClinic();
	~CSmallClinic();

	//IUnknown...
	STDMETHODIMP		QueryInterface(REFIID, void**);
	STDMETHODIMP_(ULONG)	AddRef();
	STDMETHODIMP_(ULONG)	Release();

	//IDoctor...
	STDMETHODIMP		Treat(IPatient*, long*);

	//IClerk...
	STDMETHODIMP		DispatchPatient(IPatient*, long*);
};

VBで実装するには少し方法が違うようだ(参照)。

作成したCOMを使う
作成したCOMを使うための記述も言語ごとに少々違う。ここにはVBで使用する例が書かれている。VC++でCOMを使う記述はサンプルコードに含まれていた。
大まかな流れは
・CoInitialize(NULL)で初期化して、CoCreateInstanceで対象のCOMクラスから目的のインターフェースの実装を取得。
・QueryInterfaceで対象のクラスが目的のインターフェースを持っているか問い合わせ。
・使いおわったらReleaseし最後にCoUninitializeであと始末。

HRESULT hr;
IClerk *clk;
IPatient *pa;
CSomethingBad *sb = new CSomethingBad();
hr = CoInitialize(NULL);
hr = CoCreateInstance(CLSID_SmallClinic, NULL, CLSCTX_ALL, IID_IClerk, reinterpret_cast<void**>(static_cast<IClerk**>(&clk)));

if (SUCCEEDED(hr)){
	hr = sb->QueryInterface(IID_IPatient, reinterpret_cast<void**>(static_cast<IPatient**>(&pa)));
	if (SUCCEEDED(hr)){
		long ret;
		clk->DispatchPatient(pa, &ret);
		pa->Release();
	}
	clk->Release();
}
CoUninitialize();
delete sb;

感想
COMを定義するのも使うのも一定のお作法がありそれに従うことでCOMの作成、使用はできるようになりそうだ。このサイトには、Visual StudioからATLを用いてCOMを作成する手順が説明されていた。

また、別のMSDNサイトには、上記で参照したサイトよりもう少しお作法の関数の詳細説明をまじえながらCOMを使い方が説明されていた。

COMのメリットとしてインターフェースが不変なものとして定義されるので、機能拡張をする際も拡張前用の旧クライアントプログラムも引き続き使用可能という状態を比較的容易に保つことができる参照)。もちろんC++で作成したDLLでも同じようなことができるが、インターフェースは不変なものと定義されているわけではないので、機能拡張ごとに同じDLLでも関数の名前や引数を変更することができてしまう。したがってDLLを更新すると旧クライアントプログラムは動作しないということが簡単に起きてしまう。

自分でCOMコンポーネントを作成したことはこれまでなかったしこれからもあまりないかもしれない。でも適当なソフトウェアからCOM相互運用を利用してエクセルを操作することは確かにやっていたな。

関連記事

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

スポンサード リンク

カテゴリー

スポンサード リンク