The OLEObject Class

The OLEObject class is a built-in class. No ::requires directive is needed to use the class.

Methods available to the OLEObject class:

new (Class method)
addEventMethod
connectEvents
disconnectEvents
dispatch
getConstant
getKnownEvents
getKnownMethods
getObject(Class method)
getOutParameters
isConnected
isConnectable
removeEvents
removeEventHandler
unknown

Note: It is somewhat useful to think of the Rexx OLEObject object as a proxy to the real OLE object. The real OLE object has its own methods. Which methods it has is dependent on its individual implementation. These methods are then accessed transparently through the unknown() method mechanism of the OLEObject by invoking a method of the same name on the OLEObject object.

new (Class method)

>>--new(--classID--+-----------+--+--------------+--)-------------------><
                   +-,-events--+  +-,-getObject--+

Instantiates a new OLEObject as a proxy for a COM / OLE object with the specified classID (the ProgID or CLSID). If the COM / OLE object can not be accessed or created, an error will be raised. See the list of OLE specific errors in the Open Object Rexx Reference document.

Arguments:

The arguments are:

classID

The ProgID or CLSID that identifies the COM / OLE object to proxy for.

events

Controls how the event methods of the COM / OLE object are handled:

If the argument is omitted completely, then no action concerning the event methods is taken.

If the argument is NOEVENTS then the COM / OLE object is queried to determine if it is a connectable object. If it is, an internal table is constructed listing all the event methods. But the object is not connected.

If the argument is WITHEVENTS then the COM / OLE object is queried to determine if it is a connectable object. If it is, an internal table is constructed listing all the event methods, and an event connection is established.

getObject

A flag asking to first try to get an already instantiated OLE object, rather than instantiate a new object. Some OLE automation servers register themselves with the operating system when an object is first created, but not all do. If this flag is true, then the OLEObject first tries to proxy for an already running OLE / COM object. If this fails, then a new OLE / COM object is instantiated.

If the flag is omitted, or .false then no attempt to look for an already running OLE / COM object is made.

Example:
myOLEObject = .OLEObject~new("InternetExplorer.Application")

dispatch

>>-dispatch(methodname--+------------+--)----------------------><
                        |  +------+  |
                        |  V      |  |
                        +----,arg-+--+

Dispatches a method with the optionally supplied arguments.

getConstant

>>-getConstant(-+--------------+-)-----------------------------><
                +-ConstantName-+

Retrieves the value of a constant that is associated with this OLE object. If no constant of that name exists, the .Nil object will be returned. You can also omit the name of the constant; this returns a stem with all known constants and their values. In this case the constant names will be prefixed with a "!" symbol.

Example 1:

myExcel = .OLEObject~new("Excel.Application")
say "xlCenter has the value" myExcel~getConstant("xlCenter")
myExcel~quit
exit

Possible output:

xlCenter has the value -4108

Example 2:

myExcel = .OLEObject~new("Excel.Application")
constants. = myExcel~getConstant
myExcel~quit

do i over constants.
  say i"="constants.i
end

Possible output:

!XLFORMULA=5
!XLMOVE=2
!XLTEXTMAC=19
...

getKnownEvents

>>-getKnownEvents----------------------------------------------><

Returns a stem with information on the events that the connectable OLE object supports. It collects this information from the type library of the OLE object. A type library provides the names, types, and arguments of the provided methods. The OLEObject object does not need to be currently connected to connectable OLE object.

This method will return the event methods for any connectable object. Prior to ooRexx 4.0.0, only OLEObjects created directly, and created with the 'event' flag (WITHEVENTS or NOEVENTS) would return any known events. This fact had not been fully documented. Therefore, if the user did not create the OLEObject correctly, .nil would be returned for objects that did support event connections.

In 4.0.0, the behavior is fixed (or enhanced depending on the point of view) so that the known events are returned for all connectable objects under all circumstances.

The stem provides the following information:

Table 8-1. Stem Information

stem.0The number of events.
stem.n.!NAMEName of n-th event.
stem.n.!DOCDescription of n-th event (if available).
stem.n.!PARAMS.0Number of parameters for n-th event.
stem.n.!PARAMS.i.!NAMEName of i-th parameter of n-th event.
stem.n.!PARAMS.i.!TYPEType of i-th parameter of n-th event.
stem.n.!PARAMS.i.!FLAGSFlags of i-th parameter of n-th event; can be "in", "out", "opt", or any combination of these.

If no information is available, the .NIL object is returned. This indicates that the OLE object does support events.

Example script:

myIE = .OLEObject~new("InternetExplorer.Application","NOEVENTS")
events. = myIE~getKnownEvents

if events. == .nil then
  say "Sorry, this object does not have any events."
else do
  say "The following events may occur:"
  do i = 1 to events.0
    say events.i.!NAME
  end
end

exit

Sample output:

The following events may occur:
ONTHEATERMODE
ONFULLSCREEN
ONSTATUSBAR
...

For an example of how to use events, see examples samples\ole\apps\samp12.rex and samples\ole\apps\samp13.rex. The samples directory is installed as part of the normal Windows installation.

connectEvents

>>--connectEvents----------------------------------------------><

The connectEvents() method is used to connect the instantiated automation client (the OLEObject subclass object) to the automation server (the OLE object) at any time. The method returns .true if the connection was made, otherwise .false. Remember, not all OLE objects support events. The programmer can determine if the OLE object supports events by using the isConnectable() method.

Example:


  wordApp = .OLEObject~new("Word.Application")
  wordApp~visible = .true
  document = wordApp~documents~Add

  wordApp~connectEvents

isConnected

>>--isConnected------------------------------------------------><

Determines if the OLEObject instance is currently connected to a connectable OLE automation server. Returns .true if the instance is connected and .false if not.

Example:

  wordObj = .oleObject~new("Word.Application", "WITHEVENTS")
  if wordObj~isConnected then do
    ...
  end
  else do
    ...
  end

isConnectable

>>--isConnectable----------------------------------------------><

Determines if the OLE object is a connectable object. In other words, does the OLE object support event methods and will it accept connections at this time. Not all OLE objects support events, probably the majority do not support events. This method returns .true if the object is connectable, otherwise .false.

Example:


  outLook = .oleObject~new("Outlook.Application")

  -- This searches all folders for the 'Mailbox - .. ' folder.  Which is
  -- usually the default folder in a business installation of Outlook.
  nameSpace = outLook~getNameSpace('MAPI')
  folders = nameSpace~folders
  do i = 1 to folders~count
    if folders~item(i)~name~caselessPos("Mailbox") <> 0 then do
      theMailBoxFolder = folders~item(i)
      leave
    end
  end

  -- Now that we have the Mailbox folder, get the collection of folders that
  -- are contained in the Mailbox folder.
  folders = theMailBoxFolder~folders

  if folders~isConnectable then do
    -- Add event methods to the folders object.
    ...
  end

disconnectEvents

>>--disconnectEvents-------------------------------------------><

This method disconnects from the connectable OLE object. The method returns .false if there is not a current connection, otherwise .true. After this method is called, the OLE object will no longer invoke the event methods, in effect stopping event notifications.

The internal data structures used to manage events remain intact. The programmer can use the connectEvents() method to reconnect at any time. Since the internal data structures do not need to be rebuilt, this will save some small amount of processor time. To completely remove the internal data structures use the removeEventHandler() method.

Example:

This example shows some code snippets from a program that monitors the user's inbox in OutLook. When a new mail item arrives, the user is notified. The interface for the program allows the user to turn off the notifications when she wants, then turn them back on later. When the interface signals the program to stop the notifications, the program simply disconnects the events from the OutLook object. When the user wants to resume notifications, the program reconnects the events.


  outLook = .oleObject~new("Outlook.Application")
  inboxID = outLook~getConstant(olFolderInBox)
  inboxItems = outLook~getNameSpace("MAPI")~getDefaultFolder(inboxID)~items

  ...

  inboxItems~addEventMethod("ItemAdd", .methods~printNewMail)
  inboxItems~connectEvents

  ...

    select
      when status == 'disconnect' then do
        inboxItems~disconnectEvents
        say 'ooRexx Mail Monitor - paused ...'
      end
      when status == "reconnect" then do
        inboxItems~connectEvents
        say 'ooRexx Mail Monitor - monitoring ...'
      end
      otherwise do
        nop
      end
    end
    -- End select


removeEventHandler

>>--removeEventHandler-----------------------------------------><

Removes the event handler and cleans up the internal data structures used to manage events. No event methods will be invoked after this method is called. See the disconnectEvents() method for a way to temporarily disconnect from event notifications.

Example:


  inboxItems~removeEventHandler
  inboxItems~removeEventMethod("ItemAdd")

addEventMethod

>>--addEventMethod(--name-,-methodObject--)--------------------><

addEventMethod adds a new method to this object's collection of methods. The name argument specifies the name of the new method and the methodObject argument defines the method. The acceptable values for methodObject are the same as those for the second argument to the setMethod method of the.Object class. That is, it can be a method object, a string containing a method source line, or an array of strings containgin individual method source lines.

The purpose of this method is to add an event method to a OLEObject after the object has been instantiated. See the OLE Events section for more details on events.

Example:

Note that in this example, the printNewMail method is defined as a floating method. See the documentation for the .methods directory in the Open Object Rexx Reference book for more details if needed.


  inboxID = outLook~getConstant(olFolderInBox)
  inboxItems = outLook~getNameSpace("MAPI")~getDefaultFolder(inboxID)~items

  inboxItems~addEventMethod("ItemAdd", .methods~printNewMail)

...

::method printNewMail unguarded
  use arg mailItem
  say 'You have mail'
  say 'Subject:' mailItem~subject


removeEventMethod

>>--removeEventMethod(--name--)-----------------------------------><

Removes the event method with the specified name that has been previously added to this object by the addEventMethod() method.

Example:


  inboxID = outLook~getConstant(olFolderInBox)
  inboxItems = outLook~getNameSpace("MAPI")~getDefaultFolder(inboxID)~items

  inboxItems~addEventMethod("ItemAdd", .methods~printNewMail)
  inboxItems~connectEvents

  ...

::method doneWithItemEvents private
  expose inboxItems

  inboxItems~removeEventHandler
  inboxItems~removeEventMethod("ItemAdd")

getKnownMethods

>>-getKnownMethods---------------------------------------------><

Returns a stem with information on the methods that the OLE object supplies. It collects this information from the type library of the object. A type library provides the names, types, and arguments of the provided methods. Parts of the supplied information have only informational character as you cannot use them directly.

The stem provides the following information:

Table 8-2. Stem Information

stem.0The number of methods.
stem.!LIBNAMEName of the type library that describes this object.
stem.!LIBDOCA help string describing the type library. Only set when the string is available.
stem.!COCLASSNAMECOM class name of this object.
stem.!COCLASSDOCA string describing the COM class. Only set when the string is supplied by the type library.
stem.n.!NAMEThe name of the n-th method.
stem.n.!DOCA help string for the n-th method. If this information is not supplied in the type library this value will not be set.
stem.n.!INVKINDA number that represents the invocation kind of the method: 1 = normal method call, 2 = property get, 4 = property put. A normal method call is used with brackets; for a property get only the name is to be specified; and a property set uses the "=" symbol, as in these examples: object~methodCall(a,b,c) object~propertyPut="Hello" say object~propertyGet
stem.n.!RETTYPEThe return type of the n-th method. The return type will be automatically converted to a Rexx object (see Type Conversion in the description of the UNKNOWN method of the OLEObject class).
stem.n.!MEMIDThe MemberID of the n-th method. This is only used internally to call the method.
stem.n.!PARAMS.0The number of parameters of the n-th method.
stem.n.!PARAMS.i.!NAMEThe name of the i-th parameter of the n-th method.
stem.n.!PARAMS.i.!TYPEThe type of the i-th parameter of the n-th method.
stem.n.!PARAMS.i.!FLAGSThe flags of the i-th parameter of the n-th method; can be "in", "out", "opt", or any combination of these (for example: "[in, opt]").

If no information is available, the .NIL object is returned.

Note that it is not required that an OLE object supply a type library. The methods of OLE objects that do not supply a type library can still be invoked by name, but there is no way for getKnownMethods to look up the methods. To use these OLE objects the Rexx programmer would need to consult the documentation for the OLE object.

In addition all OLE objects have methods that can only be used internally. There are mechanisms to hide these methods from the user, because they can not be used by the automation client. It is possible that these are not hidden properly and will be listed when using getKnownMethods. The following methods can not be used by an instance of the OLEObject:

AddRef
GetTypeInfoCount
GetTypeInfo
GetIDsOfNames
QueryInterface
Release

Example script:

myOLEObject = .OLEObject~new("InternetExplorer.Application")
methods. = myOLEObject~getKnownMethods

if methods. == .nil then
  say "Sorry, no information on the methods available!"
else do
  say "The following methods are available to this OLE object:"
  do i = 1 to methods.0
    say methods.i.!NAME
  end
end

exit

Sample output:

The following methods are available to this OLE object:
GoBack
GoForward
GoHome
...

getObject (Class method)

>>-getObject(Moniker-+--------+-)------------------------------><
                     +-,class-+

This is a class method that allows you to obtain an OLE object through the use of a moniker. A moniker is a string and is similar to a nickname. Monikers are used by OLE to connect to and activate OLE objects. OLE returns the object that the moniker identifies.

If the object is already running, OLE will find it in memory. If the object is stored passively on disk, OLE will locate a server for the object, run the server, and have the server bring the object into the running state. This makes monikers very easy for the automation client to use. OLE hides all the details from the client. However, since the OLEObject also hides all the details when a new OLE object is instantiated, for the Rexx programmer there is not much difference between using the getObject method and using the new method.

Note that file system names are monikers. Therefore, if a file is assoicated with an application that is an OLE automation server, a new OLE object can be instantiated by using the file name as the moniker. Obviously, this is not true of every file. It is true for files like .xls and .doc files, for example, because Word and Excel are OLE automation applications.

The optional class argument can be used to specify a subclass of OLEObject, and can be used to obtain an OLE object that supports events (the'WITHEVENTS' option will be used in this case). This method is similar to the new method where the programmer supplies a ProgID or CLSID. In this case the programmer supplies a moniker.

Example:

/* create a Word.Document by opening a certain file */
myOLEObject = .OLEObject~GetObject("C:\DOCS\HELLOWORLD.DOC")

getOutParameters

>>-getOutParameters--------------------------------------------><

Returns an array containing the results of the single out parameters of the OLE object, or the .NIL object if it does not have any. Out parameters are arguments to the OLE object that are filled in by the OLE object. As this is not possible in Rexx due to data encapsulation, the results are placed in the array mentioned above.

Example:

Consider an OLE object method with the following signature:

aMethod([in] A, [in] B, [out] sumAB)

The resulting out parameter of the method invocation will be placed in the out array at position one; the "normal" return value gets processed as usual. In this case the method will return the .NIL object:

resultTest = myOLEObject~aMethod(1, 2, .NIL)
say "Invocation result  :" resultTest
say "Result in out array:" myOLEObject~getOutParameters~at(1)

The output of this sample script will be:

The NIL object
3

Out parameters are placed in the out array in order from left to right. If the above OLE method looked like this:

aMethod([in] A, [in] B, [out] sumAB, [out] productAB),

then the out array would contain the sum of A and B at position one, and the product at position two.

unknown

>>-unknown(messageName--+----------------+--)------------------><
                        +--,messageArgs--+

The unknown message is the central mechanism through which methods of the OLE object are called.

For further information on the details on how an unknown method works, see Defining an unknown Method in the Open Object Rexx Reference.

The programmer can invoke the methods of the real OLE object by simply invoking the methods on the the Rexx (proxy) OLEObject object like this:

myOLEObject~OLEMethodName

This calls the method "OLEMethodName" of the real OLE object for any message (method) that does not exist in the Rexx OLEObject object through the unknown method mechanism. The implementation for the unknown() method in the OLEObject class does this by dispatching the method call to the real OLE object.

This presents a problem when an OLE object has a method with a name that is identical to a method defined for the OLEObject object. When this situation happens, the programmer has two choices.

One choice is for the programmer to call the unknown method directly. E.g., take an OLE object that has the method copy used to copy something from a source to a destination. Since copy is a method of the Object class, the copy method of the OLE object is a method name already defined for the OLEObject. The programmer can invoke the unknown method directly, like this:

msgArgs = .array~of("C:\open\myFile.txt", "C:\processDir\")
val = myOLEObject~unknown("copy", msgArgs)

This causes the implementation of the unknown() method in the OLEObject object to invoke the copy method of the OLE object with the arguments of C:\open\myFile.txt and C:\processDir\.

The other thing the Rexx programmer can do is use the dispatch() method. Since, in OLE automation terms, the act of invoking a method on the OLE object is commonly referred to as dispatching a message to the OLE object, this may make the code a little easier to understand. In the above example the dispatch method would be used like this:

val = myOLEObject~dispatch("copy", "C:\open\myFile.txt", "C:\processDir\")

Type Conversion

Unlike Rexx, OLE uses strict typing of data. Conversion to and from these types is done automatically, if conversion is possible. OLE types are called variants, because they are stored in one structure that gets flagged with the type it represents. The following is a list of all variant types valid for use with OLE Automation and the Rexx objects that they are converted from or into.

Table 8-3. OLE/Rexx Types

VARIANT typeRexx object
VT_EMPTY.NIL
VT_NULL.NIL
VT_ERROR.NIL
VT_I1Rexx string (a whole number)
VT_I2Rexx string (a whole number)
VT_I4Rexx string (a whole number)
VT_I8Rexx string (a whole number)
VT_UI1Rexx string (a whole, positive number)
VT_UI2Rexx string (a whole, positive number)
VT_UI4Rexx string (a whole, positive number)
VT_UI8Rexx string (a whole, positive number)
VT_INTRexx string (a whole number)
VT_UINTRexx string (a whole, positive number)
VT_DECIMALRexx string (a decimal number)
VT_R4Rexx string (a real number)
VT_R8Rexx string (a real number)
VT_CYRexx string (currency, a fixed-point number with 15 digits to the left of the decimal point and 4 digits to the right)
VT_DATERexx string (a date)
VT_BSTRRexx string
VT_DISPATCH RexxOLEObject
VT_BOOL.TRUE or .FALSE
VT_VARIANTAny Rexx object that can be represented as a VARIANT
VT_UNKNOWNOLEObject
VT_ARRAY *Rexx Array
VT_BYREF *Any Rexx object

* VT_ARRY and VT_BYREF are combined with any of the other variant types and never used alone. VT_ARRAY and another variant type are used for a SAFEARRAY datatype, an array of the other variant type. VT_BYREF and another variant type are used to pass the other variant type to or from an OLE object by reference. The programmer need not worry about this passing by reference, the OLE support handles this transparently.