PreviousUsing Other Object Domains OLE Data TypesNext

Chapter 4: OLE Automation and DCOM

Object Linking and Embedding (OLE) is a feature of Microsoft Windows which enables an application to expose functionality that can be utilized by other applications. You can use parts of off-the-shelf packages in conjunction with custom software to create new applications. All of the applications in the Microsoft Office suite and much of the Microsoft BackOffice suite expose their functionality through OLE automation, enabling you to reuse parts of them to perform common functions. For example, if you want your application to generate an attractive looking report and print it, you can use the functionality of Microsoft Word to create and print the report.

4.1 Overview

MERANT OLE automation support enables you to send messages to ActiveX objects from Object COBOL programs and classes (Object COBOL as an ActiveX client). You can also create ActiveX objects, enabling you to manipulate an Object COBOL class through OLE automation.

There are two main differences between the Component Object Model (COM) on which OLE automation is built, and that for an OO language like Object COBOL:

OLE support is documented in three main sections:

Object COBOL ActiveX clients and objects automatically use DCOM when necessary. You only need to change the registry entries. This is covered later in this chapter, in the section Using DCOM.


Note: ActiveX clients and objects were formerly known as OLE automation clients and servers.


4.1.1 Checklist - Before You Start

OLE and DCOM technology is evolving very quickly, and it is possible that not all the third-party software you plan to use for a project supports the functionality you want to use. Before beginning a project, make sure you have up-to-date releases for all third-party software, and that it supports all the functionality you need.

In particular, check the following:

The excel.app project, in folder Net Express\Base\Demo\Oledemos\Excel, and the word.app project, in folder Net Express\Base\Demo\Oledemos\Word, demonstrate how Object COBOL can be used to write an OLE Automation Client. The readme files (readme.txt) in those folders describe how to run the sample programs.

4.2 Writing ActiveX Clients

From an Object COBOL progam you can:

This enables you to control any windows application which has an OLE automation interface, directly from Object COBOL. A program which uses ActiveX objects like this is known as an ActiveX client.

COM enables the ActiveX object to be written in a different language to the client, and defines the type and format of data which can be sent between clients and objects. Any data sent to OLE objects from Object COBOL programs is type-coerced according to the rules outlined in the chapter OLE Data Types.

Object COBOL OLE automation support represents ActiveX objects as Object COBOL objects, by providing a proxy for each ActiveX object a client is using. The client sends messages to the proxy, and these are forwarded on to the ActiveX object by the Object COBOL run-time system.



Figure 4-1: A client sending messages to an ActiveX proxy

The following sections cover the process of writing an ActiveX client:

4.2.1 Enabling a Program as an ActiveX Client

Any Object COBOL program can be made into an ActiveX client; you don't need to write an Object COBOL class. All you need to do is:

  1. Set compiler directive OOCTRL(+P) in your program:
    $set ooctrl(+p)

    Note: This limits the number of parameters in any method invoked or declared to 31, plus an optional RETURNING parameter.

  2. Map each ActiveX object you want to use to an Object COBOL class name, in the CLASS-CONTROL paragraph:
    class-control.
    
        class-name IS CLASS "$OLE$windows-registry-name" 

    where windows-registry-name is the server name under which the ActiveX object is entered in the Windows system registry. You can use either the ProgID, which is a human-readable name, or the CLSID, which is a 16-byte number guaranteed to be unique for each ActiveX object in the world. You would normally use the ProgID, as the CLSID is likely to change for each new version of the ActiveX object released.

    For example:

    class-control
        Word is class "$OLE$word.basic"
        HTMLHelp is class "$OLE${adb880a6-d8ff-11cf-9377-00aa003b7a11}"
        . 
  3. Declare an OBJECT REFERENCE data item to hold the handle to the proxy for the ActiveX object.

For example:

$set ooctrl(+P)
 class-control.
*> comploan is the name (ProgID) of the ActiveX object
     comploan is class "$OLE$comploan"
     ...
     .

 working-storage section. 
 01 theCompLoanServer            object reference.

4.2.2 Creating an ActiveX Proxy Object

You need to create a proxy object for each ActiveX object you want to use. When you create the proxy, two things happen:

To create a proxy:

You can now start sending messages to the ActiveX object through the proxy.

Example:

 working-storage section. 
 01 theCompLoanServer            object reference.
 ...
 procedure division. 
 ...
 invoke comploan "new" returning theCompLoanServer
 ...

You can also specify the location of the server when you create it. To specify the server location:

Example:

 working-storage section. 
 01 theCompLoanServer            object reference.
 ...

 procedure division. 
 ...
 invoke comploan "newWithServer" using z"machine1" returning theCompLoanServer
 ...

4.2.3 Sending Messages to an ActiveX Object

Once you have an ActiveX object, you can send it messages and set or get its properties. Both message sends and property get/set operations are handled by using the INVOKE verb to send messages to the proxy object. Whenever you send a message to the proxy object, where the name begins with "get" or "set", the Object COBOL run-time system automatically converts that to a property get or set on the ActiveX object, as shown in the diagram below.



Figure 4-2: Sending messages and setting properties

To send a message:

invoke proxyObject "messagename" [using param-1 [param-2..]] 

                                 [returning result]
proxyObject The proxy for the ActiveX object. Creating a proxy and starting the ActiveX object is explained in the section Creating an ActiveX proxy object
messagename The message you want to send.
param-1 Any parameters needed for the message. COBOL data types are converted to OLE data types as explained in the chapter OLE Data Types. Pass all parameters by reference (COBOL default).
result The result if this method returns one. COBOL data types are converted to OLE data types as explained in the chapter OLE Data Types.

To set a property:

invoke proxyObject "setPropertyName" using value
proxyObject The proxy for the ActiveX object. Creating a proxy and starting the ActiveX object is explained in the section Creating an ActiveX proxy object
PropertyName The name of the property you want to set.
value The new value for the property. COBOL data types are converted to OLE data types as explained in the chapter OLE Data Types. Pass the value by reference (COBOL default).

To get a property:

invoke proxyObject "getPropertyName" returning value
proxyObject The proxy for the ActiveX object. Creating a proxy and starting the ActiveX object is explained in the section Creating an ActiveX proxy object
PropertyName The name of the property you want to set.
value The value of the property. OLE data types are converted to COBOL data types as explained in the chapter OLE Data Types. The value is returned to you in an area of memory which can be overwritten by OLE, so take a copy of the value if you need to keep it.

The following example sets a property, sends a message, and then gets a property.

working-storage section.
 …
 01 theCompLoanServer       object reference.
 01 AnAmount                pic 9(7).99.
 01 ARate                   pic 99.99.
 01 Amount20                pic $9(7).99. 
 ...

 procedure division. 
 ... 
      invoke theCompLoanServer "SetLoanTermYears" using
           "20"
      invoke theCompLoanServer "calculate"
      invoke theCompLoanServer "GetYearPayment" returning
           Amount20
 ...

Forcing the OLE Message Type

You can override the run-time system default of assuming that all OLE messages sent from Object COBOL beginning "set" or "get" are property set or get operations. You can also force a message which is not prefixed "set" or "get" to be a property set or get. To force the message type, send the message "setDispatchType" to class olesup (filename olesup):

invoke olesup "setDispatchType" using by value lsType

where:

lsType PIC X(4) COMP-5

value 0 = next message forced to invoke a method
value 1 = next message forced to invoke a property set
value 2 = next message forced to invoke a property get

The message type is only forced for the next OLE message send, after which it reverts to the default behavior until you "setDispatchType" again. Class olesup encapsulates several useful functions for OLE automation programming, and is fully documented in the OLE Automation Class Library Reference. Click Help Topics on the Net Express Help menu, and then click Reference, OO Class Library, Class Library Reference, and click the shortcut to start the Class Library Reference.

4.2.4 Using Type Library Information

The type library for an ActiveX object defines information that can be useful when you are writing a client. You can generate a COBOL copyfile for any type library by using the Type Library Assistant. Click Type Library Assistant on the Net Express Tools menu.

Depending on the information in the type library, the COBOL copyfile includes the following:

4.2.5 Finalizing an ActiveX Proxy Object

The proxy objects created to represent ActiveX objects are not automatically garbage collected by the Object COBOL run-time system. When you have finished with an ActiveX object, you should send the "finalize" message to its proxy. When an ActiveX object has no connections remaining, it will usually shut itself down unless it is visible or requires an explicit method call (for example "quit" shuts down Word 97).

For example:

invoke theCompLoanServer "finalize" returning theCompLoanServer

4.2.6 OLE Automation Exceptions

ActiveX clients get notified of OLE errors through Object COBOL exceptions. Any OLE errors raised by an ActiveX object (for instance, when you send a message that the object doesn't recognize), are returned to those ActiveX clients written in Object COBOL, as exceptions. The default exception behavior is for the client to display a message warning of the exception, and then terminate. You can trap the exception yourself though, and handle it with your own error processing code.

The instructions below assume you have first read the information on exception handling in the on-line help. Click Help Topics on the Net Express Help menu, then select Programming, Object-oriented Programming, Class Library Frameworks, Exception Handling.

To trap OLE exceptions:

  1. Declare the ExceptionManager, OLEExceptionManager, olesup and Callback or EntryCallback (see step 2) classes in the Class-Control paragraph of your ActiveX client:
    class-control. 
        ...
        OLEExceptionManager is class "oleexpt"
        ExceptionManager is class "exptnmgr"
        olesup is class "olesup"
        Callback is class "callback"
        EntryCallback is class "entrycll"
        ...
  2. Write an exception handler (either a method, or an entry-point), and create a Callback or EntryCallback to it. For example, a Callback looks like this:
    invoke Callback "new" using anObject z"methodName"
                      returning aHandler

    An EntryCallback looks like this:

    invoke EntryCallback "new" using  z"entryPointname"
                      returning aHandler
  3. Register the Callback or EntryCallback against the OLEExceptionManager class. For example:
    invoke ExceptionManager "register"
             using OLEExceptionManager aHandler

Now all OLE exceptions sent through to the client get sent to your exception handler.

Your exception handler receives two parameters; the first is the object handle to the OLEExeptionManager, and the second is an exception ID. To get the OLE error code, you need to subtract the base OLE Exception error number from the exception ID. You can get the value of the base OLE Exception error number by sending the "getBaseOleException" message to class olesup. To get the base number:

invoke OleSup "getBaseOleException" returning lsBase

where:

lsBase PIC X(4) COMP-5

The table below gives brief descriptions of the exception numbers returned through the ExceptionManager by the Object COBOL run-time system.

Value
Description
1 Server defined OLE exception
2 Parameter count mismatch.
3 OLE type mismatch error
4 OLE name not found
5 Out of memory for OLE operation.
6 Name is a method
7 Name is a property
8 OLE automation error
9 OLE server unavailable
10 The OLE server threw an exception

The exception method can get more information about the OLE error which occurred by sending the message "getLastSCode" to OleSup

invoke olesup "getLastSCode" returning lsErrorCode

where:

lsErrorCode PIC X(4) COMP-5

For information about OLE error codes, consult the Win 32 SDK documentation.

The first piece of example code below is a short COBOL subroutine for handling OLE exceptions, called ole-err1.cbl. The second piece wraps the subroutine in an EntryCallback, and registers it as an OLE exception handler.

The exception handler:

 class-control.
     olesup is class "olesup"
     .
 working-storage section.
 01 wsOffset                 pic x(4) comp-5. 
 01 wsOLEException           pic x(4) comp-5.

 linkage section.
 01 lnkExceptionObject       object reference. 
 01 lnkExceptionNumber       pic x(4) comp-5. 
 procedure division using lnkExceptionObject
                          lnkExceptionNumber. 
     ... 
     invoke olesup "getBaseOleException" returning wsOffset 
     subtract wsOffset from lnkExceptionNumber 
                     giving wsOLEException  *> OLE exception 
     ...      
     exit program.

Registering the exception handler:

class-control.
    ...
    EntryCallback is class "entrycll"
    oleexpt is class "oleexpt"
    ExceptionManager is class "exptnmgr"
          .

 working-storage section.
 ...
 01 exceptionHandler        object reference.
 ...
 procedure division.
 ...
*>   Set up exception OLE exception handler
     invoke EntryCallBack "new" using z"exception-section"
                            returning exceptionHandler
     invoke ExceptionManager "register" using oleexpt
                                              exceptionHandler
     ...

4.3 Writing ActiveX Objects in Object COBOL

The Microsoft OLE Programmer's Reference defines an ActiveX object as: "an instance of a class that exposes properties, methods, and events to ActiveX clients". You can create ActiveX objects from an Object COBOL class that meets the following criteria:

Use the Net Express Class Wizard to generate the class skeleton for you, a registry entry, type library, and a trigger program (if required). To run the Class Wizard, click New on the File menu and select Class from the New dialog box.

An ActiveX program can be run in one of three ways:

The source code for all three types is the same, but the way you build and register them is different. The next two diagrams contrast in-process, out-of-process and remote objects.



Figure 4-3: Out-of-process and in-process ActiveX objects

The following sections tell you how you can write, register and debug your own OLE Automation Servers using Object COBOL.

4.3.1 Writing a Class for OLE Automation

Object COBOL OLE automation servers are Object COBOL classes. An instance of this class is an ActiveX object. We have split writing a class into three stages:

  1. Generating a Skeleton Server Class
  2. Adding Methods to the ActiveX Object
  3. Adding Properties to the ActiveX Object

Generating a Skeleton Server Class

The Net Express Class Wizard can generate an outline of your OLE automation class, trigger program and registry entry for you. Before you run the wizard, you need to decide the following:

Then, to run the wizard:

When you click the Finish button on the last wizard dialog box, the Class Wizard generates or updates the following files and adds them to the project:

If you asked for a type library, it also generates these files:

The Class Wizard also ensures that your class is built as a .dll or .exe depending on whether you elected to build it as an in-process or out-of-process server.


Note: When you create an in-process server, the Class Wizard also adds to your Net Express project directives which link in dllserver.obj to the .dll file. Dllserver.obj is supplied in the Net Express link libraries (these are installed in \Program Files\MERANT\Net Express\base\lib by default).

To convert an out-of-process server to an in-process server: rebuild the class as a .dll file, omitting the trigger program, and add dllserver.obj to the Additional CBLLINK Directives Field, on the Link tab of the Build Settings dialog box for the .dll.

  1. Select the files for your OLE automation server (not including the trigger program) in the left-hand pane of the Project window, right-click and select Package Selected Files, Dynamic Link Library from the context menu.

  2. Enter a name for the .dll file into the Define New Dynamic Link Library dialog box, and click the OK button.

  3. Right-click on the .dll file in the Project window, and click Build Settings on the context menu.

  4. Click the Link tab and set the following:

    Click the Close button.

  5. Rebuild the project to create the .dll file.

  6. Click OLE Registry Entry Generator on the Tools menu, and create a new registry entry for the server, selecting the In-Process DLL radio button under Server Details.


Adding Methods to the ActiveX Object

All the instance methods you add to your OLE automation server class are exposed as methods of the ActiveX object, unless the method name starts with "set" or "get" (see the section Adding Properties to the ActiveX Object). Declare any parameters you want passed to or from the method in the Linkage Section of the method. The Object COBOL run-time system converts between COBOL and OLE automation data types as explained in the chapter OLE Data Types.

To add a new method:

When you add or remove methods you must update any type libraries for the object. This happens automatically when you use the Method Wizard. For more information, see Working with Type Libraries.


Note: COM does not support class objects and class methods. Any class methods you write for an OLE automation server are ignored. However, if your class object needs notification of new instances being created, write a class method named "new". Put the notification code in this method. You do not need to put any code in this "new" method to actually create the "new" instance as you would with a non-OLE Object COBOL class.


Adding Properties to the ActiveX Object

Define OLE automation properties for your ActiveX object by writing "set" and "get" methods. Any OLE property get or set operation from a client is mapped onto the "set" and get" methods. To enable setting a property, name the method "setpropertyname", where propertyname is the name of the property. To enable getting a property, name the method "getpropertyname", where propertyname is the name of the property.

Set methods have one USING parameter, from which the property is set, and Get methods have a RETURNING parameter, used to send back the value of the property. The Object COBOL run-time system converts between COBOL and OLE automation data types as explained in the chapter OLE Data Types.

To add a new method:

When you add or remove methods you must update any type libraries for the object. This happens automatically when you use the Method Wizard. For more information, see Working with Type Libraries.

The following example shows a method which enables an OLE automation client to set a property called LoanTermYears, and another which enables the client to get a property called YearPayment.

 method-id. "SetLoanTermYears".
 linkage section.
 01 NewLoanTermYears pic 99.
 procedure division using NewLoanTermYears.
      move NewLoanTermYears to LoanTermYears
      exit method.
 end method "SetLoanTermYears".

 method-id. "GetYearPayment".
 linkage section.
 01 CurrentYearPayment pic $9(7).99.
 procedure division returning CurrentYearPayment.
      move YearPayment to CurrentYearPayment
      exit method.
 end method "GetYearPayment".

4.3.2 Registering an OLE Automation Server

An OLE automation server must be registered with the Windows system before a client can send it messages, and use it to create an ActiveX object. There are four ways of registering Object COBOL automation servers:

The first of these methods can be very useful when you are developing and debugging a server. The other methods are preferable when you don't know when clients are likely to request the server to start. Once the server is entered into the Registry, Windows starts the server automatically when a client requests a new instance of the server.

Some development environments (for example Visual Basic) automatically register any new ActiveX object you create on the development machine - although you don't see the registration being carried out, it still happens behind the scenes. You need to register the object on each machine where you want to deploy it.

The Net Express Class Wizard generates a .reg file with all the information you need to register an object, whenever you use it to create an OLE automation server. This is an ASCII text file containing the entries to be inserted into the Windows registry. The format of this file is documented in the section Editing Registry Entries.

The information enables a client to obtain a unique ID for the object (its Clsid) from the object's ProgID. The ProgID for Object COBOL classes is by default the filename of the class, although you can change this by editing method "queryClassInfo" (see the next section). The Clsid enables the client to find another entry in the registry which provides the information needed to start the OLE automation server.

There are two ways of entering the information in the .reg file into the Windows registry:

Enabling Self-Registration

You can build your OLE automation servers to contain all the information needed for successful registration. The information is stored inside two methods in each OLE automation class:

These methods are generated for you by the Class Wizard when you create an OLE automation class ("queryLibraryInfo" is only included if you generate a type library at the same time). You can change some of the information returned by these methods to alter self registration details for your class.

The "queryClassInfo" method looks like this:

 method-id. queryClassInfo.
 linkage section.
 01 theProgId             pic x(256).
 01 theClassId            pic x(39).
 01 theInterfceId         pic x(39).
 01 theVersion            pic x(4) comp-5.
 01 theDescription        pic x(256). 
 01 theThreadingModel     pic x(20).  
 procedure division using by reference theProgId
                          by reference theClassId
                          by reference theInterfceId
                          by reference theVersion
                          by reference theDescription
                          by reference theThreadModel.
 move z"{clsid}" to theClassId
 move z"{clsid}" to theInterfceId
 move z"A description of your class" to theDescription
*> For automatic registration of the DLL, you can specify the
*> the ThreadingModel registry entry
 move z"Apartment" to theThreadModel
 exit method.
 end method queryClassInfo.

where clsid is a string representing a 128-bit clsid. For example: {F08FCA68-286C-11D2-831F-B01A09C10000}. Do not alter the clsid value; it is generated by a Windows API call that guarantees every class a unique clsid. The clsid is used in a number of different places as part of registering a class and its interfaces and the values must match or the results are unpredictable. You can change the ProgID of the class by moving a null-terminated string with the new ProgID into data item theProgId. The version number is currently ignored by the COBOL run-time system.

The "queryLibraryInfo" method looks like this:

 method-id. queryLibraryInfo.
 linkage section.
 01 theLibraryName        pic x(512).
 01 theMajorVersion       pic x(4) comp-5.
 01 theMinorVersion       pic x(4) comp-5.
 01 theLibraryId          pic x(39).
 01 theLocale             pic x(4) comp-5.
 procedure division using by reference theLibraryName
                          by reference theMajorVersion
                          by reference theMinorVersion
                          by reference theLibraryId
                          by reference theLocale. *> currently ignored
 move 1 to theMajorVersion
 move 0 to theMinorVersion
 move z"{clsid}" to theLibraryId
 exit method.
 end method queryLibraryInfo.

As for "queryClassInfo", you should not change the value of the library id. However, you can specify the location of the type library for a class by setting theTypeLibrary to one of the following:

no string Indicates that the type library is embedded as a resource in the .dll or .exe file for the class. This is the default location for classes created with the Class Wizard.
n A numeric value indicates that the type library is a resource in the .dll or .exe file for the class.
path A full path and filename to the type library.

You can also optionally set major and minor version numbers for your type library by setting the values of theMajorVersion and theMinorVersion.

Registration procedures for in-process and out-of-process servers are described below:

Out-of-process Servers

It is good practice to enable your out-of-process servers to register and unregister themselves. Self-registration is usually controlled by a command-line switch. For example, to register myserver.exe, the end-user enters the following command:

myserver /Register 

To unregister the server, the end-user enters:

myserver /Unregister 

The olesup class has two methods, "registerServer" and "unRegisterServer" to enable you to do this easily. You need to add code to the trigger program for the server which detects switches on the command-line for registration and unregistration.

When the trigger program detects the registration switch, execute the following code:

invoke olesup "registerServer" using theClass commandline
                           returning error-code

The "registerServer" method in olesup interrogates theClass for class and type library information using the "queryClassInfo" and "queryLibraryInfo" methods.

When the trigger program detects the unregistration switch, execute the following code:

invoke olesup "unregisterServer" using theClass 
                             returning error-code

where:

Data Item
Datatype
Description
theClass OBJECT REFERENCE The class for registration.
commandline PIC X(256) The command-line to start the server. You should include the path to the server.
error-code PIC X(4) COMP-5 Returns 0 for a successful registration. If unsuccessful returns an OLE error code.
In-process Servers

The Microsoft regsvr32 utility enables you to register or unregister an in-process server (built as a .dll file) from the command line. To register a server:

regsvr32 server.dll

To unregister a server:

regsvr32 -u server.dll

In addition to the "queryClassInfo" and "queryLibraryInfo" methods generated automatically by the Class Library Wizard, the .dll file must include an entry-point named DllOleLoadClasses for regsvr32 registration to work. The Class Library Wizard also creates this automatically if you select the option to generate a trigger program for the class.

The code below shows an example of DllOleLoadClasses:

*> OLE server trigger (for in-process servers only)
*> WARNING: Do not add further entry points to this file
$set case
 linkage section.
 01 loadReason    pic x(4) comp-5.
 procedure division.
 entry "DllOleLoadClasses" using by value loadReason.
*> OCWIZARD - start classes
*> Calling the class registers it as available to OLE clients
     call "myserver"
*> OCWIZARD - end classes
      exit program.
       .

The example above registers a single class called with the filename myserver.

You can add your own code to the DllOleLoadClasses - the parameter loadReason tells you why the COBOL run-time system has called this entry-point:

1 Server is being loaded
2 Class is being registered
3 Server is being unregistered

4.3.3 Setting Termination Options

By default, out-of-process servers terminate automatically when the last client logs out. This is not always desirable behavior - if for example, if the server is displaying a window on the desktop. You can change this by using the "setOLETerminateOption" method in class OLESup:

invoke olesup "setOLETerminateOption" using by value option

where option is one of the following:

0 Only allow the server to terminate if no windows are visible.
1 Only allow the server to terminate if no windows are visible, apart from a console window.

(An Object COBOL GUI application opens a console window if your application uses DISPLAY statements).
2 Allow the server to terminate regardless of any windows open (this is the default behavior).

4.3.4 Debugging ActiveX Objects

You debug ActiveX objects using Net Express. The procedure is slightly different for in-process and out-of-process ActiveX objects.

Debugging Out-of-process

When the OLE clients and server are running as separate processes, you need to start a copy of Net Express to debug the server. There are two different ways of doing this:

To debug by starting the trigger:

  1. Start Net Express and start animating the trigger program for the server application.

  2. Step through the trigger program until you get to one of the following statements:
    invoke olesup "becomeServer"

    or

    invoke anEventManager "run" 
  3. When you step this statement, the Net Express Animator stops responding.

    The server is now waiting in an event loop to receive a message from an OLE client. You can now run or debug the client application. When the client sends a message to the server, the Net Express Animator wakes up with the execution point positioned on the first statement of the OLE server method invoked.

    You can now debug the server method. When you step the EXIT METHOD statement, execution returns to the event loop and Net Express Animator is suspended again until the server receives another message.

To debug by adding a call:

  1. Locate the point in your source code at which you want to start debugging.

  2. Add the following statement:
    CALL "CBL_DEBUGBREAK" 
  3. Rebuild the server.

  4. Run the client program.

    When execution in the server reaches the call to "CBL_DEBUGBREAK", a message box appears asking you whether you want to start animating.

You can either run your client normally, in which case you will only debug the server, or you can start the client inside a debugger as well, in which case control switches between the two debuggers as you step into message sends from the client to the server. If the client is written in Object COBOL, you need to start a second instance of Net Express to debug the client. If the client is written in another language, debug it using the tools provided by the language vendor.

In-process Servers

In-process servers are loaded inside the same address space as the client. If the client is written in Object COBOL, simply debug it using Net Express, and as you step through statements that send messages to the server, the server code appears inside the Net Express Animator.

If the client is written in a different language, add a class method "new" to your Object COBOL OLE automation server. Put the following statement inside the "new" method:

 call "CBL_DEBUGBREAK"

When the client starts the server, it automatically starts up Net Express, enabling you to debug server code.

4.3.5 Working with Type Libraries

The type libraries generated by the Net Express Class Wizard contain the following information:

The following sections describe how you can keep the type libraries up-to-date as you change your server program:

You can also generate a type library at any time for an existing OLE automation class using the Net Express Ocreg utility. Click Help Topics on the Net Express Help menu and look up Type Library on the Index tab for more information.

4.3.5.1 Adding Properties and Methods

You should use the Method Wizard to add all properties and methods. The Method Wizard automatically updates the type library with the new interface information each time you start it.

4.3.5.2 Deleting Properties and Methods

To delete a method or property from a type library:

  1. Delete the method from the class source code.

  2. Open the filename_if.idl file in a text window (double-click on the name in the Net Express Project window).

  3. Locate the method in filename_if.idl. Each property get and set is also stored as a separate method. All methods are stored between comments like this:
    //OCXWIZARD - start methods
    ...
    //OCXWIZARD - end methods

    A method looks like this:

    [id(DISPID_CLASSNAME_METHODNAME)] HRESULT MethodName (
         parameter_descriptions);

    and a property looks like this:

    [id(DISPID_CLASSNAME_SETPROPERTYNAME), propput] HRESULT PropertyName (
        parameter_descriptions);
    [id(DISPID_CLASSNAME_GETPROPERTYNAME), propget] HRESULT PropertyName (parameter_descriptions);

    where:

    CLASSNAME Name of the class
    METHODNAME Name of the method
    property_name Name of the property
    parameter_descriptions One or more lines describing the parameters used by the method
  4. Note the method's ID (DISPID_CLASSNAME_METHODNAME) and delete the method.

  5. Open filename_if.h in a text window, then locate the method's ID and remove it.

  6. Rebuild class, the type library (.tlb file) and the resource file for the class (filename.res), either individually or by rebuilding the Net Express project.

  7. Relink or rereference any clients which use the type library.

4.3.5.3 Changing Parameter Datatypes

To change the datatypes of parameters in a method or property set or get:

1) Change them in the source 2) Locate the method in the filename_if.idl file and change the IDL types to match the new COBOL types. (A good way to do this is to create a test class & add a method to it to see what the wizard gives) 3) Recompile the .TLB and server 4) Relink/rereference any clients that use the type library

  1. Locate the method (or methods) in the class source code, and change the COBOL datatypes of parameters.

    If you are changing a property datatype, the datatype must match for the get and set methods.

  2. Open the filename_if.idl file in a text window (double-click on the name in the Net Express Project window).

  3. Locate the method in filename_if.idl. Each property get and set is also stored as a separate method. See the previous section for information on identifying methods.

  4. Change the IDL datatypes for the method to match the changes you made to the COBOL source code.

    If you are not sure how to match IDL and COBOL datatypes, use the Class Wizard to create a dummy OLE automation server, and then use the Method Wizard to add methods with the datatypes you want. You can then copy the parameter information from the type library for your dummy class to the one you are working on.

  5. Rebuild class, the type library (.tlb file) and the resource file for the class (filename.res), either individually or by rebuilding the Net Express project.

  6. Relink or rereference any clients which use the type library.

4.3.6 Setting Threading Options

You only need to read this section if you intend to build and run your servers multi-threaded. By default, Object COBOL servers are built single-threaded. Single-threaded COBOL programs cannot be run with any COM threading option other than single-threaded.

For more information about building multi-threaded programs with Net Express, click Help Topics on the Net Express Help menu, then select Programming, Multi-threaded Programming on the Contents tab.

COM has the following different threading options for servers:

COM manages threading differently for out-of-process servers and in-process servers.

4.3.6.1 Out-of-process Servers

Out-of-process servers run as single-threaded when built for single-threading (the default for COBOL programs), and free-threaded when built for multi-threading.

To set a multi-threaded out-of-process server to run as apartment threaded, include the following statement in the trigger code for the server, before the class is executed:

invoke olesup "singleThread" returning aBoolean 

where aBoolean is a PIC X(4) COMP-5, set to 1 for success.

4.3.6.2 In-process Servers

By default COM always runs in-process servers as single-threaded, regardless of build options. You can only change the threading options for a in-process server by editing its registry entry:

  1. Locate the InProcServer32 key in the server's registry entry.

    The full registry path to this key is: [HKEY_CLASSES_ROOT\{clsid}\InProcServer32] where clsid is the CLSID for the server.

  2. Change the ThreadingModel named value under this key to one of the following:

Note: The registry files generated by the Net Express do not include the named value ThreadingModel. It is not included in registry entries generated by running regsvr32 either.

You can either add this key to a registry file generated by Net Express, manually edit the registry file, or write code to use the Windows API to edit the registry when the server is registered. You can add this code to the entry-point DllLoadClasses in the in-process trigger program generated by the Class Wizard - see the section In-process Servers for more information.


4.4 Using DCOM

DCOM (Distributed Common Object Model) enables you to distribute ActiveX clients and objects across different machines on a network. You can run any Object COBOL ActiveX object across a network by providing the right registry information on the client and server machines. The registry entry for the client machine enables it to find the remote machine where the server exists.



Figure 4-5: Locating a remote ActiveX object

You need to generate two .reg files for your ActiveX object; one to register it on client machines, and one to register it on the remote server machine. The Net Express Class Wizard does this for you if you specify that an OLE Automation server class is remote when you create it, but quite often you develop an ActiveX object as a local server, and then redeploy it as a remote server when you have finished.

To generate the registry files:

  1. Click OLE Registry File Generator on the Net Express Tools menu.

  2. In the Server Details group box:
  3. In the Classes group box:

    Unless you are going to install the server in the \windows\system directory on the remote machine, also include a full path when you enter its filename.

Use the client registry file to register the server on all the client machines you want to have access to it. Use the server registry file to register the server on the server machine. You also need to install either Net Express or Application Server on your server machine.

4.4.1 DCOM and Security

Windows NT security can occasionally cause some problems in starting, running and accessing distributed servers. This section lists the main points you should check when using DCOM and Windows NT:

See Microsoft documentation for more information about security issues.

4.5 Editing Registry Entries

Net Express generates .reg files for you to make it easy to register your ActiveX objects. This section provides some information on the format of these files, in case you need to change the path to a server. A .reg file is an ASCII file in the following format:

REGEDIT
[HKEY_CLASSES_ROOT\classname]

@ = description

[HKEY_CLASSES_ROOT\classname\Clsid]

@ = uuid

[HKEY_CLASSES_ROOT\CLSID\uuid] 

@ = description

[HKEY_CLASSES_ROOT\CLSID\uuid\ProgID] 

@ = classname

[HKEY_CLASSES_ROOT\CLSID\uuid\servertype] 

@ = startup

where the parameters are:

classname The ProgID, which is the name by which your OLE automation server is known to clients. The ProgID is usually the base filename.
description A brief text comment describing the server.
uuid A unique numeric identifier created by theCreateGUID() function in the Windows API. You can also create these by running the UUIDGEN tool (this is explained below).
startup The trigger to run your server. If your trigger is compiled as a .exe file, you just need the trigger name. If you have compiled it as a .int or .gnt file, you need to use the COBOL run command.

This could be the name of a .exe file, or it could be a batch file, or a COBOL run command. For example:

run mytrigger

The .exe or batch file must be on the system path (set by the PATH environment variable) or have an explicit path set as part of the startup parameter.

servertype The type of OLE server. Common types are LocalServer32 and InProcServer32.

The UUIDGEN tool is part of the Microsoft Win32 SDK. Each time UUIDGEN runs, it generates a unique hexadecimal number.

To enter the information into the Windows registry, enter the following at a command prompt:

regedit registryfile 

where registryfile is the name of the file with your registry entry.

You need to register the automation server each time you install it into a different Windows system. You can use the same registry file each time - although if the registry file contains any machine specific paths you must edit them.


Note:

Registry files can vary in format and structure. For full details, see your Win 32 SDK documentation.


4.6 For More Information

OLE is a large subject, and is not covered in depth by a short chapter like this. You might find it helpful to familiarize yourself further with OLE. Use the information sources listed below to do this:

The following locations on the Microsoft Web Site are also worth exploring:


Copyright © 2000 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

PreviousUsing Other Object Domains OLE Data TypesNext