Skip to main content

Working with Caché ActiveX Objects

This section describes how to work with Caché ActiveX objects. The examples in this section are written in Visual Basic, but Caché ActiveX components can be used in a similar manner by any language that supports ActiveX. The following topics are covered:

Note:
ActiveX is not recommended for new development

The examples in this book were written for Visual Basic 6 or earlier (including VBA and VBS scripting languages), and may not work in .NET Basic without modification. InterSystems strongly recommends migrating to newer and more secure technologies (see the Introduction for more information).

Connecting to a Server

Typically, a client application first creates a CacheActiveX.Factory object and establishes a connection to a Caché server. Within Visual Basic the following code provides one way to do this:

'Visual Basic Code
option Explicit
Dim factory As CacheActiveX.Factory

Private Sub Form_Load()

' Create instance of factory object
' You must use "Set" to assign an object value
Set factory = CreateObject("CacheActiveX.Factory")

' Establish connection to server if one doesn't exist
If Not factory.IsConnected() Then
     Dim connectstring As String
     ' You can explicitly specify the connection string:
     connectstring = "cn_iptcp:127.0.0.1[1972]:USER"

     ' Alternately, you can pop up a connection dialog
     ' This method returns the connection string.
     connectstring = factory.ConnectDlg()

     Dim success As Boolean
     success = factory.Connect(connectstring)
End If
End Sub

First, create an instance of a CacheActiveX.Factory object by calling Visual Basic's CreateObject function:

Set factory = CreateObject("CacheActiveX.Factory")

CreateObject fails if it is unable to find or otherwise load the DLL (which contains the implementation of the CacheActiveX.Factory object). See the chapter “Configuring a Visual Basic Project.

As in the code above, you can optionally test a CacheActiveX.Factory object to see if it has an established connection using the factory.IsConnected method. This method returns True if a connection exists.

Next, using the CacheActiveX.Factory object, you then establish a connection to a server by using the Connect method of the CacheActiveX.Factory object:

success = factory.Connect(connectstring)

Connect is passed a connection string consisting of a connection protocol, an IP address or Fully Qualified Domain Name (FQDN) and port number, and a namespace separated by colons. The connection protocol is always “cn_iptcp”, signifying the TCP/IP protocol. The IP address and port together uniquely specify a specific Caché server. The port number is specified in square brackets (“[” and “]”) immediately following the IP address or FQDN. The namespace specifies which namespace contains your Caché objects. If you omit the namespace, your application connects to the default namespace.

You can directly pass the Connect method a connection string or you can let your end users specify the connection information in a connection dialog box. This dialog pops up whenever the ConnectDlg method of the CacheActiveX.Factory object is executed. Once the end user specifies the connection information and clicks the Okay button, the ConnectDlg method returns the resulting connection string which can be passed to Connect to create the connection.

Connect returns True if it has successfully connected to a server and False if it fails.

Defining a Connection String

A connectstring has the following basic form:

cn_iptcp:ipaddress[port]:namespace

connectstring consists of the following parts:

  • cn_iptcp: — A literal specifying the connection type. ActiveX only supports TCP/IP as the connection mechanism.

  • ipaddress — The IP address or Fully Qualified Domain Name (FQDN).

  • port — The port number for this connection, enclosed in square brackets and appearing immediately after the IP Address. Together, the IP address or FQDN and the port specify a unique Caché server.

  • namespace — The namespace containing the Caché objects to be used. This namespace must have the Caché system classes compiled, and must contain the objects you want to manipulate from ActiveX.

For example, the Connect method can be used as follows:

' Establish connection to server
Dim connectstring As String
connectstring = "cn_iptcp:127.0.0.1[1972]:USER"

Dim success As Boolean
success = factory.Connect(connectstring)

Defining a Secure Connection

A secure connectstring uses connection type scn_iptcp: (rather than cn_iptcp:), and has the following basic form:

scn_iptcp:ipaddress[port]:namespace:srv_principal_name:security_level:username:password

If security is not enabled on the Caché server, all content after namespace is ignored. If security is enabled, connectstring must include the srv_principal_name and security_level. The username and password are optional arguments used with SSL.

The security arguments are defined as follows:

  • scn_iptcp: — A literal specifying the connection type.

  • ipaddress — The IP address or Fully Qualified Domain Name (FQDN).

  • port — The port number for this connection, enclosed in square brackets and appearing immediately after the IP Address. Together, the IP address or FQDN and the port specify a unique Caché server.

  • namespace — The namespace containing the Caché objects to be used. This namespace must have the Caché system classes compiled, and must contain the objects you want to manipulate from ActiveX.

  • srv_principal_name — The Service Principal Name, which is the name of the principal that represents the Caché server. This is typically a standard Kerberos principal name, of the form cache/machine.domain, where cache is a fixed string indicating that the service is for Caché, machine is the machine name, and domain is the domain name, such as intersystems.com.

  • security_level — An integer indicating Connection Security Level. Valid levels are:

    • 0 — Caché login (Password)

    • 1 — Kerberos (authentication only)

    • 2 — Kerberos with Packet Integrity

    • 3 — Kerberos with Encryption

    • 10 – SSL/TLS

  • username — (Optional) The username under which the connection is being made.

  • Password — (Optional) The password associated with the specified username.

For more information, see the “Authentication” chapter of the Caché Security Administration Guide.

Connection String for Use with the Older DLL

If you are using the older CacheObject.dll (see “Upgrading from CacheObject.dll”), the Connect method can accept a connection string that includes an encrypted password. (The newer CacheActiveX.dll does not need this, because this information is encoded by the ODBC driver.) That is, the connection string can have the following form:

cn_iptcp:ipaddress[port]:namespace:username:password

If Caché Direct security is turned on for the Caché server, there must be a password and it must be encrypted using the EncryptPswd function. In a project module, make the library available that includes the password encryption function:

Declare Function 
        EncryptPswd Lib "CMVISM32" (ByVal pswd$, ByVal encrypt$) as Integer
Note:

In Visual Basic, the declaration must appear on a single line.

Then make the calls that make the connection to the server:

' preallocate the buffer, 8 nulls
ret$ = String$(8,Chr(0))

' encrypt the password for the connection string
pwlen = EncryptPswd(PlainTextPassword,ret$)

' reset the length of the password string 
' to match that of the encrypted password
ret$ = Left$(ret$,pwlen)

' Establish connection to server
Dim connectstring As String
connectstring = "cn_iptcp:127.0.0.1[1972]:USER:" & username & ":" & ret$

Dim success As Boolean
success = factory.Connect(connectstring)

If success <> True Then
    Dim MyMsg As String
    MyMsg = "Connection failed."
    MsgBox MyMsg
    End
End If
Note:

If the Cache instance is installed with Normal security and %SERVICE_CACHEDIRECT is Unauthenticated (as required for usage of CacheObject.dll), UnknownUser must also be provided with additional role access (%DEVELOPER or %MANAGER). Otherwise, the connection attempt will fail with a PROTECT error. For more information, see “%Service_CacheDirect” in the “Services” chapter of the Caché Security Administration Guide.

Creating a New Object Instance

You can create a new instance of a Caché object by using the CacheActiveX.Factory object's New method:

Dim Patient As Object
Set Patient = factory.New("Patient")

The argument of New is the name of the Caché class to instantiate. New does the following:

  • It creates a new instance of the specified object on the server. In this case, this is equivalent to calling

     Set object = ##class(Patient).%New()

    on the server. It returns the OREF value for this server-side object to the Caché Object Server for ActiveX.

  • It creates a new instance of an ActiveX object on the client that is connected to the server-side object. It returns a handle (LPDISPATCH) to the ActiveX object.

  • If New is unable to create the specified object, it raises an error condition (see “Error Trapping in Visual Basic”).

Saving an Object

You can save an instance of a persistent object using its %Save method (called sys_Save in Visual Basic). Note that sys_Save is a method of a Caché object, not of the CacheActiveX.Factory object:

Dim status As String
patient.sys_Save
patient.sys_Close
Set patient = Nothing

Note that you must call sys_Close on an object when you are through using it. This closes the object on the server. In addition you should set patient to Nothing to close the object in Visual Basic.

Note also that there are no parentheses following the calls to sys_Save and sys_Close. This is because when a call is followed by empty parentheses, Visual Basic requires that its result be used. To ignore a call's return value (or if it has no return value), omit the parentheses, as in the calls above.

Opening an Existing Object

You can load an existing Caché object from the database by using the CacheActiveX.Factory object's OpenId method:

Dim Patient As Object
Set Patient = factory.OpenId("MyApp.Patient", id)

OpenId takes two arguments: the name of the Caché class to open and the ID value with which the object was stored in the database. The ID value is treated as a string. OpenId does the following:

  • It loads the specified object into memory on the server. In this case, this is equivalent to calling:

     Set object = ##class(MyApp.Patient).%OpenId(id) 

    on the server. It returns the OREF value for the in-memory server-side object to the ActiveX client.

  • It creates a new instance of an ActiveX object on the client that is connected to the server-side object. It returns a handle to this ActiveX object.

If the OpenId method is unable to open the specified object, it raises an error condition (see “Error Trapping in Visual Basic”).

Note:

There is also a client-side Open method that opens an object using its complete OID value (similar to the %Open method of the %PersistentOpens in a new tab class.

Manipulating Caché Objects

Once you have created an instance of a Caché object by calling the CacheActiveX.Factory object's New or OpenId methods you can use the object as you would any other Visual Basic object. For example, you can get and set property values:

Dim name As String

name = patient.Name
patient.Name = name

You can invoke methods on an object (note that methods are executed on the server):

patient.Admit()

There are some differences between using objects in Visual Basic and ObjectScript: Methods that start with “%” in Caché start with “sys_” in Visual Basic. Hence the ObjectScript %Save method is the sys_Save method in Visual Basic.

Also, you should not rely on the default property of Visual Basic objects. For example, use:

patient.Name = txtName.Text

instead of using:

patient.Name = txtName ' Don't use default property syntax

Using Callback Functionality

The Caché Object Server for ActiveX supports callback functionality via the SetOutput method of the Factory class. This method allows you to direct system output to your client application. For example:

Set factory = CreateObject("CacheActiveX.Factory")
factory.SetOutput(Text1)
' System output now goes to textbox Text1

To disable output functionality you can call SetOutput with an empty string:

factory.SetOutput("")

Running a Query

Caché provides a robust interface for performing queries. This interface can be accessed from ActiveX using the ResultSet object. ResultSet objects only exist in ActiveX; they have no corresponding objects in Caché. Each ResultSet object is tied to a specific query defined in a specific Caché class.

For example, run a query, ByName, in the Person class:

Dim rset As CacheActiveX.ResultSet
Dim columns As Integer
Dim counter As Integer
Set rset = factory.ResultSet("Person","ByName")

'Find out how many columns will be in the data
columns = rset.GetColumnCount()

'Now run the ByName query.
'This query will return all people
'whose names begin with the letter specified
rset.Execute("A")

'Cycle through the returned rows and access the data
While rset.Next()
     For counter = 1 To columns
         Print rset.GetData(counter)
     Next counter
Wend

'Close the result set
rset.Close()

Using this example, you can run the query ByName defined in the class Person. This query returns all people objects whose names begin with the letter specified (“A”). After executing the query, you can move from one row to the next by calling the Next method of the rset ResultSet object. You can use the ResultSet GetColumnCount method to find out how many columns are in each row of data, then use the For loop above to print the value stored in each column of the current row. The While loop moves you from one row to the next. Together, they allow you to print all of the data returned by the query. Once all of the data has been processed, you can use Close to close the ResultSet object.

Using Streams

Stream properties are projected using the CacheActiveX.CharStream and CacheActiveX.BinaryStream classes. For example, suppose you have a Person object containing a character stream property, Memo. In addition to the standard stream methods Read and Write, you can use the client-side method Data, which fetches the entire stream in one operation:

  ' Visual Basic code
  Dim person As Object
  Dim memo As String1

  ' Open a Person object and copy its memo into a local variable
  Set person=Factory.OpenId("Sample.Person",id)
  memo=person.Memo.Data

The GetPicture method is a similar mechanism that efficiently copies binary data containing a bitmap image into a picture control. For example, if the Person object has a binary stream property, Picture, containing a bitmap image (such as a .jpg or .png file), you can display this image in Visual Basic as follows:

  ' Visual Basic code
  Dim person As Object
  Dim memo As String

  ' Open a Person object and show its picture in an Image control
  Set person=Factory.OpenId("Sample.Person",id)
  Image1.Picture = person.Picture.GetPicture

The SetPicture method can be used to copy an image in the other direction, from an Image control to a binary stream property:

  ' Visual Basic code
  person.Picture.SetPicture Image1.Picture

Using Lists

You can use CacheActiveX.SysList to pass a list (Caché $List format) back and forth between VB and Caché, as demonstrated in the following example:.

Dim colorList As New CacheActiveX.syslist 
Dim ReturnData As Object 
Dim myRef as Object 

'populate the list with red,green,blue values 
colorList.Add "red" 
colorList.Add "green" 
colorList.Add "Blue" 

'create a new instance on the server 
Set myref = o_factory.New("Pack.ClassA") 
colorList2.Clear 'sets list to null 

'pass a listbuild string and return the com object to ReturnData, which 
'can then be used to get the items of the list. GetData()is an 
'instance method that takes a %List as argument and returns %List. 

set ReturnData = myref.GetData(colorList.Get) 
Text1.Text = ReturnData.Item(1) 'display return string 'red' 

colorList.sys_Close 'close object 
Set colorList = Nothing 'close vb object 

Error Trapping

The CacheActiveX.Factory object raises a Visual Basic error if it encounters an error condition. You can obtain information about the error by using Visual Basic's err object. For example:

Private Sub OpenObject(id As String)

' Set up error handler
On Error GoTo errortrap:

' try to open non-existent object
Dim Patient As Object
Set Patient = factory.OpenId("Patient", id)

'......
Exit Sub

errortrap:
' handle error (show error string in dialog box)
MsgBox (Err.Description)

End Sub

Alternately, you can use the GetErrorText method of the CacheActiveX.Factory class. This method has the following syntax:

String GetErrorText(int ErrorNumber [, Param1, ..., Param10])

where ErrorNumber is a Caché error which was returned by the server and each of the ParamN arguments supplies a value to be substituted into the text of the error message. The return value appears in the language of the client's locale. For example:

' Caché was unable to save the class.
' The returned error is encapsulated by the error object, 
'    #5659: "Property '%1' required"
msg = factory.GetErrorText(5659,"Name")
' msg now contains the string "Property 'Name' required"

The Caché ActiveX binding may return one of the following error codes:

ActiveX Error Codes
Error Code Meaning
9990 An error occurred while attempting to create an object. Check that the class exists in the specified namespace and server.
9991 An error occurred on the Caché system.
9992 An error occurred in a method with the return type %Status causing the status code to return False. See the Caché Error Reference for the list of built-in messages.
FeedbackOpens in a new tab