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(--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.
The arguments are:
The ProgID or CLSID that identifies the COM / OLE object to proxy for.
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.
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.
myOLEObject = .OLEObject~new("InternetExplorer.Application")>>-dispatch(methodname--+------------+--)----------------------><
| +------+ |
| V | |
+----,arg-+--+Dispatches a method with the optionally supplied arguments.
>>-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
exitPossible 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
endPossible output:
!XLFORMULA=5 !XLMOVE=2 !XLTEXTMAC=19 ...
>>-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.0 | The number of events. |
| stem.n.!NAME | Name of n-th event. |
| stem.n.!DOC | Description of n-th event (if available). |
| stem.n.!PARAMS.0 | Number of parameters for n-th event. |
| stem.n.!PARAMS.i.!NAME | Name of i-th parameter of n-th event. |
| stem.n.!PARAMS.i.!TYPE | Type of i-th parameter of n-th event. |
| stem.n.!PARAMS.i.!FLAGS | Flags 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
exitSample 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----------------------------------------------><
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------------------------------------------------><
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----------------------------------------------><
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-------------------------------------------><
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-----------------------------------------><
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(--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(--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---------------------------------------------><
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.0 | The number of methods. |
| stem.!LIBNAME | Name of the type library that describes this object. |
| stem.!LIBDOC | A help string describing the type library. Only set when the string is available. |
| stem.!COCLASSNAME | COM class name of this object. |
| stem.!COCLASSDOC | A string describing the COM class. Only set when the string is supplied by the type library. |
| stem.n.!NAME | The name of the n-th method. |
| stem.n.!DOC | A 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.!INVKIND | A 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.!RETTYPE | The 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.!MEMID | The MemberID of the n-th method. This is only used internally to call the method. |
| stem.n.!PARAMS.0 | The number of parameters of the n-th method. |
| stem.n.!PARAMS.i.!NAME | The name of the i-th parameter of the n-th method. |
| stem.n.!PARAMS.i.!TYPE | The type of the i-th parameter of the n-th method. |
| stem.n.!PARAMS.i.!FLAGS | The 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
exitSample output:
The following methods are available to this OLE object: GoBack GoForward GoHome ...
>>-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--------------------------------------------><
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(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\")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 type | Rexx object |
|---|---|
| VT_EMPTY | .NIL |
| VT_NULL | .NIL |
| VT_ERROR | .NIL |
| VT_I1 | Rexx string (a whole number) |
| VT_I2 | Rexx string (a whole number) |
| VT_I4 | Rexx string (a whole number) |
| VT_I8 | Rexx string (a whole number) |
| VT_UI1 | Rexx string (a whole, positive number) |
| VT_UI2 | Rexx string (a whole, positive number) |
| VT_UI4 | Rexx string (a whole, positive number) |
| VT_UI8 | Rexx string (a whole, positive number) |
| VT_INT | Rexx string (a whole number) |
| VT_UINT | Rexx string (a whole, positive number) |
| VT_DECIMAL | Rexx string (a decimal number) |
| VT_R4 | Rexx string (a real number) |
| VT_R8 | Rexx string (a real number) |
| VT_CY | Rexx string (currency, a fixed-point number with 15 digits to the left of the decimal point and 4 digits to the right) |
| VT_DATE | Rexx string (a date) |
| VT_BSTR | Rexx string |
| VT_DISPATCH Rexx | OLEObject |
| VT_BOOL | .TRUE or .FALSE |
| VT_VARIANT | Any Rexx object that can be represented as a VARIANT |
| VT_UNKNOWN | OLEObject |
| 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.