←
▼
▲
Class_Terminate (デストラクタ) メソッドは、どの変数からもオブジェクトが参照されなく
なったとき(参照カウントが0になったとき)に呼び出されます。 このとき、オブジェクトは
削除されたと見なされます。
WScript.Quit が呼ばれたとき、ローカル変数に Set されたオブジェクトの
Class_Terminate は、呼ばれません。 実行時エラーになったときは呼び出されます。
グローバル変数のオブジェクトの Class_Terminate なら、WScript.Quit が呼ばれた
ときでも、呼ばれます。
VBScript は、例外処理構文が使えないため、Finally ブロックを記述できません。
On Error Resume Next は、サブ・プロシージャのエラーをキャッチできますが、
Finally ブロックにジャンプする機能がありません。
Finally ブロックに似た記述をする
Class_Terminate で、エラーが発生する可能性のある処理は行わないでください。
参考
参考
通常、Class_Terminate が呼ばれたら、オブジェクトは削除されたことになりますが、
厳密には、オブジェクトはメモリ上に存在し、オブジェクトを参照することはできます。
これは同時に複数のオブジェクトが削除されたときに、それぞれの Class_Terminate
が呼ばれるのですが、すでに Class_Terminate が呼ばれたオブジェクトを、別の
Class_Terminate から参照することができるところから分かります。
オブジェクト
変数 A
変数 B
Set A = new ClassA '// ClassA のオブジェクトを生成
Set B = A
A = Empty
変数 B
オブジェクト
B = Empty '// Class_Terminate が呼ばれる
上記は、変数に Empty を代入することで、オブジェクトを参照しなくなっていますが、
Empty 以外の値や、別のオブジェクトを参照するようになっても参照しなくなります。
関数の中のローカル変数であれば、関数から返っても参照しなくなります。
関連
←
▼
▲
Option Explicit
Class ClassA
Public Name
Public Ref
Public Del
Private Sub Class_Terminate()
If not IsEmpty( Del ) Then _
WScript.Echo Name + "::Class_Terminate .Del = Empty" : Del = Empty
WScript.Echo Name + "::Class_Terminate"
End Sub
End Class
'// グローバルでは、生成した順番で Class_Terminate が呼ばれます。
Dim g
Dim a : Set a = new ClassA : a.Name = "a"
Dim b : Set b = new ClassA : b.Name = "b"
If 0 Then Set a.Ref = b
If 0 Then Set b.Ref = a
'// グローバルのオブジェクトに参照関係があっても、
'// Class_Terminate が呼び出される順序に影響しません。
main
Sub main()
'// ローカルでは、生成した順番と逆に Class_Terminate が呼ばれます。
Dim c : Set c = new ClassA : c.Name = "c"
Dim d : Set d = new ClassA : d.Name = "d"
Dim e : Set e = new ClassA : e.Name = "e"
If 0 Then Set c.Ref = d '// c, d の順で呼ばれるようになります
If 0 Then Set a.Ref = e '// a, e の順で呼ばれるようになります
Set g = new ClassA : g.Name = "g" '// b の後で呼ばれます
Set a.Del = new ClassA : a.Del.Name = "a.Del" '// 後で呼ばれます
Set e.Del = new ClassA : e.Del.Name = "e.Del" '// 即時呼ばれます
If 0 Then
WScript.Quit 4 '// c,d,e の Class_Terminate は呼ばれません。
'// ただし、a.Ref = e を実行したら e の
'// Class_Terminate は、a の後に呼ばれます。
Else
unknown_symbol_error
End If
End Sub
グローバルの変数に格納したオブジェクトは、格納した順番で Class_Terminate が呼ばれます。
ローカルの変数に格納したオブジェクトは、格納した順番と逆に Class_Terminate が呼ばれます。
A から B を参照している(A のプロパティで B を参照している)場合、A、B の順に
Class_Terminate が呼ばれます。 ただし、グローバルのオブジェクト同士に参照関係があっても
無くても、呼び出される順番は変わりません。
検証コード
Class_Terminate の中で、オブジェクトの参照カウントをゼロにした場合も、参照関係と同様に、
ローカルでは即時呼ばれますが、グローバルでは後で呼ばれます。
デバッガで Class_Terminate の中でブレークしたときに、コールスタックを見ると、削除される
ローカルオブジェクトが所属する関数ではなく、その関数を呼び出している関数から、
Class_Terminate が呼ばれるように見えます。
←
▼
▲
Class ClassA
Private Sub Class_Terminate()
WScript.Echo Err ' 0 と表示します
NO_OBJ.ERROR ' エラー
WScript.Echo Err ' 実行しない
End Sub
End Class
Dim m : Set m = new ClassA
m = Empty
'// ここで、エラーメッセージが表示されます。
'// On Error Resume Next のとき、または、デバッガに接続しているときは、
'// エラーメッセージが表示されません。
WScript.Echo Err ' 500 と表示します。 Resume Next したかのような動き
On Error GoTo 0
WScript.Echo Err ' 0 と表示します
Class_Treminate の中でエラーが発生したら、Class_Terminate の中は、一般の関数と同じ
動きをします(エラー時のようなリターンが発生します)が、すぐにエラーメッセージを表示して、
Class_Terminate を呼び出した場所の続きから実行します。(エラー時のようなリターンは、
発生しません)
このため、エラーが発生して Class_Treminate で必要な処理が実行されなかったことを
見逃す可能性が高く、問題です。 特に cscript.exe で実行したときは、On Error Resume
Next していないときは、エラーメッセージが表示されるのですが、続きの実行によって
スクロールアウトして、エラーが発生したことがユーザに知らされません。 そのまま、
たとえば誤った計算結果を使ってしまい、後で問題になるかも知れません。
この問題が発生しないよう、MsgBox や ErrorCheckInTerminate でエラーを確実にユーザ
に知らせてください。
Class ClassA
Private m_bFinished
Public Sub Finish()
WScript.Echo "Finish"
m_bFinished = True
End Sub
Private Sub Class_Terminate()
Dim en,ed : en = Err.Number : ed = Err.Description
On Error Resume Next '// This clears the error
If en <> 0 Then WScript.Echo "Cancel"
WScript.Echo "Release"
ErrorCheckInTerminate en
If en = 0 and not m_bFinished Then NotCallFinish
On Error GoTo 0 : If en <> 0 Then Err.Raise en,,ed '// This sets en again
End Sub
End Class
悪いコード
良いコード
→ T_Finish.vbs
検証スクリプト
Sub NotCallFinish()
Stop : MsgBox "[ERROR] not call Finish" : WScript.Quit 1
End Sub
Sub ErrorCheckInTerminate( en )
If Err.Number <> 0 Then
Stop : MsgBox "ERROR(" & Err.Number & ") " & Err.Description & _
" in Class_Terminate"
End If
End Sub
WScript.Echo "Finish"
WScript.Echo "Cancel"
WScript.Echo "Release"
→ T_XML_Manually.vbs # [T_OpenForReplaceXML_Err_Manually]
→ T_TerminateRaise_Manually.vbs # Main
←
▼
▲
Set obj = Wscript.CreateObject("Excel.Application")
With obj
.Visible = True
End With
←
▼
▲
VBScript など、Empty で必ず初期化されるクラスの場合、Empty ではないもの
のみ有効とする。
Dim configs = new TheConfigs
configs.m_Param1 = 10
configs.m_Param2 = "abc"
obj.SetConfigs configs
Class TheConfigs
Public m_Param1
Public m_Param2
Public m_Param3
End Class
configs.Param3 は Empty なので無効
サンプル
複数の .vbs ファイルで共有するときは、上記のクラスのオブジェクトを取得する
関数を用意するとよいでしょう。
クラスの存在チェックは、この取得関数を実行するときに行われます。 C言語
のように、ヘッダを通しておくような対応は不要です。
Class TheClass
Public m_Configs ' as TheConfigs
Private Sub Class_Initialize
Set Me.m_Configs = new TheConfigs
End Sub
Public Sub SetConfigs( Configs )
With Configs
If not IsEmpty( .m_Param1 ) Then Me.m_Param1 = .m_Param1
If not IsEmpty( .m_Param2 ) Then Me.m_Param2 = .m_Param2
If not IsEmpty( .m_Param3 ) Then Me.m_Param3 = .m_Param3
End With
End Sub
End Class
実装例
Class TheConfigs
Public m_Param1
Public m_Param2
Public m_Param3
Public Property Get m_Param4
m_Param4 = m_Param1 + m_Param2
End Property
End Class
部分Empty構造体パラメータのクラスは、そのまま設定値を格納するクラスとして
使うことができます。 派生属性を Get プロパティで定義するとよいでしょう。
メンバ変数ぱプロパティの名前の先頭に m_ が付いていると、grep 時に適切な
場所を見つけやすくなります。 ローカル変数やクラス名とバッティングしなくなり
ます。
←
▼
▲
オブジェクト指向を踏まえたプログラムを VBScript 言語で記述するときの独自標準です。
ver1.0 は、vbslib 3.00 に付属のドキュメントを参照してください。
関連
→ COOL : C 言語によるオブジェクト記述法
オブジェクトの配列を生成する
ブレークポイントなどデバッグ機能の有効無効を制御する
メソッドやプロパティのセット、Java のインターフェイスに相当
まず、言語仕様を踏まえる
オブジェクトのデータを XML 形式にしたもの
静的オブジェクトの取得、演算器要求
静的オブジェクトの一覧
委譲先のオブジェクト
名前の参照、一般名 (Name) と正式名 (TrueName)
オブジェクト・ファイルからオブジェクトを取得する
オブジェクト・フォルダからオブジェクトを取得する
オブジェクト・ファイルに関する情報
→ new_ClassA_Object
オブジェクトを生成する
ファイル形式のオブジェクトを操作する
メイン .vbs ファイルに記述するユーザー定義データ
動的に追加定義するメンバー
検討中
イベントに応答するメソッド
クラスの定数
←
▼
▲
使用例:
Dim objs : new_ClassA_Array objs, 3 '// [out] objs
定義例:
Sub new_ClassA_Array( out_Objs, N )
Dim i:ReDim out_Objs(n-1):For i=0 To N-1:Set out_Objs(i)=new ClassA :Next
End Sub
ClassA
ClassA
obj(0)〜obj(2) に、ClassA のオブジェクトを 3つ生成します。
ClassA
objs
objs
3
Sub new_ClassA_Array( out_Objs as array, N as variant )
複数のオブジェクトの生成
複数のオブジェクトの生成と、Array を使った値の初期化
objs
objs
ClassA
ClassA のオブジェクトを 1つ生成し、"ABC", 1 を使って値を設定します。
Dim objs : new_ClassA_Array objs, Array( "ABC", 1 ) '// [out] objs
使用例:
参考
ClassA のオブジェクトを 2つ生成し、1つ目のオブジェクトは、"ABC", 1 を使って値を設定し、
2つ目のオブジェクトは、"DEF", 2 を使って値を設定します。
Dim objs : new_ClassA_Array objs, Array( _
Array( "ABC", 1 ),_
Array( "DEF", 2 ) ) '// [out] objs
使用例:
複数のオブジェクトの生成と、XML を使った値の初期化
使用例:
ClassA のオブジェクトを 1つ生成し、"A", "1" を使って値を設定します。
objs
Dim objs : new_ClassA_Array objs, "<ClassA name='A' num='1'/>" '// [out] objs
Set out_Obj = objs(0)
文字列は XML とします。 ルートタグは省略できるようにします。
IXMLDOMElement の場合は、子ノードが自分のクラスなら生成します。 複数の場合もあり。
Dim objs : new_ClassA_Array objs, _
"<ClassA name='ABC' num='1'/>" +_
"<ClassA name='DEF' num='2'/>" '// [out] objs
ClassA のオブジェクトを 2つ生成し、1つ目のオブジェクトは、"ABC", "1" を使って値を設定し、
2つ目のオブジェクトは、"DEF", "2" を使って値を設定します。
使用例:
参考
←
▼
▲
Class ClassI '// defined_as_interface
Public Sub MethodA( Param1 ) : End Sub
Public Sub MethodB() : End Sub
End Class
1つのオブジェクトに複数のインターフェイスを取得でき、多態する記述法です。
多態(ポリモアフィズム)とは、同じ名前の関数やプロパティを呼び出しても、インスタンスに
よって、呼んだ先の処理内容が異なることです。
VBScript の変数は variant 型で、インターフェイスを定義しなくても多態することができますが、
プロパティやメソッドのセットを明示していた方が、アトム・クラスを作成しやすくなります。
→ ポリモアフィズム(多態性)
データ構造
Dim obj as ClassI
インターフェイスを
参照する変数
オブジェクトの
インターフェイス
オブジェクト(アトム)
オブジェクトのインターフェイス。
何もしないサンプル・クラスを記述することで、インターフェイスを明示します。
オブジェクトのクラス。アトム・クラス。
ClassI の定義をコピーして定義します。 インターフェイスと同じメソッド名があります。
Class ClassA '// has_interface_of ClassI
Public Sub MethodA( Param1 ) : echo "MethodA" : End Sub
Public Sub MethodB() : echo "MethodB" : End Sub
End Class
ClassA
ClassI
定義サンプル:
定義サンプル:
使用サンプル
Dim a : Set a = new ClassA
Dim b : Set b = new ClassB
FuncUsingClassI a
FuncUsingClassI b
Sub FuncUsingClassI( i ) : i.MethodA() : End Sub
ClassI を処理する関数へ、ClassA と ClassB のインスタンスを渡す
関連
参考
←
▼
▲
イベント(あるオブジェクトの状態変化)が発生したときに、応答するオブジェクトが複数で
あることは、めずらしくありません。
しかし、あるクラスの、すべてのイベントに応答することは、まれなので、通常、複数の
登録された関数を呼び出すメンバー変数を使用します。
→ 複数の登録された関数を呼び出すメンバー変数、イベント
参考
複数のメンバー関数
書きかけ
←
▼
▲
Function get_ObjectA() as ObjectA
関連
を取得します。 もし、存在しなければ、
関数にすることで、オブジェクト(インスタンス)をカプセル化します。
何回呼び出しても、同じオブジェクトを返します。 ユーザーは、生成、削除を意識しません。
オブジェクトの定義は、get_ClassA 関数にステップインすると分かります。
Function get_Object( Name as string ) as Object
Dim g_ClassA
Function get_ClassA() '// has_interface_of ClassI
If IsEmpty( g_ClassA ) Then _
Set g_ClassA = new ClassA : ErrCheck
Set get_ClassA = g_ClassA
End Function
Function get_Object( Name )
Dim get_func : Set get_func = GetRef( "get_" + Name )
Set get_Object = get_func()
End Function
Dim g_ClassA
Function get_ClassA() '// has_interface_of ClassI
If IsEmpty( g_ClassA ) Then
Set g_ClassA = new ClassC : With g_ClassA : ErrCheck
.Name = "ClassA"
.MethodA = GetRef( "ClassA_methodA" )
End With
End If
Set get_ClassA = g_ClassA
End Function
Sub ClassA_methodA( Me_, ParamA )
:
End Sub
定義サンプル: ClassA オブジェクト(ClassA クラスのインスタンス)を返す
定義サンプル: ClassC のインスタンスのプロパティを設定して返す
定義サンプル: クラス名を文字列で指定してインスタンスを返す
Dim g_obj as ClassI
インターフェイスを
参照する変数
オブジェクト(アトム)
get_ClassA 関数
クラスを新たに作成しなくても、GetRef でメソッドに多態性を持たせることができます。
ClassI
ClassA
ClassA
ClassA
ClassA
ClassA
ClassA
ClassA
→ ClassA_get マクロ (clib)
使用サンプル:
Dim sample : Set sample = get_Sample()
sample
sample
Sample
参考
などに入っている静的オブジェクト (または演算器的なオブジェクト)
Dim sample : Set sample = get_Object( "Sample" )
sample
sample
Sample
を返すか、エラーにします。
←
▼
▲
get_Sample_FromFile( "Sample1.xml" ).Method1
Sample Sample1.xml Method1
Function get_ClassA_FromFile( Path as string ) as object
一般的なファイルを操作するオブジェクトを生成します。
【引数】
Path
返り値
ファイルのパス
ファイルをオブジェクトとして操作できるオブジェクト
サンプル:
一般的なファイル(*.xml)
一般スクリプトファイル(*.vbs)
ScriptA.vbs
Sample.xml
取得
←
▼
▲
Dim sample : Set sample = get_ObjectFromFile( "ObjectX_obj.vbs", "ObjectX" )
オブジェクト・ファイル(*_obj.vbs)
一般スクリプトファイル(*.vbs)
ScriptA.vbs
ObjectX_obj.vbs
多くのプログラムは、あるデータファイルを読み込んで処理しますが、VBScript では、データファイルの
代わりに、スクリプト・ファイルを指定して、その中で定義されているオブジェクトを使うこともできます。
sample
sample
使用サンプル: 特定のオブジェクトを取得する
get_ObjectFromFile 関数は、下記の実行に相当します。
include "ObjectX_obj.vbs"
Set get_ObjectFromFile = get_ObjectX()
参考
取得
get_ObjectX 関数の中に
オブジェクトの定義があります
←
▼
▲
使用サンプル: 特定のインターフェイスを持ったオブジェクトの一覧を取得する
参考
Dim samples
get_ObjectsFromFile "Lib\*_obj.vbs", "ClassI", samples '// [out] samples
samples
samples
定義サンプル
get_ObjectsFromFile 関数は、内部で "Lib\*_obj.vbs" にマッチしたファイルをインク
ルードして、それぞれの .vbs ファイルで定義されている get_StaticObjects 関数を
呼び出します。 get_StaticObjects 関数は、ClassI インターフェイスを持ったオブジェクト
を取得する get_Object 関数を呼び出します。
オブジェクト・ファイル(*_obj.vbs)
一般スクリプトファイル(*.vbs)
ScriptA.vbs
ObjectX_obj.vbs
多くのプログラムは、あるデータファイルを読み込んで処理しますが、VBScript では、データファイル
の代わりに、スクリプト・ファイルを指定して、その中で定義されているオブジェクトを使うこともできます。
ロード
フォルダを指定して、すべてのオブジェクトをリストアップすることができます。
ObjectX_obj.vbs
オブジェクト・フォルダ
サンプルファイル
→ T_NameList フォルダ
→ T_NameList_vbslib フォルダ
←
▼
▲
オブジェクト・ファイル(.vbsファイル) に入っている静的オブジェクトの一覧は、
get_StaticObjects という関数名で取得できるよう標準化します。
out_Obj に返すオブジェクトは、InterfaceName に入った名前のインターフェイスを
持っているもの、または、クラスが InterfaceName のものとします。
out_Obj が Empty を返すときは、対応するオブジェクトが存在しないことを示します。
対応する静的オブジェクトが複数あるときは、out_Obj に配列を返します。
get_StaticObjects が呼ばれるときのカレント・フォルダは、不定です。
Sub get_StaticObjects( InterfaceName as string, out_Objs as variant )
要求する静的オブジェクトのインターフェイスの名前
InterfaceName
【引数】
オブジェクト・ファイル(.vbsファイル) に入っている静的オブジェクトの一覧を取得します。
out_Obj
(出力) 静的オブジェクト、またはその配列
get_StaticObjects 関数は、
関数から呼ばれます。
処理フロー:
get_ObjectsFromFile
include (オブジェクト・ファイルの1つ)
For (オブジェクト・ファイルのループ)
get_StaticObjects
get_StaticObjects関数は、それぞれのオブジェクト・ファイルで定義します。
複数のオブジェクト・ファイルで定義しても、get_ObjectsFromFile は、それぞれの
get_StaticObjects 関数を区別して呼び出します。
get_StaticObjects 関数は、
をユーザーが使えるようにするために、
ライブラリが実装する関数の1つです。 ライブラリから見れば、get_StaticObjects
関数は、オブジェクト・ファイルの中で定義している静的オブジェクトの一覧を、
コール元へ渡す処理をします。
Sub get_StaticObjects( InterfaceName, out_Obj )
If IsEmpty( InterfaceName ) or InterfaceName = "ClassI" Then
Set out_Obj = get_ClassA()
ElseIf InterfaceName = "ClassI2" Then
out_Obj = Array( get_ClassA(), get_ClassB() )
End If
End Sub
定義サンプル:
get_Object
参考
サンプルファイル
→ T_NameList フォルダ
→ T_NameList_vbslib フォルダ