HOW TO - Call a C Library on the client machine with WebUtil

Introduction

Many Forms applications call out to 3rd party code, such as C, to perform processing outside the scope of Forms. For example, performing calculations or interfacing with a hardware device. The traditional way of performing this functionality is via ORA_FFI which provides an interface to these external programs. However, when moving your Forms application to the Web, the calls through ORA_FFI are now running on the application server and not the client machine. It could be that instead of calling a 3rd party program on the application server, the code needs to be executed on the client machine (for example, the software needs to interact with a hand held scanner or a measuring device). WebUtil provides an interface for calling C code on the client machine.

A Sample Application using ORA_FFI

Before embarking on a discussion on how to run this code using WebUtil, we will first of all investigate how a client/server application would use and interact with, in this example, a C program.

//Simple C function that just puts something in a string
// and returns the length of that string both as the RC and as a param
// Keeping the C interface as simple as possible.

__declspec( dllexport ) int PopulateString ( char *FormsBuffer, int *BuffLen)
{
int LocalLength;
strcpy(FormsBuffer,"A Fixed string from within the C program");
*BuffLen = strlen(FormsBuffer);
LocalLength = strlen(FormsBuffer);
return LocalLength;
}

In this case, a C program is made available in the form of a DLL called ffisamp.dll. This is a very simple C program that contains a function called PopulateString that simply accepts a string and the string length and returns a new string as a parameter and a count of the number of characters as both a parameter and a return code. Download the file from here.

How an ORA_FFI call is structured

ORA_FFI calls are defined as packaged functions. The package may handle an entire library or a single function of that library. Each library function has to be separately registered and each library function requires a PL/SQL function to invoke it.

Examining the ORA_FFI code

The Forms source is available from here; however, the relevant code is:

lh_ffisamp := ora_ffi.find_library('ffisamp.dll');
lh_ffisamp := ora_ffi.load_library(NULL,'ffisamp.dll');

This will create a handle to the DLL and load it ready for us.

fh_PopulateString := ora_ffi.register_function(lh_ffisamp, 'PopulateString',ora_ffi.C_STD);

The specific function that will be called is registered in a variable.


ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_CHAR_PTR);
ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_INT_PTR);
ora_ffi.register_return(fh_PopulateString,ORA_FFI.C_INT);

These three calls will register the parameters. The first is the string buffer which is passed in and then changed and passed out. The second is the buffer length which is passed in, updated and then passed out. The final call is for the return code which is an out parameter which also indicates the string buffer length.

How a WebUtil_C_API Call structured?

WEBUTI_C_API calls have a similar structure to ORA_FFI calls, which is not surprising given they are essentially doing the same thing. However, there are some subtle differences. WEBUTIL_C_API has a more flexible interface when compared to ORA_FFI. For example, libraries do not need to be registered; instead, the library is included when one of its functions is registered. Even the function itself does not need to be registered explicitly; library functions can simply be invoked and the invocation handles the registration. However, sticking close to the existing ORA_FFI structure will make the conversion simpler.

Examining the WEBUTIL_C_API code

The Forms source code is available from here.

f_handle := WEBUTIL_C_API.register_function('ffisamp.dll','PopulateString');

This call is similar to the ORA_FFI.Register_Function, however, this call is optional since the function invocation methods are overloaded and can take a library name and function as parameters. However, there is an overhead in using WEBUTIL_C_API.INVOKE_XXX like this, so it is good practice to explicitly register functions that are heavily used, for example inside a loop, or are shared between Forms.

args := WEBUTIL_C_API.create_parameter_list;
param1 := WEBUTIL_C_API.add_parameter(args,WEBUTIL_C_API.C_CHAR_PTR,WEBUTIL_C_API.PARAM_INOUT ,StringBuffer, 255);
param2 := WEBUTIL_C_API.add_parameter(args,WEBUTIL_C_API.C_INT_PTR ,WEBUTIL_C_API.PARAM_INOUT ,StringLength);

This next call is the ORA_FFI equivalent to register_parameter which defines the list of parameters to be passed to the DLL. In each case, a handle to the parameter list is passed and each parameter has its type, status (in, out or inout) and value defined.

rc := WEBUTIL_C_API.Invoke_Int( 'ffisamp.dll','PopulateString',args);

This call will now invoke the named function and pass the parameter. Note that the name of the WEBUTIL_C_API function is dependent on the type being returned.

message('The New value for String is: '||WEBUTIL_C_API.Get_Parameter_String(args, param1));
message('New Length '||to_char(WEBUTIL_C_API.Get_Parameter_Number(args, param2)));

These two calls will output the value of the String Buffer and String Length parameters which have been update from the call to the C program

WEBUTIL_C_API.Destroy_Parameter_List(args);

WEBUTIL_C_API.Deregister_Function(f_handle);

A call to Destroy_Parameter_List will clean up the parameter list, and optionally, a call can be made to unload the library using Deregister_Function. All libraries are unloaded automatically when the form is closed. Nevertheless, it is good practice to de-register a function once it is no longer needed; this helps to avoid memory leaks in a long running application.

 

Set Up

For the steps to set up WebUtil, please refer to the WebUtil Familiarization Manual available as part of the software download. However the main points to note are that dll should be placed in the <webutil>\lib directory.

How WebUtil finds the DLL

As mentioned above, the full documentation for setting up WebUtil is available in the WebUtil manual. However, in the context of this paper it is interesting to know the outline of how the DLL is located.

The location of the file to be downloaded onto the client machine is specified in the webutil.cfg file (see install.syslib.location relative to the webutil codebase). This ensures that all the DLL and EXE files needed by WebUtil are downloaded on to the desktop and will prevent any future configuration issues.

The DLL will be downloaded onto the client machine in a place which is available on the client machine's PATH ($JINITIATOR_HOME/bin). WEBUTIL_C_API, therefore does not in fact need the file path when the function is registered since it is picked up from PATH.

When a Form using WebUtil starts up, it checks a directory to see whether all the executables in the webutil.cfg file are in the $JINITIATOR_HOME/bin directory. It downloads any required files (new or changed) from the server side $WEBUTIL_HOME/lib directory. Currently the destination directory on the client machine is not configurable.

Acknowledgment

Thanks to A.P. Clarke of LogicaCMG for his help on documenting these features.

 

 


E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy