 |
Level: Introductory
David Wendt (wendt@us.ibm.com),
WebSphere programmer, IBM
01 May 1999
This report presents information and examples for handling national
language strings with the Java Native Interface (JNI) for 32-bit Windows
platforms.
The examples is this paper use the Java Development Kit (JDK) version 1.1.6
and the Java
Native Interface (JNI) Specification created by Sun Microsystems. The native
code written in C is compiled and built with the Microsoft Visual C++
compiler.
Introduction
Java applications are intended to be national language independent, just as
they are intended to be operating system independent. If native methods are
required on Windows, the Java application which implements the JNI methods will
require special code when handling strings. Strings in Java are managed in
Unicode. In Windows, strings are displayed and inputted in the current code
page. Therefore, conversions are necessary if user input or output is required
in a native method.
There are two JNI APIs to access Java strings from Windows native
methods:
- GetStringUTFChars
- GetStringChars
The UTF format is defined by Java and matches the first 127 characters of the
ASCII table. The UTF methods cannot generally be used because other national
language characters in the UTF format cannot be understood by Windows APIs.
However, these methods are very convenient for simple strings, such as debug
messages, where a user is not required to convert Java strings directly to and
from the C-style char* type.
The non-UTF (Unicode) JNI methods can be used but require special Windows
APIs to convert them for display. The data produced by the Unicode methods is
called wide-char or wchar* in Windows. The following Windows APIs are
used to convert these strings:
- WideCharToMultiByte
- MultiByteToWideChar
This paper will develop simple examples showing how these methods are
used.
Java String in Windows
In this section, we will create a test Java class with a native method
displaying string arguments in a Windows message box. There is no reason for
this function to be a native method, but it provides a simple example to
illustrate how Java strings are displayed.
import java.awt.*;
import java.awt.event.*;
public class StringTest extends Frame implements ActionListener
{
private TextField input = new TextField("",20);
private Button sendButton = new Button( "Send" );
public StringTest()
{
super("String Test");
setLayout( new FlowLayout() );
add( input );
add( sendButton );
sendButton.addActionListener( this );
}
public void actionPerformed( ActionEvent evt )
{
if( evt.getSource()==sendButton )
{
String s = input.getText();
showParms( s, s );
}
}
public void showParms( String s1, String s2 )
{
showParms0( s1, s2 );
}
public native void showParms0( String s1, String s2 );
static
{
System.loadLibrary( "StringTest" );
}
public static void main( String args[] )
{
StringTest frame = new StringTest();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
frame.setSize(180, 200);
frame.setVisible(true);
}
} |
For more information on JNI see the JNI
Specification available from Sun Microsystems. The example above is the
complete test program including the native method, showParms0. This
method will receive two strings which it will display in a Windows message
box.
The native method is implemented in C in file, StringTest.c:
#include <windows.h>
#include <stdio.h>
#include "StringTest.h"
char* jstringToWindows( JNIEnv* env, jstring jstr );
JNIEXPORT void JNICALL Java_StringTest_showParms0
(JNIEnv *env, jobject obj, jstring s1, jstring s2)
{
const char* szStr1 = (*env)->GetStringUTFChars( env, s1, 0 );
const char* szStr2 = jstringToWindows( env, s2 );
MessageBox( HWND_DESKTOP, (LPCSTR)szStr1, (LPCSTR)"String1", 0 );
MessageBox( HWND_DESKTOP, (LPCSTR)szStr2, (LPCSTR)"String2", 0 );
(*env)->ReleaseStringUTFChars( env, s1, szStr1 );
}
char* jstringToWindows( JNIEnv* env, jstring jstr )
{
int length = (*env)->GetStringLength( env, jstr );
const jchar* jcstr = (*env)->GetStringChars( env, jstr, 0 );
char* rtn = (char*)malloc( length*2+1 );
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,
(length*2+1), NULL, NULL );
if( size <= 0 )
return NULL;
(*env)->ReleaseStringChars( env, jstr, jcstr );
rtn[size] = 0;
return rtn;
} |
The JNI API, GetStringUTFChars is used to create a C string from
the Java string or jstring argument. The GetStringChars method
creates a jchar array (Unicode characters) from the Java string.
The StringTest.dll is created by compiling the C source. The following
compile statement uses the Microsoft Visual C++ compiler:
cl -Ic:\jdk1.1.6\include -Ic:\jdk1.1.6\include\win32 -LD StringTest.c
-FeStringTest.dll user32.lib |
where c:\jdk1.1.6 is the path for the installed JDK.
The showParms0 function is passed the same string on both parameters.
The reason for this is to show the difference between using the UTF methods and
using the Unicode methods for converting strings. The class is run using the
Java interpreter and the output is as follows:
java StringTest
This will show a window with an entry field, a pushbutton, and a listbox. As
a test, some non-English national language characters are entered along with the
word HELLO as follows:

Clicking "Send" calls the showParms0 native method which shows two
message boxes.

The string characters from the message box were converted using the UTF
method. Only the HELLO characters are correct. The second message box displays
strings from the Unicode methods.

All characters are displayed correctly.
Windows string in Java
This example will accept command line parameters and display them in the Label of the Frame of the Java application. The Java code is
written to show the argument strings:
import java.awt.*;
import java.awt.event.*;
public class MyApp extends Frame
{
public MyApp( String[] msgs )
{
super( "My Java App" );
Label label = new Label();
String labelMsg = new String();
for( int i=0; i < msgs.length; i++ )
labelMsg += msgs[i] + " ";
label.setText( labelMsg );
add( label );
}
public static void main( String[] args )
{
MyApp frame = new MyApp( args );
frame.addWindowListener( new WindowAdapter()
{
public void windowClosing(WindowEvent e) {System.exit(0);}
});
frame.setSize(200,200);
frame.setVisible(true);
}
} |
The application can be executed using the java.exe interpreter:
java MyApp First second last
which shows the frame window with the string parameters:

Passing parameters to the Java class main from C requires creating a string
array from the parameters and passing the array to CallStaticVoidMethod . The following code will launch a Java
application from a Windows .EXE and passes command line parameters to the Java
program.
#include <windows.h>
#include <stdio.h>
#include <jni.h>
#define MAIN_CLASS "MyApp"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
JNIEnv* env;
JavaVM* jvm;
JDK1_1InitArgs vmargs;
jint rc;
jclass cls;
jmethodID mainId;
/* get CLASSPATH environment variable setting */
char* szClasspath = getenv( "CLASSPATH" );
vmargs.version = 0x00010001; /* version 1.1 */
JNI_GetDefaultJavaVMInitArgs( &vmargs ); /* init vmargs */
/* the classpath returned by JNI_GetDefaultJavaVMInitArgs is wrong */
vmargs.classpath = szClasspath;
rc = JNI_CreateJavaVM( &jvm, &env, &vmargs ); /* create JVM */
if( rc < 0 )
return 1;
/* load the class containing the static main() method */
cls = (*env)->FindClass( env, MAIN_CLASS );
if( cls == 0 )
return 1;
/* find the main() method */
mainId = (*env)->GetStaticMethodID(env, cls, "main",
"([Ljava/lang/String;)V");
if( mainId == 0 )
return 1; /* error */
/* setup the parameters to pass to main() */
{
jstring str;
jobjectArray args;
int i=0;
args = (*env)->NewObjectArray(env, __argc-1,
(*env)->FindClass(env, "java/lang/String"), 0);
for( i=1; i<__argc; i++ )
{
str = (*env)->NewStringUTF( env, __argv[i] );
(*env)->SetObjectArrayElement(env, args, i-1, str);
}
(*env)->CallStaticVoidMethod(env, cls, mainId, args); /* call main() */
}
(*jvm)->DestroyJavaVM( jvm ); /* kill JVM */
return 0;
} |
The __argc and __argv variables are convenient for accessing
the command line parameters in a WinMain .EXE and are equivalent to argc and argv in a regular main .EXE.
The MyApp.exe is built using the following command line for Microsoft Visual
C++:
cl -Ic:\jdk1.1.6\include -Ic:\jdk1.1.6\include\win32 -MT MyApp.c c:\jdk1.1.6\lib\javai.lib
c:\jdk1.1.6 is where the JDK is installed. If MyApp.exe is executed from a
Windows command line, any parameters are displayed in the Java window.
In the methods presented above, the UTF strings methods are used to access
and create Java strings. These methods cannot be used for other national
language character sets like the double-byte character set (DBCS) or other
national language characters. For example, if the previous example's MyApp.exe
is executed from the command line as follows:
MyApp d?os?enregistr?
The following is shown in the GUI:

The accented e's are not shown correctly because they are above 127 in the
ASCII table. Using Unicode along with the appropriate JNI and Windows APIs will
correct this problem.
Java strings can be converted to and from Unicode character arrays using the
appropriate JNI APIs. Once the Java Strings are obtained in Unicode, the API MultiByteToWideChar converts a Windows string to a Unicode
character array. The following function can be used to create a Java String from
a multi-byte string.
jstring WindowsTojstring( JNIEnv* env, char* str )
{
jstring rtn = 0;
int slen = strlen(str);
wchar_t* buffer = 0;
if( slen == 0 )
rtn = (*env)->NewStringUTF( env, str ); //UTF ok since empty string
else
{
int length =
MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
buffer = malloc( length*2 + 1 );
if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen,
(LPWSTR)buffer, length ) >0 )
rtn = (*env)->NewString( env, (jchar*)buffer, length );
}
if( buffer )
free( buffer );
return rtn;
} |
In the above function, MultiByteToWideChar is called twice. The
first call determines the number of Unicode characters, which is then used to
compute the size of the memory allocation to hold the character array. The
second call performs the conversion into that array. The Java string is created
from the array by calling the NewString method. Now the function
will be used to correct the problem of displaying the wrong characters
introduced in the example. The above WindowsTojstring function is
added to the C code and the following line is changed from:
str = (*env)->NewStringUTF( env, __argv[i] );
to:
str = Windows2jstring( env, __argv[i] );
A recompile and re-execute now shows the parameters correctly.

Summary
This paper presented examples for implementing native methods dealing with
conversion of strings for an NLS-enabled Java program environment. Strings are
maintained in Unicode in Java and must be converted appropriately using the
following JNI APIs:
- NewString
- GetStringChars
- ReleaseStringChars
- GetStringLength
and the following Windows APIs:
- MultiByteToWideChar
- WideCharToMultiByte
See the Java Native
Interface Specification available from Sun Microsystems for more information
on JNI.
About the author
 |
|
 |
David Wendt is an IBM Programmer for WebSphere Studio in Research Triangle
Park, NC. He can be reached at
wendt@us.ibm.com. |
Rate this
page
|
 |