JNI示例
2007年05月27日 星期日 16:15
一、JNI简介
JNI:Java Native Interface,是Java语言提供的一种通用接口,用于Java代码与本地化代码的交互。所谓本地化代码是指直接编译成的与机器相关的二进制代码,而非Java字节码之类的中间代码。Windows下面的可执行文件,DLL等,Linux下面的可执行文件和SO文件等,都是二进制代码。 JNI允许Java语言编写的程序与其他语言编写的程序库(DLL, SO)或可执行文件进行互操作,包括汇编、C、C++。JNI产生的原因在于以下几种需求: (1). 你的应用程序需要使用系统相关的功能,而Java代码不支持或是难以办到。 (2). 已有其他语言写好的类库或程序,希望Java程序可以使用它们。 (3). 出于更高的性能要求,希望使用汇编或是C/C++语言来实现部分功能。 下图出自JNI Tutorial,展示了JNI的地位: ![]() 其他的理论的东西就不多说了,JNI Tutorial讲得很清楚。强烈建议阅读。 二、JNI的开发步骤 这里以使用C++编写本地化方法实现为例,开发一个使用JNI的Demo程序,具体步骤如下所示: (1). 编写带有native方法的java类 (2). 使用javac命令编译所编写的java类 (3). 使用javah命令处理类文件,生成C/C++头文件 (4). 使用C/C++实现本地方法 (5). 将C/C++编写的文件生成动态连接库 三、Demo程序,Demo程序宣示了Java代码中调用C++的输出功能打印字符串,同时演示了使用C++的输入功能读取字符串,并使用System.out.println输出。 1. 编写带native方法的Java类 本示例中的源程序就一个Java类。如下所示: // HelloJNI.java -- 简单的JNI入门示例。
说明:一共3个native方法,第一个简单的Hello,第二个使用<ctime>头文件中的相关函数计算当前时间,第三个读取输入。注意static语句块: static { System.loadLibrary("hello"); } 在JVM载入HelloJNI类的时候加载动态库,System.loadLibrary()中的参数是我们要生成的动态库文件的名字,不包括扩展名,在Windows下面是hello.dll,Linux下面是hello.so,这个由Java自动识别。 2. 编译此类 3. javah HelloJNI 生成C/C++头文件,生成的头文件不用任何修改。如下所示: /* DO NOT EDIT THIS FILE - it is machine generated */
4. 编写C/C++实现文件,如下所示: // HelloJNIImpl.cpp -- 自己编写的实现文件
说明:JNI Tutorial中使用的居然是像下面这样的代码,env指针的使用应该有误,敬请注意。 JNIEXPORT jstring JNICALL 5. 生成本地库文件。笔者使用的VS.NET 2003中的C++编译器。VS.NET 2003命令行工具生成DLL的命令: cl -I<头文件目录1> -I<头文件目录2> -LD <C/C++源文件名> -Fe<生成的DLL文件名> -I选项指定头文件目录,-LD指定C++源文件,-Fe指定生成的DLL文件名。将各个部分替换成实际的情况,实际使用的命令如下: cl -IC:/java/jdk1.5.0_10/include -IC:/java/jdk1.5.0_10/include/win32 -LD HelloJNIImpl.cpp -Fehello.dll 注:cl可以增加-EHsc参数来减少生成过程中的输出信息。如下图所示: 6. 运行结果,如下图所示:
7. 自动化。其实步骤都很简单,所以可以使用Ant来完成。下面提供一个ANT Buildfile。使用之前请根据自己的实际情况修改相关属性。
注意:Builfile编码是UTF-8的。 build.xml <?xml version="1.0" encoding="UTF-8"?>
Done!^_^!
jni调用的c++作的嵌入了一个浏览器:)
把代码发上来大家分享 ------------------------------------------------------ //MyWindow.java import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.awt.peer.*; import sun.awt.*; public class MyWindow extends Canvas { static { // Load the library that contains the JNI code. System.loadLibrary("MyWindow"); } // native entry point for initializing the IE control. public native static void initialize(int hwnd, String strURL); // native entry point for resizing public native static void resizeControl(int hwnd, int nWidth, int nHeight); public native static int getNativeWindowHandle(Canvas canvas); public void addNotify() { super.addNotify(); m_hWnd = getNativeWindowHandle(this); initialize(m_hWnd, m_strURL); } String m_strURL = "http://www.163.com"; int m_hWnd = 0; public static void main( String[] argv ) { Frame f = new Frame(); f.setLayout(new BorderLayout()); f.setTitle("Internet Explorer inside Java Canvas"); MyWindow w = new MyWindow(); if(argv.length>0) w.m_strURL = argv[0]; String strText = "URL:" + w.m_strURL; f.add(w,BorderLayout.CENTER); f.add(new Label(strText),BorderLayout.NORTH); f.setBounds(300,300,500,300); f.setVisible(true); } public void setSize( int width, int height ) { super.setSize(width,height); if(m_hWnd!=0) resizeControl(m_hWnd, width, height); } public void setSize( Dimension d ) { super.setSize(d); if(m_hWnd!=0) resizeControl(m_hWnd, d.width, d.height); } public void setBounds( int x, int y, int width, int height ) { super.setBounds(x,y,width,height); if(m_hWnd!=0) resizeControl(m_hWnd, width, height); } public void setBounds( Rectangle r ) { super.setBounds(r); if(m_hWnd!=0) resizeControl(m_hWnd, r.width, r.height); } }
将上面的文件编译得到MyWindow.class 再用javah MyWindow命令得到MyWindow.h头文件(是给c++用的) 剩下的事交给vc了 小嵩的vc是vc6.0中文版 建立一个dll的工程 文件-〉新建-〉Win32 Dynamic-Link Libiary 命名为MyWindow 建立一个.cpp文件 MyWindow.cpp ---------------------------------------------------------- //MyWindow.cpp #include <jni.h> #include <jawt.h> #include <afxwin.h> #include <windows.h> #include "MyWindow.h" #include "jawt_md.h" //#include <assert.h> #include <process.h> // Includes for ATL #pragma comment(lib,"atl.lib") #include <atldef.h> #define _ATL_DLL_IMPL #include <atliface.h> #include <atlbase.h> #include <exdisp.h> // Structure for Thread Parameters. typedef struct { char szURL[1024]; HWND hwnd; } ThreadParam; // Helper functions. VOID CreateIEControl(ThreadParam *); static void WINAPIV StartATL(LPVOID); JNIEXPORT jint JNICALL Java_MyWindow_getNativeWindowHandle (JNIEnv *env, jobject jobj, jobject window) { JAWT awt; awt.version = JAWT_VERSION_1_3; jboolean result = JAWT_GetAWT(env, &awt); if (result == JNI_FALSE) return 0; JAWT_DrawingSurface* ds = awt.GetDrawingSurface(env, window); if (ds == 0) return 0; jint lock = ds->Lock(ds); if ((lock & JAWT_LOCK_ERROR) != 0) return 0; JAWT_DrawingSurfaceInfo* dsi = ds->GetDrawingSurfaceInfo(ds); if (dsi == 0) return 0; JAWT_Win32DrawingSurfaceInfo* dsiwin = (JAWT_Win32DrawingSurfaceInfo*) dsi->platformInfo; jint ret = reinterpret_cast<jint>(dsiwin->hwnd); ds->FreeDrawingSurfaceInfo(dsi); ds->Unlock(ds); awt.FreeDrawingSurface(ds); return ret; } // native method for initializing the control. JNIEXPORT void JNICALL Java_MyWindow_initialize (JNIEnv *pEnv, jobject, jint hwndIn, jstring string) { // Fill up the params. const char *str = pEnv->GetStringUTFChars(string, 0); ThreadParam *pThreadParam = new ThreadParam; pThreadParam->hwnd = (HWND) hwndIn; strcpy(pThreadParam->szURL,str); pEnv->ReleaseStringUTFChars(string, str); // Launch the Thread. _beginthread(StartATL, 0, pThreadParam); } // Thread for creating the control void WINAPIV StartATL(LPVOID lpVoid) { ThreadParam *pThreadParam = (ThreadParam *)lpVoid; CreateIEControl(pThreadParam); delete pThreadParam; MSG msg; // Windows message loop. while(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // Creates IE control VOID CreateIEControl(ThreadParam *pThreadParam) { AtlAxWinInit(); printf("Create AtlAxWin Begin...[0x%x][%s]/n",pThreadParam->hwnd,pThreadParam->szURL); // In the 2nd Param you can use ProgID or UUID of any activex control. HWND hwndChild = ::CreateWindow("AtlAxWin", "http://www.microsoft.com", WS_CHILD|WS_VISIBLE, 0,0,0,0, pThreadParam->hwnd,NULL, ::GetModuleHandle(NULL), NULL); IUnknown *pUnk = NULL; AtlAxGetControl(hwndChild,&pUnk); printf("Create AtlAxWin Done...[0x%x]/n",pUnk); // get an interface to set the URL. CComPtr<IWebBrowser2> spBrowser; pUnk->QueryInterface(IID_IWebBrowser2, (void**)&spBrowser); if (spBrowser) { CComVariant ve; CComVariant vurl(pThreadParam->szURL); #pragma warning(disable: 4310) // cast truncates constant value spBrowser->put_Visible(VARIANT_TRUE); #pragma warning(default: 4310) // cast truncates constant value spBrowser->Navigate2(&vurl, &ve, &ve, &ve, &ve); } } // native method for handling resizes. JNIEXPORT void JNICALL Java_MyWindow_resizeControl (JNIEnv *, jobject, jint hwndIn, jint width, jint height) { HWND hwnd = (HWND) hwndIn; RECT rc; if(hwnd!=NULL) { ::GetWindowRect(hwnd,&rc); HWND hwndChild = GetWindow(hwnd, GW_CHILD); printf("got resize (0x%x,%d,%d)/n",hwndChild,width,height); ::SetWindowPos(hwndChild,NULL,0,0,rc.right-rc.left,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOMOVE); } } ---------------------------------------------------------- 在编译之前有几件事情得先做一下 1.把刚才得到的MyWindow.h文件复制到工程中 2.把C:/Program Files/Java/j2sdk1.5.0/include和 C:/Program Files/Java/j2sdk1.5.0/include/win32下的文件复制到 C:/Program Files/Microsoft Visual Studio/VC98/Include下面 3.把C:/Program Files/Java/j2sdk1.5.0/lib下的jawt.lib复制到 C:/Program Files/Microsoft Visual Studio/VC98/Lib下 并在工程-〉设置-〉Link中添加jawt.lib 一切准备就绪按F7进行编译,于是在Debug中得到MyWindow.dll文件 将它复制到你编写的java 应用程序的文件夹中 好了运行MyWindow.class看看结果吧 |