Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Integrating Visual C++, Java and Assembly

0.00/5 (No votes)
10 Dec 2002 1  
This article shows a way of integrating Java, C++ and Assembler

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

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>

/* Header for class prog1 */

#ifndef _Included_prog1
#define _Included_prog1
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     prog1
 * Method:    test
 * Signature: ()V
 */
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;

	// it is number of ticks per seconds

	QueryPerformanceFrequency(&ulFreq);
	// current valueofthe performance counter

	
	QueryPerformanceCounter(&ulTicks);
	// calculate one second interval

	ulValue.QuadPart = ulTicks.QuadPart + ulFreq.QuadPart;
	// read time stamp counter

	// this asm instruction load the highorder 32 bit of the register into EDX

	// and the lower order 32 bits into EAX

	_asm 
	{
		rdtsc
			mov ulEAX_EDX.LowPart, EAX
			mov ulEAX_EDX.HighPart, EDX
	}
	
	// start no of ticks

	ulStartCounter.QuadPart = ulEAX_EDX.QuadPart;
	// loop for 1 second

	do 
	{
		QueryPerformanceCounter(&ulTicks);
	} while (ulTicks.QuadPart <= ulValue.QuadPart);
	
	// get the actual no of ticks

	_asm 
	{
		rdtsc
			mov ulEAX_EDX.LowPart, EAX
			mov ulEAX_EDX.HighPart, EDX
	}
	
	// calculate result

	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

		// getting information from EBX

		mov pszCPUType[0], bl
		mov pszCPUType[1], bh
		ror ebx, 16
		mov pszCPUType[2], bl
		mov pszCPUType[3], bh

		// getting information from EDX

		mov pszCPUType[4], dl
		mov pszCPUType[5], dh
		ror edx, 16
		mov pszCPUType[6], dl
		mov pszCPUType[7], dh

		// getting information from ECX

		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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here