Interfacing of VC++, Java and Assembly
A few weeks ago I worked on a project in which java is involved. It is nice experience to integrate VC++, Java and assembly together
to do something. So I decide to rewrite my C# article in which I call Assembly language from C# with the help
of VC and now do the same thing with the help of VC++, Java and Assembly by using JNI (Java Native Interface).
But the basic question is same, why should we call Assembly Language from Java? There might be some reason for this:
-
You are going to do some system level work whose support is not available in Java, like calling assembly language.
-
Some code has already been written and you just want to use it in your java application.
So you will use JNI (Java Native Interface) to execute Native code from java. But be careful
when you plan to call native functions from your java program then your program will not be portable.
To call native code from java you have to follow these steps.
-
Write Java Program in which you declare native methods, which you are going to call in your Program.
-
Compile Java program to made class file.
-
Create C++ header file from Java class file using javah utility to use it in C++ Program.
-
Write C++ Program i.e. DLL which use that header file.
Now there is a question, how can we use our existing DLL into java program, because those DLLs are not written to
use java created header file? The solution of this problem is to create a wrapper DLL that just call the functions of your DLL.
Let's discuss these steps one by one.
To declare any function native in java you use native keyword with that function and done declare body of that function.
Program 1
class prog1
{
public static native void test();
public static void main(String args[])
{
System.out.println("Hello World!");
}
}
It is not necessary to declare native function static; you can declare it as a non-static function too. The
only difference comes between static native function and non-static native function comes when we call the function.
So we will the difference of these when call the function. Now compile this program at command line
Javac prog1.java
The output of this program is class file. And this is working program you can run it if you want by typing
Java prog1
Now second step is to generate header file for C/C++. There is a utility javah
comes with java which create the header file for C/C++ from the java class file.
Type this at command prompt
Javah prog1
This will create one header file with the same name as class file name i.e. prog1.h. This header file is very simple
and declares the prototype of all the functions, which are, declares native in the java program. The header file looks like this
Prog1.h
#include <jni.h>
#ifndef _Included_prog1
#define _Included_prog1
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_prog1_test
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
You should include at least one header file in you JNI based dll i.e. jni.h.
This header file is automatically included when you create heave file from javah
utility. JNIEXPORT
is define as __declspec(dllexport)
and JNICALL
is standard calling convention define is jni_md.h.
#define JNIEXPORT __declspec(dllexport)
#define JNICALL __stdcall
So the functions name is something like this
__declspec(dllexport) void stdcall Java_prog1_test(JNIEnv*, jclass)
The function name begins with the Java followed by the package name then class name and at last the
function name, which is declared as native in java file. Here we haven't defined any function so there
is no package name in exported function. The first parameter of any JNI based function is pointer to
JNIEnv structure. This structure is used java environment related function within the C++ Program,
such as the storing of string is different in Java and C++ so you have to convert it into appropriate
type before use it by calling the function define by JNI.
Now made on DLL Project in VC and include this header file in the project. And give the implementation to
those native functions. Remember you can use any name for this cpp file it is not necessary to use the
same name which is the class file name or header file name.
prog1.cpp
#include <windows.h>
#include <stdio.h>
#include "prog1.h"
BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
JNIEXPORT void JNICALL Java_prog1_test(JNIEnv *, jclass)
{
printf("Hello world from VC++ DLL\n");
}
You will get the required DLL file after compile this project. Now the next step is to use this DLL
and its function in java program. The API to load DLL is loadLibarary. Now the programs comes something
like this
Program 2
class prog1
{
static
{
System.loadLibrary("test.dll");
}
public static native void test();
public static void main(String args[])
{
System.out.println("Hello World!");
test();
}
}
When you run this program, then program crash after throwing an exception
UnsatisfiedLinkError
. So lets catch this exception and change our
program little bit.
Program 3
class prog1
{
static
{
try
{
System.loadLibrary("test.dll");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public static native void test();
public static void main(String args[])
{
System.out.println("Hello World!");
test();
}
}
The output of this program is
java.lang.UnsatisfiedLinkError: no test.dll in java.library.path
Hello World!
Exception in thread "main" java.lang.UnsatisfiedLinkError: test
at prog1.test(Native Method)
at prog1.main(prog1.java:19)
This program shows that it can't find the DLL, which we just made. Be sure to
copy the DLL in the path or the current folder. But you will get the same error
even you copy the DLL in the same folder. The reason is that you don't have to
write the extension of the DLL in the loadLibrary function.
Program 4
class prog1
{
static
{
try
{
System.loadLibrary("test");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public static native void test();
public static void main(String args[])
{
System.out.println("Hello World!");
test();
}
}
The output of this program is
Hello World!
Hello world from VC++ DLL
Now come to non-static native function. In fact you can declare native function to non-static too. But in this case you have to create instance of prog1 class and call the native function.
Program 5
class prog1
{
static
{
try
{
System.loadLibrary("test");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public native void test();
public static void main(String args[])
{
System.out.println("Hello World!");
new prog1().test();
}
}
The only difference of the program 4 and 5 are the declaration and calling of native function. The output of the
above program is same as previous one. But I will use the static native function in rest of the article.
Ok let's try to do something with passing parameter and return something to native function. Let's make a program
to call one native function which sum two numbers and return the results.
Program 6
class prog1
{
static
{
try
{
System.loadLibrary("test");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public static native int Sum(int a, int b);
public static void main(String args[])
{
System.out.println(Sum(5, 10));
}
}
And here is CPP file to create DLL
Program 7
#include <windows.h>
#include "prog1.h"
BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
JNIEXPORT jint JNICALL Java_prog1_Sum(JNIEnv *, jclass, jint a, jint b)
{
return a + b;
}
The output of this program is:
15
Handling integers is easy. Now let's do some experiments with character stream. Let's make a native
function which takes a string as a parameter and return it after capitalize it.
Program 8
class prog1
{
static
{
try
{
System.loadLibrary("test");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public static native String saySomething(String strString);
public static void main(String args[])
{
System.out.println(saySomething("Hello world"));
System.out.println(saySomething("Bye world"));
}
}
And here is the CPP program to use this
Program 9
#include <windows.h>
#include <string.h>
#include "prog1.h"
BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
JNIEXPORT jstring JNICALL Java_prog1_saySomething(JNIEnv * env, jclass, jstring strString)
{
char *lpBuff = (char*)env->GetStringUTFChars(strString, 0);
_strupr(lpBuff);
jstring jstr = env->NewStringUTF(lpBuff);
env->ReleaseStringUTFChars(strString, lpBuff);
return jstr;
}
The output of the program is
HELLO WORLD
BYE WORLD
The important thing in this program is the use of GetStringUTFChars
,
ReleaseStringUTFChars
and NewStringUTF
functions.
GetStringUTFChars
convert the character representation from Java
Unicode representation to C language Null terminated string. You have to call
ReleaseStringUTFChars
to free the memory allocated by virtual machine
allocated. If you forgot to call this then it will create memory leak. NewStringUTF
is used to create new string which is return by the function.
Now we have enough knowledge to make C# program into Java. Let's make Java Program to create header file.
Program 10
class sysInfo
{
static
{
try
{
System.loadLibrary("SysInfo");
}
catch(UnsatisfiedLinkError ule)
{
System.out.println(ule);
}
}
public static native int getCPUSpeed();
public static native String getCPUType();
public static native int getCPUFamily();
public static native int getCPUModal();
public static native int getCPUStepping();
public static void main(String args[])
{
System.out.println("Information about System");
System.out.println("========================");
System.out.println("Get CPU Speed: " + getCPUSpeed());
System.out.println("Get CPU Type: " + getCPUType());
System.out.println("Get CPU Family: " + getCPUFamily());
System.out.println("Get CPU Modal: " + getCPUModal());
System.out.println("Get CPU Stepping: " + getCPUStepping());
}
}
And here is the CPP program which use the header file created by this program.
Program 11
#include <windows.h>
#include "sysinfo.h"
BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
JNIEXPORT jint JNICALL Java_sysInfo_getCPUSpeed(JNIEnv *, jclass)
{
LARGE_INTEGER ulFreq, ulTicks, ulValue, ulStartCounter, ulEAX_EDX, ulResult;
QueryPerformanceFrequency(&ulFreq);
QueryPerformanceCounter(&ulTicks);
ulValue.QuadPart = ulTicks.QuadPart + ulFreq.QuadPart;
_asm
{
rdtsc
mov ulEAX_EDX.LowPart, EAX
mov ulEAX_EDX.HighPart, EDX
}
ulStartCounter.QuadPart = ulEAX_EDX.QuadPart;
do
{
QueryPerformanceCounter(&ulTicks);
} while (ulTicks.QuadPart <= ulValue.QuadPart);
_asm
{
rdtsc
mov ulEAX_EDX.LowPart, EAX
mov ulEAX_EDX.HighPart, EDX
}
ulResult.QuadPart = ulEAX_EDX.QuadPart - ulStartCounter.QuadPart;
return (int)ulResult.QuadPart / 1000000;
}
JNIEXPORT jstring JNICALL Java_sysInfo_getCPUType(JNIEnv * env, jclass)
{
static char pszCPUType[13];
memset(pszCPUType, 0, 13);
_asm
{
mov eax, 0
cpuid
mov pszCPUType[0], bl
mov pszCPUType[1], bh
ror ebx, 16
mov pszCPUType[2], bl
mov pszCPUType[3], bh
mov pszCPUType[4], dl
mov pszCPUType[5], dh
ror edx, 16
mov pszCPUType[6], dl
mov pszCPUType[7], dh
mov pszCPUType[8], cl
mov pszCPUType[9], ch
ror ecx, 16
mov pszCPUType[10], cl
mov pszCPUType[11], ch
}
pszCPUType[12] = '\0';
return env->NewStringUTF(pszCPUType);
}
JNIEXPORT jint JNICALL Java_sysInfo_getCPUFamily(JNIEnv *, jclass)
{
int retVal;
_asm
{
mov eax, 1
cpuid
mov retVal, eax
}
return (retVal >> 8);
}
JNIEXPORT jint JNICALL Java_sysInfo_getCPUModal(JNIEnv *, jclass)
{
int retVal;
_asm
{
mov eax, 1
cpuid
mov retVal, eax
}
return ((retVal >> 4 ) & 0x0000000f);
}
JNIEXPORT jint JNICALL Java_sysInfo_getCPUStepping(JNIEnv *, jclass)
{
int retVal;
_asm
{
mov eax, 1
cpuid
mov retVal, eax
}
return (retVal & 0x0000000f);
}
The output of this program on my computer is given below, you may get different result
depending on the computer which you are using. There is one more thing I use CPUID instruction
to identify the vendor name of the microprocessor and this instruction is available only on Pentium
and above microprocessors, so if you are using microprocessor less then Pentium then you may get unpredictable results.
Information about System
========================
Get CPU Speed: 1003
Get CPU Type: AuthenticAMD
Get CPU Family: 6
Get CPU Modal: 4
Get CPU Stepping: 2
Acknowledgments
Thanks to Tasnim Ahmed, Java team leader at SoftPakSys to answer my questions related to java,
Khuram Rehmani give me some tips on usage of JNI and Muhammad Kashif Shafiq to review this before publication.