Using the Caché ActiveX Gateway
Using Caché Activate
Go to:

This chapter describes how to create a Caché wrapper class for an ActiveX component and how to use this wrapper class within an application.

The Caché Activate Wizard
The Caché Activate Wizard automatically creates one or more Caché wrapper classes for a given set of ActiveX interfaces.
To use the Wizard:
  1. Start Caché Studio (you can do this from the Caché cube menu).
  2. Create a new project for your application.
  3. Using the Activate Wizard command within the Tools > Add-Ins... menu, start the Activate Wizard:
    Enter the package name you wish to use for the generated classes, and press the Next button
  4. The Wizard displays a list of available COM interfaces (these are interfaces available on the Caché server, not the machine on which Studio is running):
    Choose one or more interfaces and press the Next button.
  5. The Wizard automatically generates wrapper classes within the selected package and compiles them:
64–bit ActiveX Controls
On a 64-bit version of Windows with a 64-bit cache, you can call 64-bit ActiveX controls. This will not enable a 64-bit cache to use 32-bit ActiveX controls (which is impossible due to operating system constraints). However, some companies are now releasing 64-bit versions of their existing 32-bit controls, allowing customers to migrate to 64-bit systems.
Using the Generated Wrapper Classes
The classes that are generated in Caché are proxy classes for the COM objects. Once the classes have been generated and compiled, you can then use them in Caché applications.
For example, using the Activate Wizard, you can generate wrapper classes for the Microsoft SysInfo Control, which provides some information regarding system resources.
The Caché Activate Wizard creates the following classes for the SysInfo COM object:
Example: Accessing a Property
Here is an example that uses the SysInfo wrapper object to obtain the remaining battery life percentage for a laptop computer:
 Set obj = ##class(Activate.SysInfoLib.SysInfo).%New()
 Write obj.BatteryLifePercent,!
 Set obj = ""
The object is created in the same manner as any other within Caché. The BatteryLifePercent property is written out and finally the object is closed.
Example: Enumerating COM Interfaces
The Caché Activate Wizard enumerates the type libraries on a Caché Server by using a COM object called TL.dll (or TL64.dll on 64-bit systems. The file is placed in the <CacheRoot>\Bin directory and automatically registered during Caché installation). The Caché classes that are generated from this object are preloaded into the %Activate.TLLib package.
These classes consists of:
Here is an example Caché ObjectScript method that enumerates the type libraries on the system by using these classes. A concrete instance of the Utils class is created and the objlibs property is retrieved. Notice that the Item property is called via the ItemGet method, because Caché does not currently support calculated, indexed properties:
Class MyApp.ActivateTest
// ...

/// Demonstrate COM object Access and provide type library enumeration  ;
ClassMethod ListTypeLibs() {
    Set objUtils = ""
    Set objLibs = ""
    Set $ZT = "tlerr"
    Set objUtils = ##class(%Activate.TLLib.Utils).%New()
    Set objLibs = objUtils.Libraries
    For i = 1:1:objLibs.Count {
       Set tld = objLibs.ItemGet(i)
       // tld is a | delimited string
       Write !, $Piece(tld,"|"), !, $Piece(tld,"|",2), !, $Piece(tld,"|",3), !!

xit      ; Exit point          
    If objLibs'="" Set objLibs = ""
    If objUtils'="" Set objUtils = ""

tlerr      ; Exception handler
    Set $ZT = ""
    Goto xit
Special Considerations for Properties
As shown in the previous example, in COM, some properties have parameters. Furthermore, some objects have what is known as a “default property,” which means you can reference that property without specifying its name explicitly.
For example, collections (as in the previous example) always have the Count and Item property. You will note that the Item property is (obviously) not a method but that it does take an argument. An Item property is often the default property of a collection. Consider an example with Microsoft Excel. If we have a collection of workbooks, then in Visual Basic, we can access a specific workbook by name in this manner:
Although we are accessing the Item called “Sheet1”, Item is not explicitly referenced. What the code is really doing is calling:
Caché distinguishes between method call and property reference by the presence or absence of parentheses. This means that it interprets “person.Name” as a property and “person.RaiseSalary()” as a method. This makes default properties awkward because, unlike Visual Basic, Caché does not have the ability to define a default parameter nor the ability to do a property reference while passing parameters. For example, Caché cannot support the following Visual Basic syntax that has an implicit reference to an property:
Workbooks("Sheet1") ' Implicit reference to Item property
Neither can Caché support the following syntax, where Item is a property:
Workbooks.Item("Sheet1") ' Item is a property!
This does not work, because the Caché Interpreter considers Item to be a method. To work around this difference in the languages, use the following syntax:
This works because ItemGet is the method that retrieves the Item property.
Exception Handling
Any COM object may raise an exception as the result of some operation, be it a method call or a property set/get. When an exception is raised, the exception is propagated into Caché via the ZTrap mechanism. The calling code will receive an error with the error code <ZACTX> and the local variable %objlasterror will contain a complete textual description of the error. Programmers should plan for this error and take action accordingly.
Example: Exception Handling
Here is an example of using a COM object which retrieves files by FTP. The object is created and the CurrentDirectory property is queried. The COM object throws an exception because it is not valid to try to determine the current directory until the FTP connection has been made. We will try this from a Caché command line (terminal session):
 Set obj = ##class(Activate.RETRIEVERLib.FtpRetriever).%New()
 Write obj.CurrentDirectory
In this case, this will throw an error:
The error code associated with the <ZATCX> error should be in the local variable %objlasterror. We can retrieve the complete text of the error message by calling $system.OBJ.DisplayError:
 Do $system.OBJ.DisplayError(%objlasterror)
Which will result in the following output:
ERROR #1101: Com Exception: '-2147220888 Ftp Retriever Connection must
be established before attempting this operation'
%Activate.IDispatch and %Activate.GenericObject
Some COM objects do not come with a type library or you may find that the return type of a method or a property type of a COM object is just an IDispatch interface. How do you call methods and access properties for such objects?
Caché Activate provides two classes which assist with this problem, %Activate.IDispatch and %Activate.GenericObject.
Many COM objects are identified by what is called a “ProgId”, a string usually consisting of a library/object name which can be used to identify an object. In Visual Basic there is a CreateObject call which takes a Progid and returns an object reference which can be used to manipulate the object. Caché provides a CreateObject method too, as a class Mmethod of the %Activate.GenericObject class. Here is how it is used:
Example: Using CreateObject
Using the same Microsoft SysInfo object as above, we instantiate the object via its ProgId. Because the object is generic, that is, we have no type information for this object when instantiated in this manner, we must call the generic methods from the IDispatch interface which get and set properties and invoke methods by name:
 Set obj = ##class(%Activate.GenericObject).CreateObject("SYSINFO.SysInfo")
 Write obj.GetProperty("BatteryLifePercent")
 Set obj = ""
COM provides an alternative way of instantiating an object indirectly by using what is known as a moniker as a substitute for the ProgId. Visual Basic provides the GetObject call which takes a moniker and returns an object reference which can be used to manipulate the object. Caché provides a GetObject method as a Class Method in the %Activate.GenericObject class. Here is how it is used:
Example: Using GetObject
Here a moniker that accesses the LDAP protocol of the Active Directory service. It is used to return a reference to a collection of nodes which represents users in the current domain. The count of users is written out and the object closed:
 Set obj = ##class(%Activate.GenericObject).GetObject("LDAP://CN=USERS")
 Write obj.Count()
 Set obj = ""
The Become Method
Sometimes a type library specifies a method or a property which has a return type of the generic IDispatch interface. This can be very inconvenient because what you get is, in effect, an instance of %Activate.IDispatch on which you are forced to use generic methods (such as GetProperty) in order to get and set properties and invoke methods. If you know the interface that it really should be (from documentation or otherwise), then you can call the Become method on an instance of %Activate.IDispatch object and retrieve the new (now typed) interface. The Become method takes the name of a class as its argument. Effectively, %Activate.IDispatch becomes an instance of the class name you pass to the method. Become will throw an exception if the object you call does not support the new typed interface.
Some COM components have the ability to fire events during the processing of a method. The events are grouped into an event or “source” interface given a name. For example, given a COM object called MyClass, the interface may be called “MyClassEvents” or in the case of a COM object created with Visual Basic “__MyClass”.
Caché Activate provides for the event handling via two classes: %Activate.RegisterEvents and %Activate.HandleEvents. If a COM object generates events, the generated Caché class will inherit from the %Activate.RegisterEvents interface class. This adds two methods %RegisterHandler and %UnRegisterHandler. In addition to the regular COM object proxy class, another class is generated which represents the Event interface. This will inherit from %Activate.HandleEvents and implements the %Advise and %UnAdvise methods as well methods to handle specific events as defined by the event interface.
Example: Using COM Events
An example may make things clearer. Suppose we have a hypothetical COM object which does an FTP transfer. As well as implementing methods such as Connect, Close, and Download, the object implements an Event interface which expresses a single method, BytesTransferred. Following a successful connection and initiation of a download, the FTP object will fire the “BytesTransferred” Event after each 1 kilobyte of data that it has downloaded. The Event will be represented by a BytesTransferred method which has two parameters, an integer, Bytes and a boolean, Cancel which is passed by reference. When the Event fires, the BytesTransferred method will be called passing the current value of the arguments, Bytes and Cancel. These values are then available for processing. Typically the Bytes argument will be displayed via the user interface. Because the Cancel argument has been passed by reference, its value may be set and returned to the COM object which fired the event. In this instance setting Cancel to True (-1 for COM) will indicate to the COM object that the current operation should be interrupted and the call to Download should return immediately. If the download completes normally, the call to Download will return control to the caller and no more events will be fired. In Caché, the FTP COM object would be represented by a generated class such as Activate.SomeLibrary.FTP and the event interface by the class Activate.SomeLibrary.FTPEvents.
This example would look something like this. First an instance of the FTP object would be created:
 Set FTP = ##class(Activate.SomeLibrary.FTP).%New()
We want to handle events so we create an instance of an event handler:
 Set FTPHandler = ##class(Activate.SomeLibrary.FTPEvents).%New()
Before events can be handled the event handler must be registered with the object that actually fires the events, so we call:
 Do FTP.%RegisterHandler(FTPHandler)
Now we connect and do a download:
 Do FTP.Connect("")
 Do FTP.Download("/public/somefile.txt")
During the download the following method would be called on the Activate.SomeLibrary.FTPEvents class:
Class MyApp.Test

Method BytesTransferred(Bytes As %Integer,Cancel As %Boolean)
It is up the developer to actually implement the BytesTransferred method by editing the Activate.SomeLibrary.FTPEvents class directly or preferably by subclassing the class and providing the implementation in the subclass.
Following the download, we do not want events to be handled anymore so we unregister the handler:
 Do FTP.%UnRegisterHandler(FTPHandler)
and tidy up:
 Set FTPHandler = ""
 Set FTP = ""