Open Tools API その1:"Hello,World!"を表示してみる
プロジェクトの作成
IDEへのアドオンは、パッケージを作成してそれをIDEに「登録」することで実装される。RAD Studioの場合、[ファイル|新規作成|パッケージ-Delphi]で新規プロジェクトを作成する。Open Tools APIのクラスやインスタンスは通常のプロジェクトでは参照しないので、それらを含んでいるパッケージであるdesignide.dcpをプロジェクトに明示的に含めなければならない。プロジェクトマネージャーで[参照の追加]を選択。$(BDS)\lib\win32\release\designide.dcpを選択すれば、designide.dcpがプロジェクトに含まれる。
「ウイザード」の実装
ウイザードを実装するには、ToolsAPI.pasで定義してあるウィザードインターフェースとノーティファイアインターフェースを継承したサブクラスを定義し、必要に応じたメソッドを実装する必要がある。「本当に」簡単な実装は以下のコード。
unit uMain; interface procedure Register; implementation uses ToolsAPI, Dialogs; // ヘルプメニューに項目を追加するウイザードクラスの定義 type THelloWorldMenuWizard = class(TNotifierObject, IOTAWizard, IOTAMenuWizard) function GetIDString: string; function GetName: string; function GetState: TWizardState; procedure Execute; function GetMenuText: string; end; function THelloWorldMenuWizard.GetIDString: string; begin Result := 'THelloWorldMenuWizard.1.0'; end; function THelloWorldMenuWizard.GetName: string; begin Result := 'Delphi Hello World Wizard'; end; function THelloWorldMenuWizard.GetState: TWizardState; begin Result := [wsEnabled]; end; procedure THelloWorldMenuWizard.Execute; begin ShowMessage('Hello, world!'); end; function THelloWorldMenuWizard.GetMenuText: string; begin Result := 'Hello World Demo Wizard'; end; // ウイザードの登録 procedure Register; begin RegisterPackageWizard(THelloWorldMenuWizard.Create); end; end.
継承元クラスのうち、IOTAWizardはウイザードインターフェースの基本となるクラス。IDEのアドオンは必ずIOTAWizardを継承しなければならない。TNotifierObjectクラスはノーティファイアインターフェースの基本となるIOTANotifierのうち最低限のメソッドのみを実装したクラス。IOTAMenuWizardクラスは、IDEの[ヘルプ]メニューに項目を自動的に登録してくれるクラス。IDEにアクセス可能なウイザードクラスで一番実装が楽。
Open Tools APIのクラスやインスタンスはToolsAPI.pasで定義されているので、Delphiではuses節にToolsAPIを記述する。
サブクラスで記述しなければならないメソッドは以下の5つ。
メソッド名 | 役割 |
---|---|
GetIDString | IDEがウイザードを識別するためのユニークな名前 |
GetName | ユーザーが任意につけるウイザードの名前 |
GetMenuText | [ヘルプ]メニューに追加する項目のタイトル |
GetState | メニュー項目の状態(有効、無効など) |
Execute | メニュー項目が選択された場合に呼び出される |
サブクラスを定義したら、サブクラスのインスタンスをIDEに登録しなければならない。それを行うのがパッケージで定義するRegister関数。インスタンスはIDEがこのRegister関数を呼び出すことによって生成されるので管理は必要なし。*1
作成したパッケージをプロジェクトマネージャーからインストールすると、ヘルプに「Hello World Demo Wizard」なる項目が追加される。これを選択すると、お馴染みのメッセージボックスで「Hello, world!」が表示される。
[メッセージ]ビューに"Hello,World!"を表示してみる
上記のコードは普通にShowMessageで"Hello,World!"を表示するだけ。これは単純すぎて面白くないので、IDE下部の[メッセージ]ビューに"Hello,World!"を表示してみる。これは、アドオンを昔ながらのprintfデバッグでデバッグするときも有効なので、この方法を最初に紹介。
IDEのメッセージビューへのアクセスは、メッセージビューを取り扱うサービスであるIOTAMessageServicesを取得してAddTitleMessageメソッドを呼び出せば任意の文字列を取得できる。また、AddMessageGroupメソッドで任意のタブを追加できる。
先ほどのExecuteメソッドを以下の内容に差し替えて、再ビルドする。
procedure THelloWorldMenuWizard.Execute; var IMessageServices: IOTAMessageServices; MessageGroup: IOTAMessageGroup; begin // メッセージビューに関するサービスを取得する。 IMessageServices := BorlandIDEServices as IOTAMessageServices; // メッセージビューにタブを追加する。 MessageGroup := IMessageServices.AddMessageGroup('メッセージ出力テスト'); // メッセージビューに文字列を表示する。 IMessageServices.AddTitleMessage('こんにちは、世界!', MessageGroup); end;
これで、IDEのメッセージビューに「メッセージ出力テスト」というタブが追加されて、「こんにちは、世界!」という文字列が表示される。
*1:TNotifierObjectが参照カウントによるメモリ管理をしてくれるため。
Open Tools API その0:概要
戯言前口上
RAD Studioにコードフォーマッターが実装されても、残念ながら、宗教論争にすらなるC/C++のコーディングスタイルすべてをカバーし切れていないのが不満。ということで、RAD Studioのコードエディタ上からGNU IndentやUncrustifyといった既存のコードフォーマッタを手軽に呼び出したり、あるいは、エディタの選択部分に対して、sedとかawkのようなテキストフィルタをvi(ex)の!コマンドのようにフィルタをかけられたら便利かなと思い、敷居が高いと言われるOpen Tools APIに挑戦して、何とか実装してみようと。
本音を言えば、C++Builderで書きたいのだけど、キモであるウィザードインターフェースの実装部分が少々面倒*1なのと、デバッグ時に何故かブレークポイントを設定しても止まらない場合がある*2ので、少々不本意ながら記述言語はDelphiで。てか、Open Tools APIに関してはDelphiのほうが楽かも。
Open Tools APIって何?
Open Tools APIとは、RAD Studioを拡張するためのAPI群のことで、メインメニューに何らかの項目を追加したり、コードエディタ内部にアクセスするなど、これらを使用することによってRAD Studioを自由に拡張することが可能になる。IDEを拡張するには、後述する「ウイザードインターフェース」と「ノーティファイアインターフェース」と呼ばれるクラスを継承したサブクラスを持つパッケージを作成して、そのパッケージをIDEに登録するだけでOK。*3デバッグはIDEそのものをデバッグする感じでデバッグが出来る。ただし、デバッグ時にパッケージの登録・削除を繰り返すとIDEが予期せぬところでコケるので、タスクマネージャの使用は必須。
リソースいろいろ
Open Tools APIについての役に立ちそうなリソースは以下の通り。
- オンラインヘルプのIDE の拡張(Tools API)
- C++Builder6開発者ガイド 58章の「IDEの拡張」がOpen Tools APIに関する項目。
- C++Builder6のヘルプファイル
- Erik's Open Tools API FAQ
- GExpertsのソースコード
- MustangpeakのOpen Tools APIについての情報
- Tempest SoftwareのOpen Tools APIについての情報
- Borland C++Builder 6 Developer's Guide(isbn:0672324806)
- Delphiコンポーネント設計&開発完全解説(isbn:4844317466)
ウィザードインターフェース、サービスインターフェース、ノーティファイアインターフェース
ウィザードインターフェースとはIDEに対するアドオンとなるクラスのことで、用途によって以下の4種類がある。(ヘルプより抜粋)
インターフェース型 | 説明 |
---|---|
IOTAFormWizard | 通常,新しいユニット,フォーム,その他のファイルを作成する |
IOTAMenuWizard | へルプメニューに自動的に追加される |
IOTAProjectWizard | 通常,新しいアプリケーションまたはその他のプロジェクトを作成する |
IOTAWizard | 他のカテゴリに当てはまらないその他のウィザード |
ノーティファイアインターフェースとは、IDEからの通知を受け取るクラスで、ウィザードインターフェースとセットで使用する。(ヘルプより抜粋)
インターフェース | 説明 |
---|---|
IOTANotifier | すべてのノーティファイアの抽象基本クラス |
IOTABreakpointNotifier | デバッガのブレークポイントの発生または変更 |
IOTADebuggerNotifier | デバッガのプログラムの実行,あるいはブレークポイントの追加または削除 |
IOTAEditLineNotifier | ソースエディタ内の行の移動の追跡 |
IOTAEditorNotifier | ソースファイルの変更または保存,あるいはエディタにおけるファイルの切り替え |
IOTAFormNotifier | フォームの保存,あるいはフォームまたはフォーム(またはデータモジュール)上の任意のコンポーネントの変更 |
IOTAIDENotifier | プロジェクトのロード,パッケージのインストールなどのグローバル IDE イベント |
IOTAMessageNotifier | メッセージの表示におけるタブ(メッセージグループ)の追加と削除 |
IOTAModuleNotifier | モジュールの変更,保存,名前の変更 |
IOTAProcessModNotifier | デバッガにおけるプロセスモジュールのロード |
IOTAProcessNotifier | デバッガにおけるスレッドとプロセスの作成または破棄 |
IOTAThreadNotifier | デバッガにおけるスレッドの状態の変更 |
IOTAToolsFilterNotifier | ツールフィルタの呼び出し |
サービスインターフェースとは、アドオンがIDEの機能(エディタ、デバッガなど)に対して何らかの操作を行う場合の仲介役で、これにアクセスすることで、IDEの内部を触ることが出来る。主なものとして以下の物がある。
サービスインターフェース型 | 説明 |
---|---|
INTAServices | IDEそのもの |
IOTAEditorServices | ソースエディタ |
IOTAModuleServices | IDEが開いているファイル・プロジェクトなど |
IOTAMessageServices | メッセージウィンドウ |
IOTAKeyBindingServices | キーバインド |
IOTADebuggerServices | デバッガ |
IOTACodeInsightServices | コードインサイト |
実際にアドオンを実装する場合はパッケージを作成して、「ウイザードインターフェース」と「ノーティファイアインターフェース」を親に持つサブクラスを定義する。そのサブクラスをIDEに「登録」すれば、サブクラスはIDEのアドオンとなる。*4IDE内部で何らかの処理が行われた場合、「ノーティファイアインターフェース」の特定のメソッドが呼ばれるので、サブクラスに「ノーティファイアインターフェース」の対応するメソッドをオーバーライドしたメソッドを記述すれば、アドオンにおけるIDEからの応答先となる。そこで必要に応じてサービスインターフェースを介してIDE内部の情報にアクセスする。
これらのクラスの定義とインスタンスはToolsAPI.pasで定義してあるので、Delphiならばuses節にToolsAPIを記述、C++Builderならば、ToolsAPI.hppをインクルードする必要がある。最後に、プロジェクトマネージャの[参照の追加]で$(BDS)\lib\win32\release\designide.dcpをプロジェクトに含めておくこと。