Programmer's Guide



Chapter 33   Accessing DLLs Directly

This chapter describes SilverStream's native DLL support. It gives an overview of DLL integration with SilverStream and explains how to call DLLs directly from SilverStream applications.

This chapter covers the following topics:

About DLL integration   Top of page

In some situations, a SilverStream application needs to be able to call native code. SilverStream applications can use the Java Native Interface (JNI) to call native methods. However, JNI is a low-level interface that requires that you write additional C or C++ code. Writing good JNI code is difficult.To simplify access to native code, SilverStream lets you use either of the following programming techniques:

The remainder of this chapter describes how to call native DLL functions directly. For information about invoking the services of COM objects, see Using COM Objects.

DLL Basics   Top of page

A DLL is a Windows dynamic link library that contains a set of machine-language procedures. DLLs provide separate entry points for functions that can be called by a process.

SilverStream support for DLL access   Top of page

SilverStream 3.0 provides a mechanism for calling DLLs directly from Java without writing any native code. The DllLib class in the com.sssw.rt.com package has several static methods that let you load a DLL into memory, get a function pointer, and make a call to the function. DllLib also provides services for memory management and for Java to C parameter conversion.

CAUTION!   By accessing DLLs directly, you can expand the capabilities of your applications in many ways. However, you need to use a much lower level interface to access a DLL than to access a COM object. Therefore, you must exercise caution when you write Java code to access a DLL. When you access a DLL from Java, you are able to manipulate C pointers directly. In addition, because SilverStream does not provide compile time checking for code that makes native calls, your code may not be type-safe. Even small typographical errors can result in runtime errors that will bring down the SilverStream Server. For example, if you misspell the name of the DLL or function you want to access (or use the wrong case), you will crash the server.

DllLib compared with J/Direct and JNI

DllLib provides many of the same services as J/Direct, a Microsoft facility that lets you call functions in a Win32 DLL. On the positive side, DllLib is not tied directly to the Microsoft virtual machine and compiler, whereas J/Direct is. On the negative side, DllLib requires that you write Java code to access a DLL, whereas J/Direct lets you use compiler directives.

Although you need to have a good understanding of C programming concepts to use DllLib, you do not need to write additional C code to access a native DLL, as you would if you used the Java Native Interface (JNI). This greatly simplifies native DLL access. However, code that relies on DllLib is, in general, more error-prone than code that uses JNI and will run on Windows NT only.

Writing Java code to access a DLL   Top of page

Once you've identified the DLL you want to access from a SilverStream application, you can begin writing Java code to access the DLL. You can access a native DLL from any SilverStream business object, form, or page.

Coding basics   Top of page

To call a DLL function from a business object, form, or page, you need to write Java code to perform these operations:

  1. Load the DLL into memory by calling DLLGetHandle().

  2. Get a pointer to the DLL function by calling DLLGetMethod().

  3. Translate all in parameters to native integers or pointers.

    DllLib provides several functions for parameter conversion.

  4. Allocate memory for all out parameters by calling allocCoTaskMem() or allocHGlobal().

  5. Perform the function call by using call(). You need to pass the pointer returned by DLLGetMethod() to the call() method.

  6. Translate the return value and out parameters to Java types.

  7. Free memory allocated for argument translation conversion and out parameters by calling freeCoTaskMem() or freeHGlobal().

  8. Free the handle for the DLL by calling DLLFreeHandle().

To allow you to work with native Windows pointers in the Java context, DllLib represents pointers as integers.

Parameter conversion

DllLib provides several functions for converting Java objects to native memory. The following table describes these functions.

Function

Description

stringToPtrAnsi(String string)

Converts a java.lang.String to a native Ansi buffer

stringToPtrUni(String string)

Converts a java.lang.String to a native Unicode buffer

getPointer(type[] jarray)

Returns a pointer to a native buffer that contains a Java array. The getPointer() function is overloaded. It provides several versions that let you create a native buffer from arrays of int, long, float, and double values. The getPointer() function automatically allocates a buffer that is large enough to receive the data from the Java array.

copy(type[] jarray, int idx, int pmem, int nelems)

Copies data from a Java array to a native buffer. The copy() function is overloaded. It provides several versions that let you copy data to a native buffer from arrays of int, long, float, and double values. You need to allocate a native storage buffer before calling copy().

DllLib also provides functions for converting native objects to Java. The following table describes these functions.

Function

Description

ptrToStringAnsi(int ptr)

Converts a native Ansi buffer to a java.lang.String

ptrToStringUni(int ptr)

Converts a native Unicode buffer to a java.lang.String

ptrToStringBufferAnsi(int ptr, StringBuffer stringbuf)

Converts a native Ansi buffer to a java.lang.StringBuffer

ptrToStringBufferUni(int ptr, StringBuffer stringbuf)

Converts a native Unicode buffer to a java.lang.StringBuffer

copy(int pmem, type[] jarray, int idx, int nelems)

Copies data from a native buffer to a Java array. The copy() function is overloaded. It provides several versions that let you copy data to Java arrays of int, long, float, and double values.

In addition to these functions, DllLib provides functions that let you read and write values in a native memory block.

Structures

To pass a structure as a parameter to a native DLL function, you need to know the structure's layout. In particular, you need to know the offset of each field within the structure. To read values from and write values to the structure, you need to specify the offset of each field you want to access.

To pass a structure, you need to write Java code to perform these operations:

  1. Allocate a block of memory that is sufficiently large to hold the structure by calling allocCoTaskMem() or allocHGlobal().

  2. If the structure is an in parameter, populate each field in the structure by calling one of the DllLib write() functions (write1(), write2(), write4(), or writeN(), which is inherited from AgCom).

  3. Call the function, passing the structure as a parameter.

  4. If the structure is an out parameter, read each field in the structure by calling one of the read() functions available with DllLib (read1(), read2(), read4(), or readN(), which is inherited from AgCom).

Kernel32 class

To simplify the process of calling many of the functions in the Win32 API, SilverStream provides a class called Kernel32 that maps the functions in the kernel32.dll to Java functions. This class removes the need to call the DLLGetHandle() and DLLGetMethod() functions to load the DLL and get a handle to the function.

To use the kernel32 class to call a DLL function, you need to write Java code to perform these operations:

  1. Call the getKernelProc() static method on the Kernel32 class, passing the name of the function in kernel32.dll that you want to call.

    The getKernelProc() method returns a pointer to the function in kernel32.dll.

  2. Perform any setup operations required to pass parameters to the function.

  3. Perform the function call by using the DllLib call() method. You need to pass the pointer returned by getKernelProc() to the call() method.

  4. Process data returned by the function.

Example 1: Making a simple function call   Top of page

The following code makes a call to the GetCurrentProcessID() function in kernel32.dll. For the purpose of illustration, it shows how you would use DLLGetHandle() and DLLGetMethod() to load the DLL and get a handle to the function. In Example 2, you will see how to use the Kernel32 class to call a kernel32.dll function.

The SilverStream function getPID() performs these operations:

  1. Loads kernel32.dll into memory by calling DLLGetHandle().

  2. Gets a pointer to the GetCurrentProcessID() function in kernel32.dll by calling DLLGetMethod().

  3. Performs the function call by using call().

  4. Frees the handle for the DLL by calling DLLFreeHandle().

Here's the code for the getPID() function:

  public int getPID() { 
   //Get our DLL handle
   int hKern = DllLib.DLLGetHandle("kernel32.dll");
   //Get the function pointer
   int pp_getpid =
      DllLib.DLLGetMethod(hKern,"GetCurrentProcessId");
   //DWORD GetCurrentProcessId(VOID)
   int ans = DllLib.call(pp_getpid);
   //Unload DLL
   DllLib.DLLFreeHandle(hKern);
   return ans;
}

Example 2: Calling a function with parameters   Top of page

The following code makes a call to the GetEnvironmentVariableA() function in kernel32.dll. It demonstrates several of the parameter handling services that DllLib provides. In addition, it shows how to use the Kernel32 class to get a handle to a method in a DLL.

The SilverStream function getEnvVar() performs these operations:

  1. Gets a pointer to the GetEnvironmentVariableA() function in kernel32.dll by calling the getKernelProc() static method on Kernel32.

  2. Converts the string value that specifies the name of the environment variable into a native ANSI buffer by calling stringToPtrAnsi().

  3. Allocates space for the environment variable value by calling allocCoTaskMem().

  4. Performs the function call by using the DllLib call() method.

  5. Converts the environment variable value in the buffer to a Java String by calling ptrToStringAnsi().

  6. Frees both native buffers by calling freeCoTaskMem().

Here's the code for the getEnvVar() function:

  public String getEnvVar() { 
   int pp_getenv =
      Kernel32.getKernelProc("GetEnvironmentVariableA");
   int namebuf = DllLib.stringToPtrAnsi("Path");
   int valbuf = DllLib.allocCoTaskMem(4096);
   DllLib.call(pp_getenv, namebuf, valbuf, 4096);
   String value = DllLib.ptrToStringAnsi(valbuf);
   DllLib.freeCoTaskMem(valbuf);
   DllLib.freeCoTaskMem(namebuf);
   return value;
}






Copyright © 2000, SilverStream Software, Inc. All rights reserved.