Sunday, June 6, 2010

Java Native Interface

The Java Native Interface (JNI) is a programming framework that allows Java code running in a Java Virtual Machine (JVM) to call and to be called by native applications and libraries written in other languages, such as C, C++. (Wikipedia)

In this example we will be using win32 apis like FindWindow, SetForegroundWindow, GetWindowText, GetWindow, IsWindowVisible & GetDesktopWindow in a Java application using JNI.




The first step in using JNI is to create a java file that will call our native methods.

package jniexample;
/**
 *
 * @author Naveed Quadri
 */
public class NativeWin32 {

        public  static final int GW_HWNDFIRST = 0;
        public  static final int GW_HWNDLAST  = 1;
        public  static final int GW_HWNDNEXT  = 2;
        public  static final int GW_HWNDPREV  = 3;
        public  static final int GW_OWNER     = 4;
        public  static final int GW_CHILD     = 5;
    static {
        System.loadLibrary("NativeWin32");
    }
    public static native long jniFindWindow(String windowTitle);

    public static native boolean jniSetForegroundWindow(long hWnd);

    public static native String jniGetWindowText(long hWnd);

    public static native long jniGetWindow(long hWnd, int cmd);

    public static native boolean jniIsWindowVisible(long hWnd);

    public static native long jniGetDesktopWindow();
}


The first thing to note is the keyword 'native'. This tells the compiler that this method will be accessed from an external library.

The second thing to note is this code snippet. This loads the dll file, NativeWin32.dll

static 
{
 System.loadLibrary("NativeWin32");
}


Now lets generate the header the file.

//This compiles the java file
javac jniexample/NativeWin32.java

//This generates the jni header file
javah -jni jniexample.NativeWin32

The function names in the header file should be in the order of

Java_<packageName>_<className>_<functionName>

Now lets implement this header file. In Visual Studio select a win32 dll project. The IDE provides you with with example implementations of exported variable, function and a constructor which we can get rid of.


JNIEXPORT jlong JNICALL Java_jniexample_NativeWin32_jniFindWindow
  (JNIEnv * env, jclass jobj, jstring windowName)
 {
  HWND iHwnd;
  const char *lpWindowName = env->GetStringUTFChars(windowName,0); 
  iHwnd = FindWindow(NULL, (LPCSTR)lpWindowName);
  return (jlong)iHwnd;
 }

 JNIEXPORT jboolean JNICALL Java_jniexample_NativeWin32_jniSetForegroundWindow
  (JNIEnv *, jclass, jlong jniHwnd)
 {
  jboolean foregroundWindowSet = false;
  if(SetForegroundWindow((HWND)jniHwnd) != 0)
  {
   foregroundWindowSet = true;
  }
  return foregroundWindowSet;
 }

 JNIEXPORT jstring JNICALL Java_jniexample_NativeWin32_jniGetWindowText
  (JNIEnv * env, jclass, jlong jniHwnd)
 {
  PSTR pszMem;
  int cTxtLen = GetWindowTextLength((HWND)jniHwnd);
  
  // Allocate memory for the string and copy 
  // the string into the memory. 
  pszMem = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (cTxtLen + 1), MEM_COMMIT, PAGE_READWRITE); 
  GetWindowText((HWND)jniHwnd, pszMem, cTxtLen + 1);
  jstring jstrWindowText = env->NewStringUTF((char *)pszMem);
  VirtualFree(pszMem, 0, MEM_RELEASE); 
  return jstrWindowText;
 }

 JNIEXPORT jlong JNICALL Java_jniexample_NativeWin32_jniGetWindow
  (JNIEnv *, jclass, jlong jniHwnd, jint jniCmd)
 {
  HWND iHwnd;
  iHwnd = GetWindow((HWND)jniHwnd,(int)jniCmd);
  return (jlong)iHwnd;
 }

 JNIEXPORT jboolean JNICALL Java_jniexample_NativeWin32_jniIsWindowVisible
  (JNIEnv *, jclass, jlong jniHwnd)
 {
  jboolean windowVisible = false;
  if(IsWindowVisible((HWND)jniHwnd) != 0)
  {
   windowVisible = true;
  }
  return windowVisible;
 }
 JNIEXPORT jlong JNICALL Java_jniexample_NativeWin32_jniGetDesktopWindow
  (JNIEnv *, jclass)
 {
  HWND iHwnd;
  iHwnd = GetDesktopWindow();
  return (jlong)iHwnd;
 }


Compile the project and copy the dll to your java project. Now the native functions can be invoked as normal functions

//Gets the handle to the desktop.
long handle = NativeWin32.jniGetDesktopWindow();

You can find the complete source code to the dll here & the java project with compiled binary can be found here.

1 comment: