(for Internet Explorer)
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 フォルダ