杉田和久 テックステート取締役

今回は、Visual C++.NETでバージョンが7.0に上がったATLについて取り上げます。ATLは、MFCを使うよりもずっと高機能で軽量、高速なCOMコンポーネントを作れます。しかし、COMのプログラムは、ヘッダ、ソースの他にIDLやDEFなど定義が複数に分かれており、プログラム自体も複雑で難易度が高く、習得も困難でした。Visual C++6.0からVisual C++.NETに変わってATLが簡単に作れるようになったわけではないものの、新しく属性機能が加わり可読性の高いコードが記述できる上、手間も少なくなりました。

属性機能を知る

 ATLのプロジェクトを作る場合、通常以下のステップを踏みます。

1 ATLの任意のプロジェクト(ローカル、インプロセス、サービスより選択)を生成。
2 必要なコンポーネントを追加。
3 必要なプロパティ、メソッド、イベント等を追加。

 これはVisual C++6.0もVisaul C++.NETも同じです。しかし、生成されるソースと追加しなければならないコード、そしてファイル構成が大きく異なります。

 Visual C++6.0の属性なしのATLは、主にソース(CPP)、インターフェース定義ファイル(IDL)、モジュール定義ファイル(DEF)で構成されますが、Visual C++.NETで属性を利用するとソース(CPP)だけで済み、プログラム量も極端に少なくなります。ソース(CPP)をコンパイルするとき属性機能により動的にIDLやDEFファイルが作られるからです。

 たとえば、属性を持たない、いちばん小さなインプロセスサーバーのソースは、以下のようになっています。

#include "stdafx.h"
#include "resource.h"
#include "ATLInprocess2.h"

class CATLInprocess2Module : public CAtlDllModuleT< CATLInprocess2Module >
{
public :
DECLARE_LIBID(LIBID_ATLInprocess2Lib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_ATLINPROCESS2, "{3C2B82D3-A98C-4B5A-9B83-B217FF80D671}")
};

CATLInprocess2Module _AtlModule;

// DLL エントリ ポイント
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
hInstance;

  return _AtlModule.DllMain(dwReason, lpReserved);
}

// DLL を OLE によってアンロードできるようにするかどうかを指定します。
STDAPI DllCanUnloadNow(void)
{

  return _AtlModule.DllCanUnloadNow();
}

// 要求された型のオブジェクトを作成するクラス ファクトリを返します。
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{

  return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}

// DllRegisterServer - エントリをシステム レジストリに追加します。
STDAPI DllRegisterServer(void)
{

  // オブジェクト、タイプ ライブラリおよびタイプ ライブラリ内の全てのインターフェイスを登録します

  HRESULT hr = _AtlModule.DllRegisterServer();
return hr;
}

// DllUnregisterServer - エントリをレジストリから削除します。
//
STDAPI DllUnregisterServer(void)
{
HRESULT hr = _AtlModule.DllUnregisterServer();
return hr;
}

 インプロセスサーバーはDLLサーバーなので、DLLのソースには、エントリーポイントや、レジストリ登録のためのモジュールなども記述されています。

 このソースが、Visual C++.NETで属性を利用すると、以下のようになります。

// ATLInprocess.cpp : DLL エクスポートの実装です。

#include "stdafx.h"
#include "resource.h"

// このモジュール属性は、DllMain、DllRegisterServer および DllUnregisterServer を自動的に実装します。
[ module(dll, uuid = "{6767D810-BA77-4A43-8B2A-9996D189F50D}",
name = "ATLInprocess",
helpstring = "ATLInprocess 1.0 タイプ ライブラリ",
resource_name = "IDR_ATLINPROCESS") ]
class CATLInprocessModule
{
public:
// CAtlDllModuleT メンバのオーバーライド
};

 これだけです。IDL記述内容もソースに含まれるため、IDLファイルも不要です。

 そもそもCOMが定義すべきは、クラスとインターフェース、メソッド、プロパティだけです。DLLのエントリポイントなどはCOM実装と関係なく、いつも共通記述する部分であり、プログラマーが本来考えるべきものではありません。

 属性は、C++の言語機能を拡張、面倒で複雑なCOMのプログラムを簡単にします。

 Visual C++.NETに搭載の新コンパイラは、「_ATL_ATTRIBUTES」が定義されていると、ソース中から属性を確認、属性プロバイダというモジュールを使いコード挿入や変更をします。たとえば、上記ソースでは、クラス定義の直前のカッコ [](属性ブロック)で囲まれた領域で、「module」というキーワードが使われています。「module」キーワードで「dll」と指定してあるため、DLLのエントリポイントやIDLなどインプロセスサーバーに必要なプログラムは、ビルド時に動的に生成されるのです。

 属性は、クラスやインターフェース、メソッド定義などの直前にカッコで囲まれた「属性ブロック」へのキーワード記述により、直後の定義に影響を与えます。IDLファイルの記述には従来より属性定義がありましたが、Visual C++.NETの属性では、IDL同様の定義をC++のソース中で行えるのです。

 IDLでは、インターフェースとその属性の定義だけができましたが、実装とプロトタイプの定義はソースに記述する必要がありました。COMの構造をより簡素にするには、インターフェースの定義とプロトタイプ宣言をわざわざ別ファイルにする必要はないはずです。インターフェース定義と同名のC++の関数を結び付け定義する従来のやり方でなく、C++自身で属性をサポートし関数自体でインターフェースやクラス等を表現できるようにしたわけです。

 属性により、コンポーネントに含まれるメソッドやプロパティなどの実装もわかりやすくなりました。たとえば、メソッドに注釈(helpstring)を設定する場合、属性を使わないとIDLファイルの中身を編集する必要がありましたが、属性を使えばC++のプロトタイプ宣言で定義されるので、ここだけ確認すればいいわけです。

 イベントについては、従来はディスパッチインターフェースの定義をイベントとして利用する作りでしたが、Visual C++.NETの属性では「__event」というキーワードだけでイベント定義が終了するので、プログラムが見やすく簡潔になりました。

Visual C++.NETでの属性の実装方法

図:ATLのプロジェクトウィザード。標準設定では、属性のチェックが入っている。
 Visual C++.NETでATLを使う場合、標準設定は「属性あり」。[拡大表示]はATLのプロジェクトウィザードの画面です。ここで属性のチェックを外すと、従来同様のプロジェクトが生成されます。

 コンポーネント追加の場合、ソリューションエクスプローラでのプロジェクト指定か、クラスビューでクラス名を指定し[追加|クラスの追加]を選択します。Visual C++6.0のようにATL独自のメニューはなくなり、他のさまざまなコンポーネントと統合されました。この操作以外はVisual C++6.0と同様の操作で行えます。

 ATLを利用しているのであれば、Visual C++.NETはぜひとも使いたいものです。属性により、従来複雑だったCOM開発が、非常に簡潔になっています。余計なコードに気を取られず必要な部分だけを開発できるので、バグも減らせます。

 これまで4回にわたり、Visual C++.NETへの乗り換えメリットを解説してきました。Visual C++.NETは、3年の期間を空けたこともあり、開発環境はもちろん、MFCやATLの性能もはるかに高機能になってます。名称に.NETを含むため多くの誤解ももたらしましたが、Visual C++.NET開発だけでなく、従来機能も広く拡張されたことが理解できましたでしょうか。

 開発者はプログラミングに多くの時間を費やすため、本来考えるべきアルゴリズムやスペック以外の部分は、できる限り開発ツールに解決してもらいたいもの。それが障害の発生と工数を低く抑える最も容易な解決策です。.NETという名前を聞いて敬遠した方も、この機に導入を検討してみてはいかがでしょうか。

杉田和久 テックステート取締役
 .NETやWindows上の開発作業全般に精通。日経BP ITPro実践スクール,日経ソフトウエア誌など,技術セミナー講師や執筆を手掛ける。