Interfacing VC++, Java, and Assembly

Environment:

A few weeks ago I worked on a project in which Java is involved. It was a nice experience to integrate VC++, Java, and Assembly together to do something. So, I decided to rewrite my C# article in which I call the 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 the 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, as in 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. When you do, your program will not be portable.

To call native code from Java, you have to follow these steps:

  • Write a Java program in which you declare native methods, which you are going to call in your program.
  • Compile a Java program to a made class file.
  • Create a C++ header file from the Java class file using javah utility to use it in a C++ program.
  • Write a C++ Program; in other words, a DLL that uses that header file.

Now there is a question. How can we use our existing DLL in a Java program because those DLLs are not written to use a Java-created header file? The solution to this problem is to create a wrapper DLL that just calls your DLL's functions.

Let's discuss these steps one by one.

To declare any function native in Java, you use the native keyword with that function and declare the 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 the native function as static; you can declare it as a non-static function, too. The only difference between a static native function and a non-static native function comes when we call the function. So we will determine the difference of these when call the function. Now, compile this program at the command line.

Javac prog1.java

The output of this program is a class file. This is a working program; you can run it if you want by typing:

Java prog1

Now, the second step is to generate a header file for C/C++. There is a utility, javah, that comes with Java. It creates the header file for C/C++ from the Java class file. Type this at the command prompt:

Javah prog1

This will create one header file with the same name as the class file name—prog1.h. This header file is very simple and declares the prototype of all the functions, which are declared 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—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 a standard calling convention, defined as jni_md.h.

#define JNIEXPORT __declspec(dllexport)
#define JNICALL __stdcall

So, the function's 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 the 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 a pointer to JNIEnv structure. This structure is used in the Java environment related function within the C++ program, such as the storing of strings is different in Java and C++ so you have to convert it into the appropriate type before using it by calling the function defined by JNI.

Now, make a DLL project in VC and include this header file in the project. And give the implementation to those native functions. Remember that 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 compiling this project. Now, the next step is to use this DLL and its function in a Java program. The API to load the DLL is loadLibarary. Now, the program becomes 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, it crashes after throwing an exception, UnsatisfiedLinkError. So, let's catch this exception and change our program a 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 to the path or the current folder. But, you will get the same error, even if 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, we come to the non-static native function. In fact, you can declare a native function to non-static, too. But, in this case, you have to create an instance of the 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 differences between Programs 4 and 5 are the declaration and calling of native functions. The output of the above program is same as previous one. But, I will use the static native function in the rest of the article.

Okay. let's try to do something with passing parameters and returning something to a native function. Let's make a program to call one native function that sums two numbers and returns 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 the 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 integer is easy. Now, let's do some experimenting with the character stream. Let's make a native function that takes a string as a parameter and returns it after capitalizing 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 the GetStringUTFChars, ReleaseStringUTFChars, and NewStringUTF functions. GetStringUTFChars converts the character representation from Java Unicode representation to a 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, it will create a memory leak. NewStringUTF is used to create a new string that is returned by the function.

Now we have enough knowledge to turn a C# program into Java. Let's make a Java program to create a 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 that uses 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 value of the 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 number of ticks
  ulStartCounter.QuadPart = ulEAX_EDX.QuadPart;
  // loop for 1 second
  do
  {
    QueryPerformanceCounter(&ulTicks);
  } while (ulTicks.QuadPart <= ulValue.QuadPart);

  // get the actual number 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 a different result depending on the computer that you are using. There is one more thing: I use the CPUID instruction to identify the vendor name of the microprocessor. This instruction is available only on Pentium and above microprocessors; so, if you are using a microprocessor less then a Pentium, 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

Acknowledgment

Thanks to Tasnim Ahmed, Java team leader at SoftPakSys, for answering my questions related to Java; Khuram Rehmani, who gave me some tips on the usage of JNI; and Muhammad Kashif Shafiq for reviewing this before publication.

Downloads

Download source - 4 Kb


About the Author

Zeeshan Amjad

C++ Developer at Bechtel Corporation. zamjad.wordpress.com

Comments

  • dynamic context

    Posted by Legacy on 05/14/2003 12:00am

    Originally posted by: ynic

    Good stuff, but you always call the native function from a static context,
    When I try to call a native function from a class which is created dynamically, I allways have an "java.lang.UnsatisfiedLinkError": not when I load the library, but when I call the native function.

    Regards,

    ynic

    Reply
  • how to call a java class from c++ code

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: Ruilin Yang

    I need to call a java class to do RMI multi-casting to communicate with mutiple client machine.

    I do not know how to call the Java classes to implement the task.

    Please help.

    Ruilin

    Reply
  • Nice job

    Posted by Legacy on 12/27/2002 12:00am

    Originally posted by: Mahatir

    nice job
    but better work on your english!

    Reply
  • Good Examples

    Posted by Legacy on 12/18/2002 12:00am

    Originally posted by: Afzal Shaheedi

    Your examples are excellent. Thanks

    Reply
  • UIT

    Posted by Legacy on 12/14/2002 12:00am

    Originally posted by: UIT

    Sir Nice work!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds