很無聊,是一直到無意間買到 WinAPI on VB6 的書,才
開始繼續對 VB 有興趣。一直到接觸了 Java 之後,還是對
Windows API 抱著很大的興趣,一直想要用 Java 呼叫 WinAPI
但是都不得其門而入。過了一年多,現在想想當初真的很好笑
Java 操作 WinAPI 根本破壞了 Java 本身最大的特點:跨平台。
現在用 Java 呼叫 WinAPI 已經是不得以的情況才會用了....
如果有人想要用 Java 呼叫 Windows API,就參考看看吧。
【本文開始】
先備知識:Java、C、WindowsAPI
使用軟體:Dev C++ (Windows)
當安裝好 JDK 和 Dev C++ 之後,要先作一件事情,請到 JDK 安裝
目錄(我的例子是安裝在C:\Program Files\Java\jdk1.6.0_17)
找到 include 資料夾,並把裡面的所有檔案,都複製到 Dev C++ 安
裝目錄下的 include 中。這樣子 Dev C++ 在編譯檔案的時候,才抓的
到需要的 Header File。
因為 Java 不能透過 JNI 直接呼叫 Windows API,必須得另外撰寫一個
DLL 去連結 WinAPI。所以這邊要利用 自己撰寫的 DLL 去連結 WinAPI。
首先編寫好 Java 部分的程式,假設這邊要呼叫 Windows API 的 ShowWindow
方法,讓輸入的 hWnd 視窗隱藏起來:
/* In File WinAPI.java */
public class WinAPI
{
static{System.loadLibrary("WinAPI");}
public static native int setWindowVisible(int hWnd,int state);
}
首先說明一下, static{} 是類別程式區塊,會在類別載入時就執行
由於這個類別需要引用 DLL 檔(Library),所以要先寫在這邊。
而其中的 WinAPI 就是 dll 的檔名,不用和這個 Class 名稱一樣
只是方便識別而已。
要注意的是 native,告知 Java 這是一個 Native Method。
編譯這個檔案: javac WinAPI.java
接著,要根據這個檔案製作出對應的標頭檔,指令是:
javah WinAPI
(javah Class名稱)
成功的話會產生一個 .h 的檔案,內容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class WinAPI */
#ifndef _Included_WinAPI
#define _Included_WinAPI
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: WinAPI
* Method: setWindowVisible
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_WinAPI_setWindowVisible
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
宣告的方式就是:
JNIEXPORT 傳回型態 JNICALL Java_Class名稱(以_取代.)_方法名稱
剩下的就是一般的宣告,接著開啟 Dev C++ 新增一個 C 語言的
DLL 專案:
接著再開啟的檔案裡面,會有一個 dll.h,把這邊的內容用剛剛產生出來
的文件取代掉。
接著看到開啟的 dllmain.c 把裡面的 HelloWorld 方法整個砍掉(這是預設的範例)
接著,複製 dll.h 裡面的方法宣告到 dllmain.c 裡面,並且給予變數名稱。
(紅色字部分表示增加的)
JNIEXPORT jint JNICALL Java_WinAPI_setWindowVisible
(JNIEnv *jvm, jclass jCls, jint hWnd, jint state)
{
}
這邊要注意兩個特殊參數: jvm 和 jCls (變數名稱可自訂)
jvm 代表這個 JVM 的實體,也就是說可以利用它來直接操作 JVM
jCls 是呼叫的 Java Class 實體,在應用到 CallBack 時會需要,不過
這個範例沒有要用到,所以先不用理它。
接著就是在裡面撰寫要呼叫的 WinAPI 了。先來看 ShowWindow 這個方法:
BOOL ShowWindow(HWND hWnd,int nCmdShow);
但是我們從 Java 那邊接收到的型態卻是兩個 jint,該怎麼辦呢?
請開啟 jni.h (到 dll.h 那邊,找到 #include
再找到 #include "jni_md.h" 一樣開啟 jni_md.h 看,可以看到:
typedef long jint;
原來 jint 就是 long,所以可以對他強制轉型啦。
我們直接在方法裡面呼叫:
return (jint)ShowWindow((HWND)hWnd,(int)state);
完整如下:
JNIEXPORT jint JNICALL Java_WinAPI_setWindowVisible
(JNIEnv *jvm, jclass jCls, jint hWnd, jint state)
{
}
接著就可以編譯了。編譯成功之後,應該會輸出一個 WinAPI.dll
把它放到 WinAPI.class 的目錄底下,讓 Java loadLibrary 的時候
可以抓到。
接著再撰寫一個 class(TEST.class 和 WinAPI.class 在同目錄下):
/* In File TEST.java */
import java.util.*;
public class TEST
{
public static void main(String[] args)
{
Scanner ipt=new Scanner(System.in);
System.out.print("請輸入 hWnd:");
int hWnd=ipt.nextInt();
System.out.print("輸入選項: (1)隱藏 (2)顯示 (0)離開:");
while(ipt.hasNextInt())
{
int tmp=ipt.nextInt();
switch(tmp)
{
case 0:
System.exit(0);
break;
case 1://SW_HIDE=0
WinAPI.setWindowVisible(hWnd,0);
break;
case 2://SW_SHOW=5
WinAPI.setWindowVisible(hWnd,5);
break;
}
System.out.print("輸入選項: (1)隱藏 (2)顯示 (0)離開:");
}
}
}
執行,就可以得到我們要的結果了。
範例程式下載:http://w5.loxa.com.tw/kk6/Java/JNI.rar
沒有分類,反正 .java 和 .class 是屬於 Java 的,剩下的就是 Dev C++
沒有留言:
張貼留言