COM/MTS Guide


Chapter 4 . Building Client Screens

In Panther Windows applications, any Panther client screen can access COM components. A series of C functions, also callable in JPL, are available for interacting with all Panther service components, in either a COM or Enterprise JavaBean Panther application. In addition, there are some C functions written specifically for COM or MTS deployment.

You can also embed ActiveX controls in a client screen. However, ActiveX controls are the only COM components visible in a client screen. All other COM components must first be instantiated before setting their properties or calling their methods. For information about using ActiveX controls in client screens, refer to Chapter 18, "ActiveX Controls," in Using the Editors.


Working with COM Components

Instantiating COM Components

You must first specify which type of service components are currently in use with the current_component_system property: PV_SERVER_COM for COM components.

To access a COM component, you must instantiate the component by calling sm_obj_create. It takes a string parameter, the name of the component, for example cCustomers or cCustomers.1.

You can also instantiate a COM component using its Class ID (CLSID) by calling sm_c_com_obj_create; this function takes a string parameter, the Class ID of the component. This is the unique identifier that is generated when COM components are first created. The curly braces in the Class ID specification are optional. A sample Class ID is something like:

{59CCB4A0-727D-11CF-AD36-00AA00A47DD2}

These functions return the object ID for the specified component or PR_E_OBJECT if they fail. Using the object ID, you can access the component's properties and methods.

Note that these functions are not needed when working with ActiveX controls. Because such controls are embedded in Panther screens, the properties of the ActiveX container specify the CLSID and name of the ActiveX control allowing Panther to instantiate the control.

In our sample screens, the unnamed JPL procedure declares a variable to contain the component's object ID and the screen entry function instantiates the component. For example:

vars id
proc entry
@app()->current_component_system=PV_SERVER_COM
id = sm_obj_create("cStrings")
return

If you use sm_obj_create to instantiate your components, you need to ensure that you do not have two components with the same name but with different CLSIDs on the same machine.

Destroying COM Components

After invoking and working with the methods or properties of a component, you should destroy it by calling sm_obj_delete_id. This function takes one parameter: the object ID for the component you wish to destroy. Otherwise, the component will continue to exist until the application terminates (or goes from test to edit mode).

If a component is running under MTS, its life cycle can be managed for you by MTS, depending on whether the component is marked as belonging to a transaction and whether the work in the transaction is complete.

In our sample screens, the components are programmatically destroyed during screen exit:

proc exit
call sm_obj_delete_id(id)
return

Accessing the Component's Methods

In order to access a COM component's methods, you need to know the component's parameters and call the function sm_obj_call. The syntax in JPL is as follows:

ret = sm_obj_call (objid, methodName, parm1, parm2, ....)

The function's first parameter is the object ID of the component whose method you wish to use. The second parameter is the name of the method you are calling. The rest of the parameters are a comma-separated list of the parameters to the method itself.

Specifying the method's parameters

COM components can take three kinds of parameters: in parameters, out parameters and in/out parameters. Parameters can be passed as literal strings or using the property API syntax. For out and in/out parameters, Panther assigns the returning values to the variables, fields or properties originally specified.

For example, you have a component called cEmployee that supports a method called newEmployee. newEmployee takes three parameters in the following order:

You can invoke NewEmployee method with the following JPL:

vars id, ret
@app()->current_component_system=PV_SERVER_COM
id = sm_obj_create ("cEmployee")
ret = sm_obj_call (id, "NewEmployee", \
EmpId, EmpName, StartDate)

In addition to the out parameters, this method call returns a value. ret contains the return value for the method.

A method cannot return an array. In such cases, that information needs to be passed as a parameter.

Determining the parameter's data type

At runtime, Panther uses the information in the type library to determine the data type for each parameter. Even if the type library is missing, Panther can generally determine the correct data type.

However, if the parameter must be passed using a dispatch interface (and therefore the object ID must be specified) and the type library is either missing or does not indicate a dispatch interface is needed, you will get a "type mismatch" error. In this case, generate the object ID and specify the parameter using @obj. For example, the following JPL from the Treeview Control in the COM samples uses this syntax:

vars imagelist   // imagelist control 
vars images // list of images in the imagelist
vars pic // one picture
imagelist = sm_obj_create ("COMCTL.ImageListCtrl")
images = sm_obj_get_property(imagelist, "ListImages")
pic = sm_com_load_picture ("logo.bmp")
call sm_obj_call (images, "Add", 1, '', @obj(pic))
sm_obj_delete_id (pic)

Calling Microsoft's COM Components

Popular Microsoft applications, such as Microsoft Excel and Microsoft Word, are implemented as COM components or Active Document Servers and can be called from your Panther application. One of the Panther COM Samples illustrates calls to Microsoft Excel which get and set data in a spreadsheet.

The following procedures instantiate Microsoft Excel and write data to a spreadsheet:

vars wsid, cid
proc screen_enter
{
wsid = sm_obj_create("Excel.Sheet")
cid = sm_obj_get_property(wsid, "Application.ActiveSheet")
}
proc setcell
{
call sm_obj_set_property \
(cid, 'Range(":(col):(row)").Value', CellValue)
}

The following procedures create a Microsoft Word document. For more information, refer to Microsoft documentation on how to use OLE Automation with Microsoft Word.

vars word
// Run "Word" and get the Application object
word = sm_obj_create ("Word.Application")
// Add a new document (untitled)
call sm_obj_call (word, "Documents.Add")
// Add some sample text
call sm_obj_call \
(word, "Selection.TypeText", "This is some text")
// Save the document with the specified name
// If you do not give a full path, the file will be
// put into Word's notion of current directory
// Note that colons must be doubled in JPL
call sm_obj_call \ 
(word, "ActiveDocument.SaveAs", "C::\Test.doc")
// Quit Word
call sm_obj_call (word, "Quit")
// and destroy the COM object
call sm_obj_delete_id (word)

Accessing the Component's Properties

Properties in COM components generally contain the application state information. You can use the sm_obj_set_property and sm_obj_get_property functions to access properties. The following example sets a property on the component associated with the id variable:

ret = sm_obj_set_property(id, "PropName", "PropSetting")

ActiveX controls have additional functionality. Their properties are listed in the Properties window and can be accessed at runtime using the property API syntax. For more information on property access for ActiveX controls, refer to "Setting Properties at Runtime" in Using the Editors.

Even though you cannot pass an array as a parameter to these functions, indexed properties are supported. The following command would get the 6th element of the property's array:

ret = sm_obj_get_property(id, "Name[6]")

Designating an Error Handler

You can define an error handler for COM method invocations, for example:

call sm_obj_onerror ("ErrorHandlerName")

The string passed to sm_obj_onerror is the name of a function that you want to designate as the error handler. If a COM operation (method call, property access, or object invocation) generates a negative exception code, the error handler function will be called. The specified function is passed three parameters: the error number in decimal format, the error number in hexadecimal format, and a description of the error.

COM methods can also return exception codes using the JPL verb raise_exception or its C equivalent sm_raise_exception. You can get the value returned by calling sm_com_result. The function sm_com_result_msg looks up the exception code in a Windows system table and returns the message to which it corresponds.

Designating an Event Handler

You can designate event handlers for COM components using sm_com_set_handler. Events are mainly programmed for ActiveX controls in response to user-initiated events, such as mouse clicks. For example:

call sm_com_set_handler (id, "Event", "EventHandlerName")

For more information, refer to "Specifying an ActiveX Event Handler" in Using the Editors.


Sample Client Screens

Although simple in appearance, this screen contains the fields and push buttons needed to operate the client part of an application. Nothing in the client interface indicates that it is calling a COM component. However, the JPL Procedures property contains the JPL processing to create and destroy the COM component, call its methods, and get its property values.

Figure 4-1 An application's client screen that searches for customer names.

The unnamed JPL procedure creates the variable which will hold the object ID of the COM component. During screen entry, the COM component is instantiated.

vars id
proc enter
{
@app()->current_component_system=PV_SERVER_COM
id = sm_obj_create("cCustomers")
}

On exiting the screen, the COM component is destroyed.

proc exit
{
call sm_obj_delete_id(id)
}

This JPL procedure calls the COM component's GetCustomer method and gets the value of the RowCount property:

proc do_search
{
vars error
CompanyName[1] = search
error = sm_obj_call (id, "GetCustomer", \
CompanyName, CustomerID, Phone)
rowcount = sm_obj_get_property (id, "RowCount")
}

The Panther COM Gallery includes this sample in addition to other COM components and client screens. To view these samples, open PantherInstallDir\samples\com\comsamples.lib in the Panther editor.

Writing a Java Event Handler

To implement the processing for the sample screen in Java, a screen event handler instantiates and destroys the COM component. The client screen calling this event handler has a Java Tag of ClientScreen.

import com.prolifics.jni.*;
public class ClientScreen extends ScreenHandlerAdapter{
   public void screenEntry(ScreenInterface s, int context){
FieldInterface id = s.getField("id");
FieldInterface id1=s.getField("id1");
CFunctionsInterface cFuncs = s.getCFunctions();
ApplicationInterface appface=s.getApplication();
ScreenInterface tscr=appface.getScreen();
   int a=appface.set_int
     (Constants.PR_CURRENT_COMPONENT_SYSTEM, 
Constants.PV_SERVER_COM);
id1.itofield(a);
      id.itofield(cFuncs.sm_obj_create("cStrings"));
}
   public void screenExit(ScreenInterface s, int context){
CFunctionsInterface cFuncs = s.getCFunctions();
FieldInterface id = s.getField("id");
cFuncs.sm_obj_delete_id(id.intval());

A button event handler for the Search push button calls the method and gets the number of returned rows. The push button calling this event handler has a Java Tag of SearchButtonHandler.

import com.prolifics.jni.*;
public class SearchButtonHandler extends ButtonHandlerAdapter{
   	public int buttonValidate
(FieldInterface f, int item, int context){
ScreenInterface s = f.getScreen();
FieldInterface idField = s.getField("id");
FieldInterface rowField = s.getField("RowCount");
FieldInterface searchField = s.getField("search");
FieldInterface
companyNameField = s.getField("CompanyName");
CFunctionsInterface cFuncs = f.getCFunctions();
companyNameField.putfield(1,searchField.getfield());
int id = idField.intval();
String i = cFuncs.sm_obj_call("(" + id + ",
'GetCustomer',CompanyName,CustomerID,Phone)");
String st = cFuncs.sm_obj_get_property
( id, "RowCount");
rowField.putfield(st);
return id;
}
}

For more information about Java event handlers, refer to Chapter 21, "Java Event Handlers and Objects," in Application Development Guide.