在SWT Extension中,引入了Function這個類。基本上所有的Win32 JNI庫都有這個類,用來直接操縱Win32 的部分API。有了這個Class,我們不用編寫JNI,就可以實現某些簡單的,甚至是較復雜的Win32 API。這里我們就以EnumWindows這個API舉例,看看怎么Java來執行這個Win32 API。
private static final String FUNTION_ENUMWINDOWS = "EnumWindows";
private static final String USER32_LIB = "user32";
private static List windowsList = new ArrayList();
public static int[] enumWindows()
{
windowsList.clear();
Callback callback = new Callback(Windows.class, "enumWindowsProc", 2);
int address = callback.getAddress();
if (address != 0)
{
try
{
Function function = new Function(USER32_LIB, FUNTION_ENUMWINDOWS);
function.invoke_I(address, 0);
function.close();
} catch (Exception e)
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
callback.dispose();
}
int[] handles = new int[windowsList.size()];
for (int i = 0; i < windowsList.size(); i++)
handles[i] = ((LONG) windowsList.get(i)).value;
return handles;
}
private static int enumWindowsProc(int hwnd, int lParam)
{
windowsList.add(new LONG(hwnd));
return 1;
}
EnumWindows是用來遍歷Windows窗口的API,它需要傳入一個返回boolean值的callback的地址作為參數。實際上在C里面,一個boolean值無非就是是否非0,如果為0,則為false,不為0,則為true。我們只需要new 一個function實例,傳入這個API所在的Lib和API名字,然后執行invoke方法就OK了,在Function里面,可以最多執行含有4個簡單類型參數的API。
讓我們再來看看FindWindowEx這個API,它需要傳入2個int變量和2個字符串指針,根據SWT的設計,我們是可以將Java的字符串轉換為指針的,因此通過Function我們也可以實現這個API:
private static final String FUNTION_FINDWINDOWEX = Extension.IsUnicode ? "FindWindowExW"
: "FindWindowExA";
private static final String USER32_LIB = "user32";
public static int findWindowEx(int parent, int hwndChildAfter, String className,
String windowName)
{
int result = 0;
int lpClassName = 0;
int lpWindowName = 0;
int hHeap = Extension.GetProcessHeap();
if (className != null)
{
TCHAR buffer = new TCHAR(0, className, true);
int byteCount = buffer.length() * TCHAR.sizeof;
lpClassName = Extension.HeapAlloc(hHeap, Extension.HEAP_ZERO_MEMORY, byteCount);
Extension.MoveMemory(lpClassName, buffer, byteCount);
}
if (windowName != null)
{
TCHAR buffer = new TCHAR(0, windowName, true);
int byteCount = buffer.length() * TCHAR.sizeof;
lpWindowName = Extension.HeapAlloc(hHeap, Extension.HEAP_ZERO_MEMORY, byteCount);
Extension.MoveMemory(lpWindowName, buffer, byteCount);
}
try
{
Function function = new Function(USER32_LIB, FUNTION_FINDWINDOWEX);
result = function.invoke_I(parent, hwndChildAfter, lpClassName, lpWindowName);
function.close();
} catch (Exception e)
{
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
}
if (lpClassName != 0) Extension.HeapFree(hHeap, 0, lpClassName);
if (lpWindowName != 0) Extension.HeapFree(hHeap, 0, lpWindowName);
return result;
}
其實像這種簡單參數類型的API,Win32 里還有很多,我們完全不必為其專門編寫JNI,只需使用熟悉的Java即可。雖然不是調用全部的API,但大部分常用的API都是沒有問題的,關鍵是如何靈活運用。現在的大型商業RCP應用中,其實多多少少都參和了JNI,用于提升對用戶的友好性和軟件的執行性能,畢竟Java天生就是客戶端開發的矮子。對于JNI,我們既不能一味排斥,也不能濫用,要把握一個平衡點,使之成為Java客戶端開發的利器。