←
▼
▲
(src)
Sub get_ObjectsFromFile( GetFuncs as string or Array of Function,
InterfaceName as string, out_Objs as Array of Object )
静的オブジェクト・スクリプトファイルをすべて取得して一覧できるようにします。
【引数】
GetFuncs
InterfaceName
スクリプトのパス、または include_objs の out_GetFuncs
Empty または、取得するオブジェクトが持つインターフェース
out_Objs
取得したオブジェクトの配列
get_ObjectsFromFile "Lib\*_obj.vbs", "ClassI", m_Objs '// [out] m_Objs
サンプル: インクルードと同時に、静的オブジェクトを取得する
include_objs "Lib\*_obj.vbs", Empty, m_CreateFuncs '// [out] m_CreateFuncs
get_ObjectsFromFile m_CreateFuncs, "ClassI", m_Objs '// [out] m_Objs
サンプル: インクルードした後で、静的オブジェクトを取得する(ファイルを走査しない)
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_StaticObjects
関数を定義する必要があります。 get_ObjectsFromFile や include_objs は、別のスクリプト
ファイルであれば、同じ関数名でも異なる関数インスタンスになります。
参考
→ T_GetObject.vbs [T_IncludeObjs2] [T_IncludeObjs3]
テスト
←
▼
▲
参考
Function new_X( Name as string ) as object
文字列で指定した名前のクラスのオブジェクトを生成します。
Function new_ClassA()
Set new_ClassA = new ClassA
End Function
機能的には、"new_" + Name の関数を呼び出すだけなので、別途関数を用意する必要があります。
たとえば、Name = "ClassA" なら、new_ClassA の関数が呼ばれます。
(src)
ただし、多くの場合、get_Object の方が適していることが多いので、get_Object を使うことを検討
してください。
サンプル:
Dim obj : Set obj = new_X( "ClassA" )
引数は、なしにしてください。
←
▼
▲
Sub include_objs( Wildcard as string, Flags as Empty, out_GetFuncs as Array of Funcion )
静的オブジェクト・スクリプトファイルをインクルードします。
【引数】
Wildcard
Flags
スクリプトファイルのパス、またはフォルダパス
Empty を指定してください
out_GetFuncs
Empty、または、静的オブジェクトを取得する関数の集合
out_CreateFuncs が Empty ではないときのサンプル
include_objs は、インクルードのみ行います。 オブジェクトの生成は、new で生成するか、
get_ObjectsFromFile で行います。
(src)
サンプル: Lib フォルダをインクルードして、その中で定義された ClassA オブジェクトを生成する。
include_objs "Lib\*_obj.vbs", Empty, Empty
Set obj : Set obj = new ClassA : ErrCheck
→ new_X_array
Flags = Empty かつ out_GetFuncs = Empty なら、include で代用できます。
関連
out_GetFuncs は、静的オブジェクトを取得する関数の集合です。 get_ObjectsFromFile の第1引数
に渡すと、文字列を渡したときより高速に処理します。
→ T_GetObject.vbs [T_IncludeObjs1c]
テスト
→ T_GetObject.vbs [T_IncludeObjs3]
→ T_GetObject.vbs [T_IncludeObjsMulti]
→ T_GetObject.vbs [T_IncludeObjsMulti_NoWild]
→ T_GetObject.vbs [T_IncludeObjsEmptyArray]
←
▼
▲
Sub get_DefineInfoObject( in_out_Object as DefineInfoClass, FullPath as string )
単純な DefineInfo オブジェクトを生成します。
参考
DefineInfo オブジェクトに FullPath プロパティしか必要ないときのみ使えます。
他のプロパティがあるときは、
(src)
Dim g_SrcPath
If IsEmpty( g_SrcPath ) Then MsgBox "This is vbslib object file." : WScript.Quit
Dim g_Sample_vbs : get_DefineInfoObject g_Sample_vbs, g_SrcPath
'// g_Sample_vbs.FullPath
サンプル:
←
▼
▲
Function get_NameDelegator( Name as string, TrueName as string,
InterfaceName as string ) as InterfaceName_Delegator
指定のインターフェースを持った、正式名オブジェクトに委譲する、静的な一般名オブジェクトを取得します。
【引数】
Name
TrueName
一般名。 取得するオブジェクトの Name プロパティ
正式名。 取得するオブジェクトの委譲先 (*1)
InterfaceName
一般名オブジェクトと正式名〜が共通に持つ、インターフェースの名前 (*2)
(src)
一般名オブジェクト (*3)
返り値
(*2)
(*3)
Name と TrueName の組み合わせが初めてのときは、get_NameDelegator の内部で、
new_(InterfaceName)_Delegator 関数を呼び出して、オブジェクトを生成します。
返り値となるオブジェクトにアクセスすると、Name プロパティとオブジェクトの実体の違いを除けば、
正式名オブジェクトにアクセスします。
(ここでは一般名オブジェクト)を生成することができる、new_(InterfaceName)_Delegator
関数が定義されていること。 参考:new_X
と、(InterfaceName) インターフェースを持ったインスタンス
Class NameDelegator '// defined_as_interface
Public Name
Public Property Get TrueName() : TrueName = NameDelegator_getTrueName( Me ) : End Property
Public m_Delegate ' as ClassA or ClassB or string(before validated)
Public Property Get DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
End Class
Const F_ValidateOnlyDelegate = &h40000000
Dim g_bNeedValidateDelegate
Function NameDelegator_getTrueName( m )
Sub NameDelegator_validate( m, Flags )
Function NameDelegator_getXML( m )
DefineInfo プロパティは、オプションです。
Public Property Get DefineInfo() : Set DefineInfo = m_Delegate.DefineInfo : End Property
テスト
→ T_NameDelegator_vbslib フォルダ
参照
場面Aの関数
Name 以外は
参照
場面Aでの Name
場面Bでの Name
ある1つのインスタンス
TrueName
(正式名)
ClassI_Delegator クラス
(一般名)
場面Bの関数
ClassI
ClassI
ClassB
ClassI
ClassA
Name = "ClassN", TrueName = "ClassA"
Name = "ClassN", TrueName = "ClassB"
一般名で選択
参照
一般名と正式名の関係は、1対多、または、多対1があります。
(*1)
(InterfaceName) インターフェースを持った、静的な正式名オブジェクトを取得することができる
get_(TrueName) 関数が定義されていること。 参考:get_Object
get_NameDelegator 関数は、
ものです。 get_NameDelegator 関数は、get_(一般名) 関数の内部からのみ呼ばれるようにしてください。
の内部で呼ばれる get_(一般名) 関数の実装を支援する
サンプル:
Function get_GeneralObject()
Set get_GeneralObject = get_NameDelegator( "GeneralObject", "TrueName", "InterfaceName" )
End Function
Function get_TrueName() : ... : End Function
Function new_InterfaceName_Delegator() : ... : End Function
返り値となる一般名オブジェクトは、Name プロパティと委譲元となるオブジェクトの実体の違いを除けば、
同じ1つの正式名オブジェクトとして扱うことができます。 参考: 下記の図の「ある1つのインスタンス」
←
▼
▲
(src)
Sub new_ObjectFromStream( out_Obj as object, ClassName as string, Stream as variant )
ストリームから、オブジェクトを1つ生成します。
【引数】
out_Obj
ClassName
(出力) ストリームから生成した1つのオブジェクト、または Empty
生成するオブジェクトのクラス名 (下記)
Stream
XML ストリーム (下記)
ClassName 引数
XML のタグ名がクラス名に対応します。
XML のタグの中の属性まで一致するデータを元にオブジェクトを生成するときは、
XPath と同様に下記のようにします。
Set xml = LoadXML( "Sample.xml", Empty )
new_ObjectFromStream obj, "ClassA[@attr='value']", xml
参考
→ XPath の [@name='value'] (属性の値)
<Root><ClassA attr="value" ... /></Root>
VBScript コード
XMLデータ
Stream 引数
の引数に指定できるものを指定できます。
new_X に対応した関数の準備
内部で、new_X を呼び出しています。 new_X を使うのに必要な関数を用意する必要が
あります。 たとえば、ABC クラスなら、new_ABC 関数が必要です。
参考
テスト
→ T_ObjectFromStream フォルダ
←
▼
▲
(src)
Sub new_ObjectsFromStream( out_Objs as array of object, ClassName as string, Stream as variant )
ストリームから、オブジェクトをいくつか生成して、配列に格納します。
【引数】
out_Objs
ClassName
(出力) ストリームから生成したオブジェクトが入った配列
生成するオブジェクトのクラス名
Stream
XML ストリーム
1つもオブジェクトを生成しなかったときでも、out_Objs には配列が格納されます。
ClassName, Stream については、
テスト
→ T_ObjectFromStream フォルダ
←
▼
▲
Class NameOnlyClass
Public Name
Public Delegate
End Class
Name 変数(任意の型の識別値)と、Delegate 変数(指しているオブジェクトなど)の
対応関係を表すクラスです。
Delegate 変数を使わずに、Name だけのオブジェクトとして使うこともできます。
(src)
関連
に対応しています。
←
▼
▲
Function new_NameOnlyClass( in_Name as string, in_Delegate as variant ) as NameOnlyClass
NameOnlyClass のオブジェクトを生成します。
(src)
←
▼
▲
Class JoinedClass
Public Left
Public Right
End Class
(src)
配列番号 0 と 1 で対応関係を表すことができます
→ PointerPairClass (clib)
関連
Left 変数の値と Right 変数の値の対応関係を表すクラスです。
←
▼
▲
Function new_JoinedClass( Left as variant, Right as variant ) as JoinedClass
JoinedClass のオブジェクトを生成します。
【引数】
Left
Right
生成するオブジェクトの Left プロパティに設定する値
生成するオブジェクトの Right プロパティに設定する値
返り値
生成された JoinedClass のオブジェクト
ソース
→ vbslib.vbs
←
▼
▲
オブジェクトに数値の ID を割り当て、ID からオブジェクトが取得できるようにします。
Set root = new SampleRootClass
Set element = new SampleElementClass
'// Link
element.RootObjectID = root.ObjectID
Set root.Element = element
'// Call a method
element.Method
Class SampleRootClass
Public ObjectID
Public Element
Private Sub Class_Initialize()
Me.ObjectID = g_ObjectIDs.Add( Me )
End Sub
Private Sub Class_Terminate()
g_ObjectIDs.Remove Me.ObjectID
End Sub
End Class
Class SampleElementClass
Public RootObjectID
Public Sub Method()
Set root = g_ObjectIDs( Me.RootObjectID )
End Sub
End Class
サンプル
が期待通りに動作するようになります。
SampleRootClass
SampleElementClass
Element メンバー
RootObjectID メンバー (g_ObjectIDs 経由)
g_ObjectIDs
g_ObjectIDs
g_ObjectIDs
→ vbslib.vbs
ソース
.Add
.Remove
.Item
新規 ID を取得します
ID を返却します
Add できる最大数、同時に存在できる ID の数は 0x7FFE です。
同時に存在できる ID の数を超えて、Remove と Add を繰り返すと、返却された ID を再利用します。
同じオブジェクトに複数の ID が割り当てられる状況になっても、エラーにはなりません。
渡された ID に対応するオブジェクトを返します
関連
←
▼
▲
相互参照しているオブジェクトに対して、適切に
p
p
ユーザーの変数
ユーザーの変数
所属オブジェクトのメンバー変数やメソッドにアクセスするときは、ハンドルのメンバー変数 p を
介する必要があります。
相互参照
関連
→ ガーベージコレクション (GC)
参考
一般に、関数が終了したときにオブジェクトを参照している変数がなくなると、ガーベージコレクター
(1)
(2)
(3)
(4)
(5)
(7)
(6)
内部で相互参照するグループに LifeGroupClass を使うと下記の図のような構成になります。
グループに所属しているオブジェクトの Class_Terminate が呼び出されるタイミングは、
グループに所属しているオブジェクト(ルート オブジェクトも含む)を参照するすべてのハンドルが、
すべてのユーザーの変数から参照されなくなったときです。
( ) の中の数字は、削除される順番です。
(1) と (3)、(2) と (4)、(5) と (7) の順番は入れ替わることがあります。
(1) だけが削除されたときは、(3)が削除されるまで、(4)以降のオブジェクトは残ります。
によって
プログラム終了時に呼ばれます。 LifeGroupClass は、相互参照しているオブジェクトでも
Class_Terminate が呼び出されるようにします。
が呼ばれますが、相互参照していると Class_Terminate は呼ばれません。
このように相互参照しているオブジェクトとハンドルのオブジェクトを分けることで、
参照している変数がなくなったことをハンドルで検出できるようにしています。
オブジェクト ID を経由することで Class_Terminate を呼び出す
A
B
相互参照
→ T_Dic.vbs
T_LifeGroupClass
→ vbslib.vbs
テスト
ソース
注意
メンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要があります。
のキーや配列番号で参照することで、相互参照が原因で Class_Terminate が呼ばれなく
なる状況を回避することができます。
root.p.Member
辞書のキーや配列番号
B
A
オブジェクトの Set
が呼ばれるようにします。
p を介する必要がないが、多少手間がかかるライフサイクル管理
←
▼
▲
Class RootClass
Public p '// as RootBodyClass : Target object of this handle
Private Sub Class_Initialize()
Set group = new LifeGroupClass
group.AddHandle Me, new RootBodyClass
End Sub
Private Sub Class_Terminate()
If not IsEmpty( Me.p.LifeGroup.Group ) Then _
Me.p.LifeGroup.Group.AddTerminated Me.p
End Sub
End Class
使用サンプル
RootBodyClass
相互参照しているオブジェクトのグループのうち、最初に生成するオブジェクトのハンドルの
クラスは、下記のように定義します。 RootBodyClass を最初に生成するオブジェクトのクラス名
に変更すること以外は、そのままにしてください。
Class RootBodyClass
:
Public LifeGroup '// Don't write except for LifeGroupClass ■追加
Public Sub DestroyReferences() '// ■追加
Me.Reference = Empty
End Sub
End Class
最初に生成するオブジェクトのクラスは、メンバー変数 LifeGroup と、
を定義してください。
定義サンプル
Set root = new RootClass
root.p.Member
と同じメンバーを持ちます。
グループのルート(下記の RootClass)は、
RootClass
RootBodyClass
Reference
p
メンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要があります。
… p の追加が必要
相互参照しているオブジェクトを使用するコードに、この変更を要求することがどうしても
できないなら、p を介すだけのメソッドやプロパティを定義します。
←
▼
▲
相互参照しているオブジェクトのグループに所属するオブジェクトのメソッドから、グループに
所属させるオブジェクトを新しく生成するときは、下記の CreateMember メソッドのように、
グループに追加します。 そうしなければ、グループが削除されるタイミングで、生成した
オブジェクトに対して DestroyReferences が呼ばれなくなります。
Class RootBodyClass
:
Public LifeGroup '// Don't write except for LifeGroupClass
Function CreateMember()
Set CreateMember = Me.LifeGroup.Group.Add( new MemberClass )
:
End Function
End Class
定義サンプル
MemberClass
Class MemberClass
:
Public LifeGroup '// Don't write except for LifeGroupClass ■追加
Public Sub DestroyReferences() '// ■追加
Me.Reference = Empty
End Sub
End Class
グループに所属するオブジェクトのクラスは、メンバー変数 LifeGroup と、
定義サンプル
を定義してください。
です。
CreateMember メソッドの返り値は、
使用サンプル
Set root = new RootClass
Set member = root.p.CreateMember()
MemberClass
すぐに CreateMember の返り値を参照しなくなっても、DestroyReferences がすぐに呼ば
れることはありません。 グループに所属するすべてのオブジェクトが参照されなくなった
ときに呼び出されます。
ルートのハンドルが参照されなくなったときに、グループに所属する一部のオブジェクトが
削除されても構わないときは、ハンドルの代わりに生成したオブジェクトを返しても構いま
せん。
←
▼
▲
相互参照しているオブジェクトを出力するときは、下記の SearchMember メソッドのように、
を生成して返してください。
Class RootBodyClass
:
Public LifeGroup '// Don't write except for LifeGroupClass
Function GetReference()
Set GetReference = Me.LifeGroup.Group.Add( Me.Reference )
End Function
End Class
定義サンプル
使用サンプル
Set root = new RootClass
Set member = root.p.GetReference()
を呼び出し、
GetReference
ただし、グループの中で参照するときは、LifeHandleClass を生成しないでください。
そうしなければ、ルートのハンドルが削除されるタイミングで、グループに所属する一部の
オブジェクトが削除されてしまいます。 ルートのハンドルが参照されなくなったら、グループ
に所属する一部のオブジェクトが削除されても構わないときは、LifeHandleClass を生成
しなくても構いません。
←
▼
▲
Sub (グループのに所属するオブジェクトのクラス)::DestroyReferences()
相互参照を切ります。
グループのユーザーは、本メソッドを直接呼び出さないでください。
Class_Terminate メソッド(デストラクター)が呼び出されて、オブジェクトが
破棄されるように、相互参照を切ってください。 つまり、相互に参照している
関係にあるメンバー変数に Empty や Nothing を代入してください。
から呼ばれます。
Class RootBodyClass
:
Function DestroyReferences()
Me.Member = Empty
End Function
End Class
定義サンプル
←
▼
▲
(グループ)に所属する、相互参照しているオブジェクトを参照できるハンドル。
LifeHandleClass が参照している、相互参照しているオブジェクトを、グループの外にある変数
に直接格納しないでください。 もしそうしてしまうと、すべてのハンドルへの参照が無くなったときに、
相互参照が切れたオブジェクトになり、正しく動作しなくなります。
メンバー
生成
相互参照しているオブジェクトを参照します。
を呼び出します。
オブジェクトのメンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要が
あります。
Set root = new RootClass
Set member = root.p.CreateMember() '// member as LifeHandleClass
member.p.Method
サンプル