最近の記事
- 4/22 - 思い出の紙時計
- 3/29 - さくらインターネットの VPS リニューアル
- 3/19 - SSHD への攻撃を分析してみた
- 3/9 - キーボードの過酷さ
- 3/8 - .NET のパフォーマンスについて
- 3/7 - phpMyAdmin への攻撃
- 3/1 - ミイラ取りがミイラになりかけた
- 2/22 - URL を知らなければ安全だと?
- 2/16 - 決意
- 1/30 - ルーターの UPnP 対応状況
Entering Passive Mode
COM サーバをレジストリに登録し、
実際にアイコンが表示されるかどうか試してみよう。
レジストリへの登録内容は昨日書いたとおりだが、
登録は COM クラス単位で行うので、
複数の COM クラスを提供するサーバの場合、
その数だけ登録する必要がある。
本来はプログラムから登録を行うのだが、
この時点ではまだ動作確認だけでよいので、
レジストリへの登録や登録解除は、
レジストリファイルを使うことにしよう。
インプロセスサーバの DLL が完成しても、
そのままでは COM 基盤はその存在を知る事ができない。
そこで、COM 基盤からサーバを呼び出せるように、
レジストリに情報を登録する必要がある。
COM クラスの情報は、HKEY_CLASSES_ROOT\CLSID の配下の、
{クラス ID} のキーに格納する。
キーのデフォルト値には、クラスの説明文字列を入れる。
これは人間が利用するための情報であり、必須ではない。
インプロセスサーバを登録する場合は、
更に InProcServer32 というサブキーを作成し
キーのデフォルト値に、DLL のファイル名を入れる。
これで、COM クラスと提供サーバの関連付けが行われるのだ。
さて、やっと DllGetClassObject を実装する準備が整った。
現時点で公開するクラスは HardLinkIconID だけであり、
DllGetClassObject は CLSID_HardLinkIconID に対応して、
HardLinkIconIDClass クラスオブジェクトを返すことになる。
今回はクラスオブジェクトを static として確保しておき、
常にそれを返すようにすることにしよう。
ま、単純なシングルトンということで。
========== library.cpp ==========
クラスオブジェクトが完成した。
これをクライアントに公開するためには、
クラスに一意のクラス ID を割り当てる必要がある。
これは、guidgen などのコマンドを使えば生成できる。
プログラム的に作成したい場合は、
CoCreateGuid API を呼び出しても取得できる。
guidgen を使った場合は、生成した ID を
C++ のソースに書ける形でクリップボードにコピーできる。
HardLinkIconID のインスタンスの作成ができないのは、
HardLinkIconID の定義が完全でないため、
具象クラスになることができないのが理由である。
では、何が不足しているのか。
HardLinkIconID は、IShellIconOverlayIdentifier と、
Object の 2 つを継承した、多重継承クラスである。
IShellIconOverlayIdentifier は IUnknown を、
Object も IUnknown を継承しているので、
HardLinkIconID の逆継承木を書くと以下のようになる。
HardLinkIconID クラスをクライアントに公開するためには、
専用のクラスオブジェクトを作成する必要がある。
クラスオブジェクトには、IClassFactory を実装する。
パフォーマンスを考え、クラスオブジェクトは
簡単なシングルトンとして作成し、
1 つのインスタンスが常に存在するようにしておこう。
そのため、クラスオブジェクトに対しては、
参照カウンタを使った生存管理は必要ない。
そのため、Object クラスは継承させないことにする。
DllCanUnloadNow に対応するには、
サーバ単位でインスタンスの参照を数えておく必要がある。
参照の確認には、AddRef と Release が使われるが、
これはインスタンス自身の生存期間の管理だけでなく、
それを含むサーバの生存期間の管理も必要となるのだ。
そのためには、サーバのロックカウントを用意し、
AddRef, Release で増減してやる必要がある。
ここでは、単純にグローバル変数としておこう。
例によって、global.hpp に宣言しておく。
クラスオブジェクトは、その一般的な機能を提供するために、
IClassFactory インタフェースを実装することが推奨される。
IClassFactory の定義を以下に示す。
class IClassFactory : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE CreateInstance(
/* [in] */ IUnknown* pUnkOuter,
/* [in] */ REFIID riid,
/* [out] */ void** ppvObject) = 0;
インプロセスサーバがクラスを提供するためには、
DLL に DllGetClassObject 関数を実装する必要がある。
この関数は以下のようなプロトタイプを持っている。
STDAPI DllGetClassObject(
/* [in] */ REFCLSID rclsid,
/* [in] */ REFIID riid,
/* [out] */ void** ppvObject);
rclsid はクラスの ID、riid はインタフェースの ID、
そして、ppvObject にはクラスのインスタンスを返す。
これまでは、COM クラスの実装や利用について書いていたが、
肝心のインスタンスの作成に関しては説明していなかった。
COM クライアントが COM クラスを利用するためには、
既存のインスタンスを取得するか、
新しいインスタンスを作成する必要がある。
一般的には後者の方が良く使われる。
COM はクライアントサーバシステムであるため、
クライアントがクラスのインスタンスを作成する際には、
C++ 固有の new 演算子を使うことはできない。