Add-In機能は開発環境を変える
オブジェクト指向のもう一つの効用
ちまたのアプリケーションでは、一時「アドイン」なるモノが大はやりした。これは、
アプリケーションに足りない機能を追加するものであった。何と、開発ツールであるVisualBasicにもアドインを使うことができるようになったという。一体どういうものなのか? 何ができるのか? その可能性を探ってみよう。
酒井 法雄
●アドインとは?
よく、DOSアプリケーションなどにアドインとかアドオンとか言っていたモノがあった。アプリケーション本体には元々ない機能だが、あたかも内蔵しているかのように呼び出して使うことができる小さなアプリケーションをこう呼んでいた。これは、DDEやOLEあるいは最新のOLE 2.0などを使えばなんてこともないモノではあるのだが、シングルタスクのDOSの時代には重宝されたものらしい。らしいというのは、私はそういうモノを使ったことがなかったからである。だって、DOSでそういうコトをするなんて信用できないではないか。いや、そういう理由ではなかったかもしれない。単に某社製の難しいアプリケーションを使うことができなかったからだろう。
それはともかく、VisualBasic 4.0にはアドインの機能が付いたという。一体それは何のことだ? 私は首をひねった。Windowsでアドインというからには、OLE 2.0がらみのコトだろうか? それともOCXのコトか? だったら、素直にOLE Serverを使えるとか、OCXコントロールを追加できるとかそういう表現でいいのではないか? そうじゃないアドインがあるとするならば、一体何なのだろう?
実際にアドインに触れて、私は再び驚いた。確かに予想通りにOLE 2.0がらみではあった。しかし、単にOLE Serverを使うだけならアドインとは呼ばない。やはり、OLE Serverを作れ、それを呼び出せるという表現で十分である。アドインと言うのは、VisualBasicをユーザーがどう操作したかを調べ、必要に応じてVisualBasicでのユーザーの操作をコーディングでシミュレーションしてしまう機能があるからなのだ。つまり、デザイン時にプログラミングの手伝いをするツールを作るとか、バージョン管理ツールを作るとか、主としてそういった開発補助ツールを作ることに向く仕組みなのである。
このようなアドイン独自の機能は、いくつかのオブジェクトというカタチで用意されている。このオブジェクトとOLE Serverを作るクラスなどの機能を組み合わせると、アドインを作成することができるというワケだ。したがって、アドインはあくまでもOLE Serverであるが、通常のOLE Serverと違って、VisualBasicの開発環境を監視、制御するものである。
●アドインオブジェクト
アドインオブジェクトは、大きく分けて次の4つに分類される。
AddInManagerオブジェクト
- Menuオブジェクト
- MenuItemsコレクション
- MenuLineオブジェクト
- FileControl オブジェクト
これらのオブジェクトの関係は、図のようになっている。
Applicationオブジェクト
ここからも分かるように、すべてのオブジェクトはApplicationオブジェクトを親としている。このオブジェクトには、次のようなプロパティがある。ここではいちいちその内容を記述しないが、プロパティ名からも分かるように、開発環境で実行されるプロジェクトについてのさまざまな情報を知ることができる。
次に、それぞれのオブジェクトについて概要を述べよう。
★Applicationオブジェクト
プロパティ
アドインの初期化を管理するオブジェクト
AddInManagerオブジェクトには、ユーザーがアドインマネージャダイアログボックスなどでアドインを起動または終了したときに発生するイベントを含むオブジェクトである。このイベントには初期化コードを記述する。
★AddInManager オブジェクト
イベント
VisualBasic開発環境に新しいメニューを追加したり、対応するコードを実行するためのオブジェクト
Menuオブジェクトはメニューそのものを表す。実際にシステム全体のメニューにアクセスするには、MenuItemsコレクションを使う。MenuLineオブジェクトは、ユーザーが実行した最後のメニュー選択項目を表す。
★Menuオブジェクト
プロパティ
MenuItems
Parent
★MenuItemsコレクション
プロパティ
メソッド
★MenuLineオブジェクト
プロパティ
メソッド
イベント
VisualBasicのプロジェクトに関係したファイル操作を管理するオブジェクト
FileControl オブジェクトは、VisualBasicでのファイル管理に関わるすべての情報を知ることができる。
★FileControl オブジェクト
プロパティ
メソッド
イベント
プロジェクト内のフォームとコントロールを操作するオブジェクト
ProjectTemplateオブジェクトは、VisualBasicのプロジェクトに関する処理を操作する。
SelectedComponetsコレクションは、プロジェクト内ぶ現在選択されているすべての構成要素(フォーム、標準、クラスなどのモジュール)の集合を示す。
Componentオブジェクトは、VisualBasicのプロジェクトの一つの構成要素を示す。
FormTemplateオブジェクトは、VisualBasicのプロジェクト内のフォームを表す
ControlTemplatesコレクションは、フォーム上のすべてのコントロールを表す。
SelectedControlTemplatesコレクションは、FormTemplatesのプロパティで、現在選択されているコントロールを表す。
ControlTemplateオブジェクトは、フォーム上の一つのコントロールを表す
Propertiesコレクションは、プロパティのコレクション。
Propertyオブジェクトは、フォームまたはコントロールのプロパティを表す。
★ProjectTemplateオブジェクト
プロパティ
メソッド
★SelectedComponetsコレクション
プロパティ
メソッド
★Componentオブジェクト
プロパティ
メソッド
SaveAs
★FormTemplateオブジェクト
プロパティ
メソッド
★ControlTemplatesコレクション
プロパティ
メソッド
★ControlTemplateオブジェクト
プロパティ
ControlTemplatesコレクションと同じ
メソッド
ControlTemplatesコレクションと同じ
★SelectedControlTemplatesコレクション
★Propertiesコレクション
プロパティ
メソッド
★Propertyオブジェクト
プロパティ
Propertiesコレクションと同じ
メソッド
Propertiesコレクションと同じ
このように、アドインのオブジェクトを使えば、VisualBasicでのユーザーの動作を知ったり、開発環境の状態を知ったり、フォームなどのモジュールやコントロールについてのプロパティを得たりといった情報取得ができる。さらには、環境やフォームをはじめとするオブジェクトに対して操作をすることもできるわけだ。
●アドインの使い方
ここでは、アドインのサンプルとして用意されているいくつかのサンプルプログラムを使って、実際にアドインの動作をさせてみよう。
図は、VisualBasicの[アドイン]メニューを開いたところである。データマネージャとレポートデザイナがすでにあるが、これは正確にはアドインではない。VisualBasicから起動する独立したアプリケーションである。ここで、[アドインマネージャ]を選んでみると、図のようなダイアログが開く。ここには、3つのアドインが表示されている(最初は一つしかなかったのだが、サンプルをコンパイルしてEXEファイルを作成したので、このように3つ表示されている)。ここから、アドインとして実行したいモノをチェックして<OK>ボタンを押す。ここでは、Align Sample Add-Inを選んだ。
再び[アドイン]メニューを選んでみると、図のようにメニュー項目が増えている。これは、アドインの実行が開始されてメニューが追加されたからである。しかし、この動作はアドインによって違っている。アドインは必ず起動はされるのだが、File Event Spyならばすでにダイアログは開いており、実際にSpy機能をするかどうかをチェックで指定する。データフォームデザイナならば、このメニューを選ばれたときに初めてダイアログが表示されるといった具合だ。
ちなみに、こうして起動したアドインはOLEアプリケーションであるから、タスクリストにもきちんと表示されるし、[ツール][参照設定]メニューを選べば、図のように参照設定して呼び出すことができる。
これらのサンプルアドインの実際の動作であるが、File Event SpyはVisualBasicでのファイル操作に関するイベントを調べてリストボックスに表示するものだ(実行図)。VisualBasic 3.0までで同様のことをするプログラムを作ったとしたら、これはかなりスゴいことなのだが、先ほどの有用なオブジェクトを見てからだと、これは結構カンタンそうだなと思ってしまう。
また、データフォームデザイナーは、データソース、テーブル、フィールドを指定してカンタンなメンテナンスをするプログラムを作ってしまうというモノだ(実行図)。こうした指定をすると、図のようなフォームが作成され、それぞれのボタンに対応した処理のコードまで自動的に作られる。実行すれば、きちんと動作する。最近 Accessなどに装備されているなんとかWizardみたいなものも、このようにアドインを使って作ることもできるわけだ。
実行画面
●アドインのコーディング例
こうしてアドインを使うのはカンタンだが、作るのはそうカンタンではない。少なくともOLE Serverアプリケーションを作成できるようオブジェクト指向の考えに慣れ、VisualBasicの動作などについても十分に知っていなければならない。
そこで、サンプルプログラムから一部のコードを紹介して、実際にはどんなコーディングがなされているかを見てみよう。ここでは、先ほどの例にもあった Align アドインを使ってみる。
すでに紹介したAlignのメニューからも分かるように、このアドインはVisualBasicのフォーム上に配置した複数のコントロールをマルチセレクトし、アライメントやサイズを合わせるというモノだ。図のように適当に配置した複数のコントロールを選択し、右ぞろえにし、さらに高さを合わせてみた。こう言ったことがアドインで可能なのである。
Align前。選択状態になっている
Alignで右に合わせた
Alignで高さを合わせた
ALIGNでは3つのクラスを使っている。
ALIGN.CLSはアドインの起動時および終了時にメニューを登録/削除するものである。ConnectAddInイベントが起動時の処理をする起動されたVisualBasicのインスタンスなどの情報を得、VisualBasicのメニューにアライメントを指定する項目を追加する。
追加したメニューについては、それぞれ対応するALIGHALLおよびSIZEALLのクラスを割り当てる。これは、メニューが選択されたときに発生するのは対応するオブジェクトのAfterClick()イベントであるからだ。AfterClick()イベントは単一にあって引数でどのメニューが選ばれたかを知るというものではないことに注意してほしい。したがって、選ばれたメニューに対応した処理をするために、ALIGNALLとSIZEALLクラスにあらかじめ設定しておいたメンバー変数、すなわちプロパティに識別するための印を入れておく必要がある。
・ALIGN.CLS
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Connector"
Attribute VB_Creatable = True
Attribute VB_Exposed = True
Attribute VB_Description = "Align Sample Add-In"
'This class is used to connect this addin to the Visual Basic
'design environment. It must the Public property set to True, and the
'Instancing property set to '2 - Createable MultiUse' (recommended for addins)
'or '1 - Createble SingleUse'
Option Explicit
'Storage for the menu and menuline objects created when the add-in adds its own menu
'and menulines to Visual Basic
Const AlignLeft = 0
Const AlignRight = 1
Const AlignTop = 2
Const AlignBottom = 3
Const AlignAbout = 4
Const SizeWidth = 5
Const SizeHeight = 6
Const SizeBoth = 7
Dim AlignMenu As VBIDE.SubMenu
Dim SizeMenu As VBIDE.SubMenu
Dim MenuLines(AlignLeft To SizeBoth) As VBIDE.MenuLine
Dim AlignHandler(AlignLeft To AlignBottom) As AlignAll
Dim SizeHandler(SizeWidth To SizeBoth) As SizeAll
Dim ConnectID(AlignLeft To SizeBoth) As Long
Dim VBInstance As VBIDE.Application
Sub ConnectAddIn(VBDriverInstance As VBIDE.Application)
Dim i%
'This method is called when this add-in is installed in an instance
'of Visual Basic. Choose "Align Sample Add-In" from the Add-In manager
'on another instance of VB. VBInstance will be the object representation
'of the instance of Visual Basic which installed the add-in. To change
'the name of the addin in the addin manager, select the Align project in the
'object browser, click on the Connector class, and choose Options.
'Save the instance of Visual Basic so we can refer to it later.
Set VBInstance = VBDriverInstance
With VBInstance.AddInMenu.MenuItems
'Add our Align menu and menu lines to the Visual Basic Add-In menu.
Set AlignMenu = .AddMenu("A&lign")
With AlignMenu.MenuItems
Set MenuLines(AlignLeft) = .Add("&Left Align")
Set MenuLines(AlignRight) = .Add("&Right Align")
Set MenuLines(AlignTop) = .Add("&Top Align")
Set MenuLines(AlignBottom) = .Add("&Bottom Align")
Set MenuLines(AlignAbout) = .Add("About...")
End With
'Add our Align menu and menu lines to the Visual Basic Add-In menu.
Set SizeMenu = .AddMenu("Si&ze")
With SizeMenu.MenuItems
Set MenuLines(SizeWidth) = .Add("&Width")
Set MenuLines(SizeHeight) = .Add("&Height")
Set MenuLines(SizeBoth) = .Add("&Both")
End With
End With
'Connect the corresponding event handler object to the correct menu line.
For i% = AlignLeft To AlignBottom
'Create a new handler for each direction.
Set AlignHandler(i%) = New AlignAll
'Save the ID for each connected event.
ConnectID(i%) = MenuLines(i%).ConnectEvents(AlignHandler(i%))
'Pass the VBInstance to the child objects.
Set AlignHandler(i%).VBInstance = VBInstance
Next i%
'Set the AfterClick handler for the about box.
ConnectID(AlignAbout) = MenuLines(AlignAbout).ConnectEvents(Me)
'Connect the SizeHandler events. Same as hooking up Align events.
For i% = SizeWidth To SizeBoth
Set SizeHandler(i%) = New SizeAll
ConnectID(i%) = MenuLines(i%).ConnectEvents(SizeHandler(i%))
Set SizeHandler(i%).VBInstance = VBInstance
Next i%
'Initialize the directional Align instances.
AlignHandler(AlignLeft).MainProp = "Left"
AlignHandler(AlignRight).MainProp = "Left"
AlignHandler(AlignRight).ShiftProp = "Width"
AlignHandler(AlignTop).MainProp = "Top"
AlignHandler(AlignBottom).MainProp = "Top"
AlignHandler(AlignBottom).ShiftProp = "Height"
'Initialize SizeFlags on Size instances.
SizeHandler(SizeWidth).SizeFlags = 1
SizeHandler(SizeHeight).SizeFlags = 2
SizeHandler(SizeBoth).SizeFlags = 3
End Sub
Sub DisconnectAddIn(Mode As Integer)
Dim i%
Dim mnuItems As VBIDE.MenuItems
'Remove AlignMenu items.
Set mnuItems = AlignMenu.MenuItems
For i% = AlignLeft To AlignAbout
'Disconnect the event handlers from the menu lines
MenuLines(i%).DisconnectEvents ConnectID(i%)
'Remove the menu and menu lines we installed in Visual Basic
mnuItems.Remove MenuLines(i%)
Next i%
'Remove SizeMenu items.
Set mnuItems = SizeMenu.MenuItems
For i% = SizeWidth To SizeBoth
MenuLines(i%).DisconnectEvents ConnectID(i%)
mnuItems.Remove MenuLines(i%)
Next i%
'Remove items from Addins menu.
With VBInstance.AddInMenu.MenuItems
.Remove AlignMenu
.Remove SizeMenu
End With
End Sub
'Use this class to provide an AfterClick handler for About...
Public Sub AfterClick()
About.Show vbModal
End Sub
実際にメニューが選ばれると、AfterClick()イベントが呼び出される。初期化時に設定されているプロパティなどから、それぞれのオブジェクトにあるこのイベントは、自分が何をすべきなのかを判断することができるようになっているわけだ。
ここで注目したいのは、For Each〜Next構文と、With〜End With構文だ。
For Each〜Nextは、オブジェクト指向の項でも紹介したとおり、コレクションなどから一つずつのオブジェクトを取り出して、それぞれについて処理をするためのモノである。
一方、Width〜End Withは、あるフォームやコントロールなども含んだオブジェクトについて、それぞれのプロパティの内容にアクセスすることができるものだ。同じオブジェクトについての複数のプロパティにアクセスしたいときには、タイプする文字が少なくできる他、オブジェクト変数などと組み合わせると非常に効率よいコーディングができる。
・ALIGNALL.CLS
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "AlignAll"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
'Instances of this class handle all Alignment events.
'This class does not need its Instancing or Public
'properties set.
Option Explicit
Public MainProp As String 'Left or Top
Public ShiftProp As String 'Width or Height
Public VBInstance As VBIDE.Application
Public Sub AfterClick()
Dim TheseControls As VBIDE.SelectedControlTemplates
'Using 'As Object' for collection iterator is faster cross process.
Dim Control As Object 'VBIDE.ControlTemplate
Dim bDoShift As Boolean
Dim CheckValue As Long
Dim tmpValue As Long
Dim Controls() As VBIDE.ControlTemplate 'Don't cycle twice through collection
Dim i%, iControlCount As Integer
'Shift properties are Height and Width. bDoShift will be true for
'Bottom and Right alignment.
bDoShift = Len(ShiftProp)
If Not bDoShift Then CheckValue = 99999 'Defaults to 0 for a shifted property
Set TheseControls = VBInstance.ActiveProject.ActiveForm.SelectedControlTemplates
'If a property access fails, then just ignore it.
On Error Resume Next
iControlCount = TheseControls.Count
If iControlCount < 2 Then Exit Sub 'Nothing to do
ReDim Controls(iControlCount - 1)
'Scan all the selected controls, checking for the best value to align to.
For Each Control In TheseControls
'Keep a reference to the Control for the second pass through the collection.
Set Controls(i%) = Control
If bDoShift Then 'Right and Bottom alignment
With Controls(i%).Properties 'Use the typed object for fastest performance.
tmpValue = .Item(MainProp) + .Item(ShiftProp)
If tmpValue > CheckValue Then CheckValue = tmpValue
End With
Else 'Left and Top alignment.
With Controls(i%).Properties(MainProp)
If .Value < CheckValue Then CheckValue = .Value
End With
End If
i% = i% + 1 'Increment to maintain the position in the collection.
Next
'Use the best value found on the first pass to align each control.
For i% = 0 To iControlCount - 1
If bDoShift Then 'Right and Bottom alignment
With Controls(i%).Properties
.Item(MainProp) = CheckValue - .Item(ShiftProp).Value
End With
Else 'Left and Top alignment
Controls(i%).Properties(MainProp) = CheckValue
End If
Next i%
End Sub
・SIZEALL.CLS
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "SizeAll"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
Option Explicit
Private Const WidthProp = "Width"
Private Const HeightProp = "Height"
Public SizeFlags As Integer '1=Width, 2=Height, 3=Both
Public VBInstance As VBIDE.Application
Public Sub AfterClick()
Dim ControlIterator As Object
Dim Control As VBIDE.ControlTemplate
Dim NewWidth As Long
Dim NewHeight As Long
Dim bHaveNewValues As Boolean
'If a property access fails, then just ignore it.
On Error Resume Next
bHaveNewValues = False
For Each ControlIterator In VBInstance.ActiveProject.ActiveForm.SelectedControlTemplates
Set Control = ControlIterator
With Control.Properties
If bHaveNewValues Then
If SizeFlags And 1 Then .Item(WidthProp) = NewWidth
If SizeFlags And 2 Then .Item(HeightProp) = NewHeight
Else
If SizeFlags And 1 Then NewWidth = .Item(WidthProp)
If SizeFlags And 2 Then NewHeight = .Item(HeightProp)
bHaveNewValues = True
End If
End With
Next
End Sub
●アドインの使われ方
このように、アドインは面白い。従来は細かい処理ができなかったことが不満だったVisualBasicなのに、このアドインを使えばここまで面白いことができてしまう。面白いということは、実際に何ができるかはアイデア次第であり、いろいろな可能性があるだろう。
たとえば、サンプルにもあったようにデザイン時の開発効率を上げるものや、フォームやコードを自動生成してくれるもの、さらにはユーザーの操作を監視して記憶しておく、ありがちだがバージョン管理など、考えれば実にいろいろなことができそうだ。
この分野もOLE Serverであるから、実はVisualBasicでなくてもアドインを作成することはできる。そういう意味でも、今後面白い製品が考えられ、発売されそうな分野である。私も、今から何か面白いコトを考えてみたい。