The implementation of COM in VTS is intended to mimic the natural object model currently used by VTS. As mentioned in Accessing COM Objects, opaque handle held in a variable instance that has a ValueType of "COM Client Interface", references an instance of a COM object. VTS itself does not act as a server for COM objects; rather, it acts merely as a client. In other words, VTS provides no COM objects; it simply provides the ability to use COM objects.
All properties and methods of the COM object are accessed using the scope resolution operator [SRO] "\". This presents the VTS programmer with both syntactic and semantic compatibility with existing VTS objects.
For example, a VTS module can be instantiated, creating an object instance. The object instance can be scoped into to examine or modify values within the object. An object instance may provide methods that can be invoked. Only the instantiation syntax differs between the same operations, performed on a COM object. This is a necessary departure in order to describe the parameters necessary to locate an object server and cause it to instantiate an object.
Consider the following code:
[
MyVTSObject
Module;
VTSObj;
COMObj;
]
Init [
If 1
Main;
[
{ Instantiate
a VTS object }
VTSObj =
MyVTSObject();
{
Instantiate a COM object
}
COMObj =
COMClient("Trihedral.Widget");
]
]
Main
[
]
Firstly, an instance of the VTS module "MyVTSObject" is instantiated. The object value returned from the instantiation is stored in variable "VTSObj". Next, an instance of the COM object identified by the ProgID "Trihedral.Widget" is instantiated, and the COM Client Interface value is stored in variable called "COMObj".
Suppose that we wish to modify the contents of a value, held in the variable "MyValue" in the VTS object created above. We could use code such as:
VTSObj\MyValue = 42;
Similarly, if the COM object instantiated in the above code had a property called "MyValue", we would use code such as:
COMObj\MyValue = 42;
This is termed a "property set" operation.
Reading the contents of a value in a VTS object, or a property in a COM object is also similar:
VTSResult = VTSObj\MyValue;
COMResult =
COMObj\MyValue;
For the COM object, this is termed a "property get" operation.
These operations can be combined:
VTSObj\MyValue2 += VTSObj\MyValue;
COMObj\MyValue2 +=
COMObj\MyValue;
Note that the normal expression operators [+= in this case] can be used just as easily with COM properties as with VTS values.
An invocation of a method in a COM object is no different from invoking a method of a VTS object:
VTSObj\MyValue2 = VTSObj\GetSomething(1,
2);
COMObj\MyValue2 = COMObj\GetSomething(1, 2);
In the first of the above two lines of code, a method contained in VTS object instance is called with two numeric parameters. In the second, the same thing is happening with a COM object.
In the code above, the VTS object was "launched". A VTS object will remain running as long as its caller remains running. A COM object will remain running as long as there is a valid reference to it. VTS objects can, however, be "called" from steady state. In this case, the VTS object remains running until the state containing the call stops. Even if the VTS object terminates, it will restart as long as the steady-state call remains running. A similar situation exists with COM objects. If a COM object is called from steady state, it will remain running as long as the steady-state statement remains running. A change of state will stop the COM object, even if there are other references held on it; in other words, the COM Client Interface value returned from the COMClient call is assigned elsewhere. Any variable value holding references to the COM object will be automatically invalidated when the COM object stops:
[
MyVTSObject
Module;
VTSObj;
COMObj;
COMObjName;
]
Init
[
If 1 Main;
[
COMObjName =
"Trihedral.Widget";
]
]
Main
[
{ Instantiate a VTS object }
VTSObj = MyVTSObject();
{ Instantiate a COM object
}
COMObj =
COMClient(COMObjName);
If Trigger
Done;
]
Done [
{ Both the VTS object and the
COM object are now stopped }
]
Like steady-state VTS object calls, a steady state COM object call will re-trigger if its return value changes or any parameters to it change. In the case of a COM object, the return value would only change if some external event caused the COM object to be lost (e.g. communication failure, or if the parameter(s) to the COMClient statement caused the COM object to be destroyed). In the above code, changing the value of COMObjName would cause the existing COM object to be destroyed, the value of COMObj to be invalidated, and a new COM object to be constructed. Once the new object has been constructed, the value of COMObj will change to hold the COM Client Interface value for the new object.
Whether the object is a VTS object or a COM object, properties and methods may also be accessed in both steady-state statements and scripts, but note that a property get in a steady-state statement will only evaluate once, as, unlike VTS values, there is no automatic trigger from a COM object that a property value has changed. Instead, COM objects employ "events" to indicate changes, and these events may call VTS subroutines.
Consider the following code:
[
Changed
Module;
COMObj;
Latest;
]
Init [
If 1
Main;
[
{ Instantiate
a COM object }
COMObj = COMClient("Trihedral.Widget", Invalid, Self(),
Self(), Self());
]
]
Main
[
If Watch(Valid(Latest),
Latest);
[
...
]
]
<
Changed
(
NewValue;
)
ChangedEvent
[
If watch(1);
[
Latest =
NewValue;
Return(0);
]
]
>
Note that the COMClient statement has grown four extra parameters. The first of these specifies the context in which it is permissible to instantiate the COM object. The second parameter specifies the scope in which event subroutines are to be found. All COM events have a name associated with them, defined by the COM object. If a subroutine module of the same name as the event exists in the scope specified in the third parameter to the COMClient statement, that subroutine will be called each time the corresponding event occurs. The subroutine is called on the same thread as the incoming event from the COM object, and so therefore may occur concurrently with other scripts running in the same scope.
The event subroutine is entered with whatever parameters were supplied by the COM object and the return value is passed back to the COM object as a result code (an HRESULT). Zero is a "success" return value. Each COM object defines what parameters are provided and what it expects the return value to be under different error conditions.
The event subroutine is run in the scope of the parent specified in the fourth parameter of the COMClient statement; therefore, any non-local variables referenced in the subroutine are resolved to values within that scope. In the above example, "Latest" is resolved to the value of "Latest" within the module that made the COMClient instantiation. The fifth parameter specifies the caller scope that is set up for the subroutine invocation. While this is normally meaningless for a subroutine, it can be used to pass "auxiliary" scope to the subroutine, adding flexibility.
The parent and caller parameters are optional, and if not specified, will default to Self(), Self(). The event subroutine search scope parameter is also optional; however, failure to specify a valid search scope, or subsequent invalidation of that parameter, prevents event subroutines from being called. By specifying a variable as the event subroutine search scope parameter, you can enable and disable event subroutine calls, or even move the search scope for them.
The code above, then, uses a COM event to re-trigger the single statement in state Main when the COM object raises a "Changed" event that changes the value of "Latest". This is the primary method by which COM objects cause event-driven processing in VTS.