Chapter 18 Creating ActiveX Clients


Develop and test the ActiveX client

To write and test code for your ActiveX client, you must be connected to a server (or have the server running on your machine) and have the ActiveX runtime files installed on your machine. To install the ActiveX runtime files, see "Deploy the ActiveX client"; if you install EAServer on your machine, you have the option to install the ActiveX runtime files as well. For more information, see the EAServer Installation Guide.

Before invoking methods on component instances, the client must connect to a server and instantiate the components. There are two techniques for proxy instantiation:

If you currently have ActiveX proxy automation server clients, Sybase recommends that you migrate you current ActiveX clients to use the CORBA-style so that you can take advantage of the new benefits. The following features are available to CORBA style clients and not to EAServer 1.1 style clients:

The ORB, SessionManager, and other CORBA-style interfaces are documented in Chapter 4, "ActiveX Client Interfaces" in the EAServer API Reference.

Instantiating proxies using CORBA-style interfaces

Proxies are local objects that allow you to call EAServer component methods as if the component were a local object in your program. Instantiate proxies using the EAServer ORB and SessionManager::Manager interfaces, as follows:

Step What it does Detailed explanation
1 Initialize the CORBA ORB and create an ORB reference. "Initializing the ORB"
2 Use the ORB reference to create a Manager instance for the server. "Creating a Manager instance"
3 Use the Manager instance to create a Session. "Creating sessions"
4 Use the Session instance to create stub component instances. "Creating stub instances"
5 Call the stub methods to remotely invoke component methods. "Invoke component methods"

Note   If you are using Visual Basic, before using the ORB, Session, Factory, and Manager objects in your client, create references to JaguarORB.tlb, SessionManager.tlb and CtsSecurity.tlb in your Visual Basic project using the standard Visual Basic mechanism.

Initializing the ORB

Before any ORB classes can be used, you must call the init method, which:

Initialization parameters

The ORB.init() method acceps a formatted string that can contain settings for multiple initialization parameters. Pass initialization parameters as shown in this example, which configures the -ORBlogFile property and the -ORBpin property, to specify a file name for logging errors and the Sybase SSL-certificate-database password, respectively:

orb.init("-ORBlogFile=d:\jagorb.log,-ORBpin=sybase")
As shown in the example, parameter names and values must be separated by an equals sign, '=', and each name/value pair must be separated from the next with a comma and no white space.

For each initialization parameter, there is an equivalent environment variable. If the environment variable and initialization parameter are set, the value of the initialization parameter is used. Parameter and environment variable names are the same as for the C++ client ORB (see Chapter 15, "Creating CORBA C++ Clients").

You can set any initialization parameter to a value of none, which overrides the value of the environment variable and sets the value to the default, if any.

You can pass the following initialization parameters to the driver class:

Example: ORB initialization

ORB initialization is demonstrated in the following example.

Dim orb As ORB
Dim Manager As Manager
Dim Session As Session
Dim Factory As Factory

' Create a new ORB object
Set orb = New ORB

' Initialize the ORB instance
orb.init ("")

init returns an object reference to the EAServer ORB. When orb is deallocated or assigned a new object reference, it will be automatically released.

Creating a Manager instance

The SessionManager::Manager interface is used for interacting with a server. To create a Manager instance, you must identify a server listener using a URL of the format:

protocol://host:port

where:

Pass the URL to the string_to_object method to convert the URL string into a Manager instance, as shown in the following example. The object returned by string_to_object must be narrowed to the SessionManager/Manager interface.

Dim orb As ORB
Dim Manager As Manager
Dim obj as Object
... deleted orb initialization ...
Set obj = orb.string_to_object(
     "iiop://puddle:9000")
Set Manager = obj.Narrow_("SessionManager/Manager")
...

Creating sessions

The SessionManager::Session interface represents an authenticated session between the client application and a server. The createSession method accepts a user name and password and returns a session object, as shown in the example below:

Dim orb As ORB
Dim Manager As Manager
Dim Session as Session
Dim obj as Object
...deleted manager initialization
Set obj = Manager.createSession("jagadmin","")
Set Session = obj.Narrow_("SessionManager/Session")
...

Creating stub instances

You call the Session.lookup method to return an object reference factory. You then use the factory to create one or more proxies for the component.

lookup takes a string that specifies the EAServer component name. By default, the name is package/component, where package is the Jaguar Manager package name and component is the component name. Package and component names are not case sensitive. Component developers can override the default name by setting the JNDI Name property for EJB components, or the com.sybase.jaguar.component.bind.naming property for other types of components.

lookup returns a CORBA::Object reference. You use Narrow_ to convert the object reference into an instance of the factory for the component.

After instantiating the factory, the factory Create method returns an instance of the component proxy.

The code to instantiate a proxy for a component named Foo/Bar looks like this:

Dim Session as Session
Dim fact as Factory
Dim barComp as Bar // Component proxy
Dim obj as Object
...deleted session initialization ...
Set obj = Session.lookup("Foo/Bar")
Set fact = obj.Narrow_("SessionManager/Factory")
Set barComp = fact.Create()

Instantiating stub instances using the EAServer 1.1 interface

Sybase recommends that you use the CORBA style interfaces for new development. The EAServer 1.1 interface is provided for backward compatibility with existing applications.

To invoke EAServer components, your ActiveX client should:

  1. Declare proxy objects - The application creates an ActiveX interface pointer for the proxy object.
  2. Set connection properties - The application sets connection properties for the component instance. These properties describe the server that contains the component and the user name to be used for a connection.
  3. Instantiate server components - The application calls the proxy object's Initialize method. Initialize connects to the server and creates an instance of the server component. After Initialize succeeds, the server component methods can be called through the proxy object.
  4. Invoke component methods - The application invokes methods on the server, passing the appropriate ActiveX datatype for each parameter.

Declare proxy objects

Proxy objects are instantiated and invoked via ActiveX dispatch interfaces. EAServer proxy objects can be identified by their program identifier (ProgID). See "Check the ProgID for each interface" for more information.

Different ActiveX-enabled IDEs have different mechanisms for declaring an ActiveX object. In Visual Basic, you can simply declare the proxy object and instantiate it. For example, you can write either one of the following to instantiate a proxy object:

Dim bar as Bar
Set bar = New Bar
or
Dim bar as Object
Set bar = CreateObject("Foo.Bar")

Although the ActiveX proxy object exists once you have declared it, you cannot invoke methods until after you have set connection properties and called the Initialize method.

Set connection properties

Before calling the Initialize method, set the connection properties, such as UserName , Password , Host , and Name . The ActiveX client uses connection properties to connect to the server. This example sets the connection information for the employeeproxy object.

employeeproxy.UserName = "Guest"
employeeproxy.Password = "Guest"
employeeproxy.Host = "Jaguar"
employeeproxy.Name = "Company/Employee"

The user name and password, which must be specified, are required for login authentication and access control. The defaults for user name and password are empty strings. If the server administrator has enabled authentication, you must use a valid user name and password. If user access to the package or component is limited, the user name must be in a group that has access to the component. For more information on security, see "Security Configuration" in EAServer Administration Guide.

The Host property, which is optional, is the machine name and IIOP port number or the environment variable that specifies the machine name and IIOP port number. If the machine name and IIOP port number are specified for the Host property, the environment variable is ignored. See "Deploy the ActiveX client" for more information about defining the environment variable.

The syntax for specifying the machine name and IIOP port number is:

"machine:port"

where:

machine is the machine name.

port is the IIOP port number.

Note   If the Host property or environment variable is not specified, or defined incorrectly, the default, which is "localhost:9000", is used.

The Name property, which is optional, specifies the package and component names. By default, the package name is the same as the module name, and the component name is the same as the interface name. Specify the Name property when a component's package or component name is different from its module or interface name. The package and component are automatically located relative to the server's Initial Context property. The syntax for the Name property is:

"package/component"

where:

Instantiate server components

To instantiate the components on the server, use the Initialize() method. Initialize() establishes a connection to the server, using the connection properties you set in the previous step. If the server host name is not valid, or if another error occurs, the APAS displays an error message. This example executes the Initialize() method on the employeeproxy object, and instantiates on the server an instance of the Employee component belonging to the Company package.

employeeproxy.Initialize()

Invoke component methods

"ActiveX datatype support" lists the ActiveX types supported by EAServer, as well as the equivalent Jaguar Manager and CORBA IDL types.

EAServer components appear as automation objects in the ActiveX-enabled IDE. If your IDE supports it, you can simply drag and drop the component method into your ActiveX client code and use the IDE's object browser to see the component's method syntax. You must call the proxy methods using the syntax required by your development tool.

To execute a component method, execute the method on the proxy object. In this example, the GetEmployeeInfo() and the SetEmployeeInfo() methods are executed on employeeproxy . The parameters in the SetEmployeeInfo() method are in parameters. The parameters in the GetEmployeeInfo() method are inout parameters.

String name
Long age
String sex

name = "John"
age = 32
sex = "male"

// Example for parameters using the in argument mode
employeeproxy.SetEmployeeInfo (name, age, sex)

// Example for parameters using the inout argument mode
employeeproxy.GetEmployeeInfo (REF name, REF age, REF sex)

Methods may return result sets. After a method invocation, you can retrieve result sets as described in "Result-set support".

Note   If a component that the ActiveX client accesses is an ActiveX component and a C++ IDE such as Visual C++ was used to develop it, string parameter types are always passed by reference (as BSTR *). Make sure that you defined these parameters as inout in Jaguar Manager.

When you invoke component methods, these restrictions apply:

Code exception handling

Always make sure that your application handles exceptions gracefully. At minimum, you should display the exception text, which will aid debugging.

Errors in ActiveX proxy execution can be handled as ActiveX exceptions, or inline using a try/catch model similar to the structured exception handling model in the C++ and Java languages.

Using an ActiveX error handler

By default, the ActiveX proxy raises an ActiveX exception when an EAServer component method raises an exception or an internal error occurs. Visual Basic and most other ActiveX scripting tools do not allow you to handle these errors inline. Instead, control transfers to an error handler (specified by on error goto in Visual Basic) or to a system-wide error dialog box. To handle proxy errors inline, you must enable inline exception handling as described in "Handling exceptions inline".

Structure of an ActiveX exception

In C++, the OLE EXCEPINFO structure describes an ActiveX exception. Different ActiveX-enabled IDEs provide different mechanisms for applications to obtain the EXCEPINFO structure contents.

In Visual Basic, exceptions are mapped to the built-in Err object. The exception number maps to Err.Number and the description is available as Err.Description. You can handle exceptions by activating error handling code with On Error Goto statement or by checking whether Err.Number is > 0.

The proxy type library defines error numbers for client-side errors in the JagORBClientErrNum enumeration and server-side error numbers in the JagORBServerErrNum enumeration.

Note   IDL user-defined exceptions are not supported and are mapped to error number 9000.

Client error numbers

The following table lists the codes for client-side error numbers defined in the JagORBClientErrNum enumeration:

Table 18-1: JagORBClientErrNum error codes
Symbolic error code Number Description
jagClNonByteArrayErr 8000 Method arguments of type array can only have a base element type of byte.
jagClMultiDimArrayErr 8001 Multi-dimensional arrays not supported as an argument to a method.
jagClArrayRedimErr 8002 A Fatal Internal Error was encountered while attempting to resize a method argument of type array.
jagClArrayProcErr 8003 A Fatal Internal Error was encountered while processing a method argument of type array.
jagClArrayEmptyErr 8004 An array of size 0 was passed as parameter to a method.
jagClArrayBoundsErr 8005 A Fatal Internal Error was encounterd while attempting to determine the upper bound on a method argument of type array.
jagClNotJagComponentErr 8006 The component being instantiated is not a valid EAServer component or was not registered in the Windows Registry.
jagClOutOfMem 8007 The Application failed to acquire memory from the Operating System.
jagClCreateFactErr 8008 The EAServer Proxy Server could not instantiate a Factory Object. Please contact Sybase Technical Support.
jagClTypeLibErr 8009 The type library for the Component could not read from the Windows Registry. Please check if a valid directory location was specified for the Type Library while registering the component.
jagClTypeInfoErr 8010 The type information for the Component could not read from the Type Library. Please regenerate TLB and REG files for the component using Jaguar Manager.
jagClMethInfoErr 8011 The metadata for the method or component could not be read from the Windows Registry or the method is using parameter types that are not presently supported in the EAServer ActiveX Proxy.
jagClMethNameErr 8012 The metadata for the method invoked on component could not be read from the Windows Registry. Please regenerate TLB and REG files for the component using Jaguar Manager.
jagClCompNameErr 8013 The component name for the component being instantiated could not read from the Windows Registry.
jagClPkgNameErr 8014 The package name for the Component being instantiated could not read from the Windows Registry.
jagClPxyCreateErr 8015 Component creation failed.
jagClPxyDestroyErr 8016 Component deletion failed.
jagClPxyFuncDescErr 8017 The metadata information for the method could not read from the type library.
jagClArgCountErr 8018 There was a mismatch between the number of parameters passed to method and the number of parameters as described by the information in the type library.
jagClInternalErr 8019 An error was encountered while invoking an EAServer method.
jagClParamInfoErr 8020 The type information for a method parameter could not be read from the Type Library.
jagClTypeMismatchErr 8021 There is a mismatch between type of the value passed as an argument with its specified type in the Type Library.
jagClConversionErr 8022 The data conversion attempted is presently not supported.
jagClArgUpdateErr 8023 An error was encountered while updating an input-output or output parameter for a method.
jagClRetValSetErr 8024 An error was encountered while updating the return value for a method.
jagClRecsetArgErr 8025 The ResultSet type cannot be passed as a parameter in either the input or input-output modes by an EAServer ActiveX application.
jagClUnsuppTypeErr 8026 An unsupported OLE Automation type was used as a parameter in a method.
jagClAxConvertErr 8027 An error was encountered while converting a input-output method parameter received from the server.
jagClJagConvertErr 8028 An error was encountered while converting a input parameter prior to method invocation.
jagClNoInitErr 8029 an EAServer component instance must be created prior to invoking a method.
jagClRecordsetCreateErr 8030 An internal error was encountered while creating the Recordset object.
jagClRecordsetMoveErr 8031 Attempt to call MoveNext on a RecordSet which has its EOF property as TRUE.
jagClIteratorPosErr 8032 An invalid position was specified while attempting to retrieve an element from a collection.
jagClInvalidMethodErr 8033 The only method supported on the generic Object type is Narrow_.
jagClNarrowFailErr 8034 The object reference cannot be narrowed to the interface name specified.
jagClInvalidIntfErr 8035 The fully scoped interface name passed as an argument to the Narrow_ method is invalid.
jagClOrbInitErr 8036 An internal error was encountered while initializing client-side ORB.
jagClOrbStrToObjErr 8037 An internal error was encountered while invoking the ORB.string_to_object method.

Server error numbers

The following table lists the codes for server-side error numbers defined in the JagORBServerErrNum enumeration:

Table 18-2: JagORBServerErrNum error codes
Symbolic error code Number Description
jagSrvMethExcepErr 9000 The method implementation threw an user-defined exception while executing on the server.
jagSrvMethInvalidErr 9001 The method name is either invalid or is presently not defined in the component's interface.
jagSrvMethInvalidArgErr 9002 The invocation of the method on the server failed because an invalid number of parameters was passed or a parameter type mismatch occurred.
jagSrvMethNotImplErr 9003 The invocation of the method on the server failed because the component does not implement the method.
jagSrvCompPermErr 9004 The invocation of the method on the server failed because user does not have the permissions to instantiate the component.
jagSrvCompDeployErr 9005 The invocation of the method on the server failed because component implementation was not deployed on the server.
jagSrvInternalErr 9006 The invocation of the method on the server failed due a fatal internal error.
jagSrvArgCountErr 9007 The invocation of the method on the server failed because an invalid parameter type was used by the method.
jagSrvSrvConnectErr 9008 The requested operation failed since the client could not to acquire connection to the server.
jagSrvConversionErr 9009 The invocation of the method on the server failed due to a data conversion error.
jagSrvFreeMemErr 9010 The invocation of the method on the server failed while releasing memory resources.
jagSrvIntfReposErr 9011 The invocation of the method on the server failed while trying to access the interface repository.
jagSrvOutOfMemErr 9012 The invocation of the method on the server failed while trying to acquire memory from the Operating System.
jagSrvOutOfResErr 9013 The invocation of the method on the server failed since it could not acquire the necessary resources.
jagSrvSrvRespErr 9014 The invocation of the method on the server failed because there was no valid response from the server.
jagSrvInvObjrefErr 9015 The invocation of the method on the server failed because the object reference is invalid.

Handling exceptions inline

By default, the ActiveX proxy raises an ActiveX exception when an EAServer component method raises an exception or an internal error occurs. Visual Basic and most other ActiveX scripting tools do not allow you to handle these errors inline. Instead, control transfers to an error handler (specified by on error goto in Visual Basic) or to a system-wide error dialog box.

Inline exception handling can simplify the code that handles recoverable errors. For example, you can keep program logic that allows a user to retry a failed login in one place, rather than split into mainline code and the separate error handling code. Inline exception handling also allows you to handle errors explicitly in scripting tools that do not allow you to install user-coded error handlers.

The ActiveX proxy supports inline exception handling with Try, Catch, and End methods and an internal exception store. When an exception occurs with inline handling active, the proxy stores the error information rather than raising an ActiveX exception. Each component proxy object supports these methods and contains an exception store that is specific to that object. To handle exceptions inline, call the Try_, Catch_, and End_ methods as follows:

Special considerations

The Try_ and Catch_ methods do not have the same semantics of structured exception handling in Java or C++. In particular:

Example: using "catch all" exception handling

When you call the Catch_ method, you can check for exceptions of a specific type, or for exceptions of any type. To check for any exception, pass "..." as the exception type parameter.

The following example illustrates this style of exception handling:

barcomp.Try_
barcomp.methodThatRaisesException(1007)
Dim anyExcep As Object
If (barcomp.Catch_("...", anyExcep) = True) Then
    Dim excepType as String
    excepType = anyExcep.GetExceptionType
    if (StrComp(excepType, "Foo/NotValidIdException") == 0) then
       Dim invalidIdExcep as NotValidIdException
       set invalidIdExcep = anyExcep
       Dim id as integer
       Dim msg as String
       id = invalidIdExcep.id
       msg = invalidIdExcep.message
    Else if (StrComp(excepType, "Foo/NoAuthorizationException") == 0) then
        Dim noAuthorizationExcep as NoAuthorizationException
        set noAuthorizationExcep = anyExcep
        Dim user as String
        Dim cert as String
        user = noAuthorizationExcep.username
        cert = noAuthorizationExcep.certificate
    Else if (StrComp(excepType, "Jaguar/ClientException") == 0) then
        Dim systemExcep as SystemException
        set systemExcep = excep
        Dim code as integer
        Dim msg as String
        code = systemExcep.code
        msg = systemExcep.message
    End if
    Else
        ' No Exception has occurred. Proceed
End If

Exception datatypes

Exception datatypes are used with the Try_ method when handling exceptions inline. The ActiveX proxy includes predefined system exceptions that correspond to the standard CORBA system exceptions. User-defined exceptions that are declared in an IDL module are also mapped to ActiveX types.

System exceptions In IDL, system exceptions extend the CORBA SystemException IDL type:

interface SystemException
{
  long code;      // numeric error code
  string message; // text error message
};

Unlike user-defined exceptions, a component method can throw system exceptions that are not listed in the raises clause of the IDL method signature. The C++ and ActiveX client runtime engines may also raise system exceptions when errors occur in the processing of a method invocation.

In the ActiveX proxy, system exceptions are mapped to the interface SystemException with the following properties and methods:

The ActiveX proxy uses SystemException to represent the standard CORBA system exception types that can be returned by components, as well as errors that occur in the ActiveX proxy. "Exception identifiers" lists the system exception types.

User-defined exceptions In IDL, user-defined exceptions are defined using syntax similar to an IDL structure. For example:

exception InvalidValueException
{
  string message;
  string value;
};

User-defined exceptions can be defined within an IDL module or interface. The IDL method signature for a component method must list user-defined exceptions thrown by the method in the raises clause. A method cannot throw user-defined exceptions that are not listed in the raises clause.

In ActiveX, the IDL exception maps to an interface with the following properties and methods:

Exception identifiers Both system and user-defined exceptions support a GetExceptionType method that returns a string identifier for the exception. The exception identifier for a user-defined exception defined in a module is:

module/exception 

Where module is the IDL module name and exception is the IDL exception type. For example, "CtsSecurity/No Certificate Exception". The exception identifier for an exception defined in an interface is:

module/interface/exception 

Where interface is the IDL interface name.

Exception identifiers for system exceptions are predefined and listed in the following table:

Table 18-3: System exception identifiers
Identifier Notes
Jaguar/ClientException An error occurred internally to the ActiveX proxy. For example, you may have called a method that uses an unsupported parameter type.
CORBA/BAD_CONTEXT
CORBA/BAD_INV_ORDER
CORBA/BAD_PARAM
CORBA/BAD_OPERATION
CORBA/BAD_TYPECODE
CORBA/COMM_FAILURE A network error occurred. When creating a connection, this usually indicates that the server is down or you have specified the wrong listener address. When calling a method, the error may indicate a transient network fault; you can retry the method.
CORBA/DATA_CONVERSION
CORBA/FREE_MEM
CORBA/IMP_LIMIT
CORBA/INTERNAL
CORBA/INTF_REPOS
CORBA/INV_FLAG
CORBA/INV_IDENT
CORBA/INV_OBJREF
CORBA/INVALID_TRANSACTION
CORBA/INITIALIZE
CORBA/MARSHAL
CORBA/NO_IMPLEMENT The component does not implement the method that you called.
CORBA/NO_MEMORY
CORBA/NO_RESOURCES
CORBA/NO_RESPONSE
CORBA/NO_PERMISSION The user cannot access the server or a specified component.
CORBA/OBJ_ADAPTER
CORBA/OBJECT_NOT_EXIST The object does not exist. This can happen if:
  • The component is not installed correctly on the server. For example, the component class or skeleton class cannot be loaded.
  • The object represents a stateful component and your reference to it has expired. Check the value of the component's Instance Timeout property, and, if needed, code your client to create another instance in response to this error.
CORBA/PERSIST_STORE
CORBA/TRANSACTION_REQUIRED The method you attempted to call must be called in the context of an open transaction.
CORBA/TRANSACTION_ROLLEDBACK The method you called rolled back its transaction, or if you have started a client-managed transaction, the transaction timed out.
CORBA/TRANSIENT
CORBA/UNKNOWN

Example

This example calls a method CtsSecurity.SSLServiceProvider.setGlobalProperty. This method can be called to specify SSL settings for a connection to a server (see the EAServer Security Administration and Programming guide for more information).

The method signature and the exceptions raised are detailed in the following IDL:

module CtsSecurity
{
  interface SSLServiceProvider
  {
    string setGlobalProperty
    (
      in string property,
      in string value
    )
    raises (CtsSecurity::InvalidPropertyException,
      CtsSecurity::InvalidValueException);
  };
  
  exception InvalidPropertyException
  {
    string message;
    string property;
  };
  
  exception InvalidValueException
  {
    string message;
    string value;
  };
};

setGlobalProperty raises InvalidValueException if you attempt to set a property to an invalid value, and raises InvalidPropertyException if you specify a property that does not exist.

The following Visual Basic code calls setGlobalProperty and calls the Catch method to handle InvalidValueException inline. Since there is no Catch_ call for InvalidPropertyException, if this exception is thrown, it will be thrown as an ActiveX exception when End_ is called:

Dim ssp as CtsSecurity.SSLServiceProvider

// Assume ssp has been properly initialized

Dim ivException as CtsSecurity.InvalidValueException
// Activate inline exception handling
call ssp.Try
ssp.setGlobalProperty("qop", "An invalid value")
if (ssp.Catch_("CtsSecurity/InvalidValueException", ivException) then
  call MessageBox ("Invalid value: " & ivException.value & ". " & _
              ivException.message, , "Error");
endif
call ssp.End_

Using Try_ and Catch_ in multithreaded programs

If your program uses a proxy object in multiple threads and handles exceptions inline, you must call the Duplicate_ method to obtain a copy of the proxy object for use in each thread. Duplicate_ has the following syntax:

Object Duplicate_

Duplicate_ returns a proxy instance of the same type as the original.

 


Copyright © 2002 Sybase, Inc. All rights reserved.