(一) 動態鏈接庫初入門
1.前段時間,閒來無聊,想做個像QQ截圖一樣的截圖工具,在卻在做的過程中發現java自帶API中的監聽器帶有很大的侷限性,java的鼠標監聽器只有在鼠標在程序窗口之上時纔會生效,而鍵盤監聽器則更加侷限,只有在當前窗口爲焦點窗口時纔會生效,這顯然是不符合我們需要效果的,我們所需要的是全局的鍵盤監聽,不管你在幹什麼,只要觸發我們設定的固定的組合鍵是,就會執行我們需要的功能,所以用java是不太好辦。
2.那麼QQ是怎麼做到的呢?QQ是用C語言寫的,並不是java,而且windows也是C語言寫出來的,所以他能輕易的實現這種全局監聽。這也就是一個突破口,native,不知道你有沒有注意過這個關鍵字,這是用來調用本地代碼的一個關鍵字。我們可以這樣設想,假如我們將所有的代碼全部用java實現,卻用C語言去監聽全局鍵盤,然後返回爲我們所用。是不是很有意思。
首先,我說一說整體的思路,由於java中監聽器的侷限性,我們將用C語言的HHOOK消息鉤子,來獲取到全局消息的監聽,然後通過jni技術,用java調用,於是就形成java監聽器的全局監控。
3.下面我來說明這樣用java代碼調用C語言的方法(函數)。
package test;
public class HelloWorld{
static{
System.loadLibrary("HelloDll");
}
public static void main(String args[]){
HelloWorld hw = new HelloWorld();
hw.sayHello();
}
public native void sayHello();
}
上面這個System.loadLibrary("HelloDll");此句爲引入一個叫做HelloDll的本地文件,而這個必定包含了sayHello();的實現代碼。java工作我們算是昨晚了,也就是一個簡單的打印HelloWorld的代碼,重要的是我們怎樣用C語言去實現這個本地方法,怎樣讓其經行工作。
(1).編寫java文件。上一步已經實現
(2).javac運行源文件生成class文件
(3).javah test.HelloWorld 這裏注意不用上帶後綴,我在經行這一步驟時,路徑問題很麻煩。在包這一層運行javah test.HelloWorld就會生成一個叫test_HelloWorld.h的C語言頭文件。
因爲我們的方法是要用C語言實現的,所以經行到這一步,我們得到了一個C的頭文件。如果你打開這個頭文件,你會發現裏面會有一個叫做JNIEXPORT void JNICALL Java_test_HelloWorld_sayHello(JNIEnv *, jobject);的方法,這個方法其實就是我們的java方法在C語言裏邊的映射了(我一般這麼稱呼,不知道對不對),我們只要將這個方法用C語言實現了,然後編譯成一個java能調用的DLL本地文件就OK了。
(4)打開Microsoft Visual C++,File-->new-->Win32 Dynamic-Link Library,新建一個叫HelloDLL的文件動態鏈接工程,點擊左下角FileView,點開HelloDll files。然後File-->new-->C/C++ Header File,名字爲test_HelloWorld.h,然後點開Header Files裏的test_HelloWorld.h,將我們剛纔生成的test_HelloWorld.h文件內容複製進去,再File-->new-->C++ Source File,名字hello(隨便取),然後點開文件寫入內容:
#include "test_HelloWorld.h"
#include <iostream.h>
JNIEXPORT void JNICALL Java_test_HelloWorld_sayHello
(JNIEnv *, jobject){
cout<<"HelloWorld"<<endl;
}
可以看出,我們生成頭文件的目的,無非就是爲了用C語言實現,因爲我們這裏引入了這個頭文件,而這裏邊唯一的一個方法,就是我們生成的頭文件,也就是我們的java代碼未實現的代碼在C中的映射,然後我在這個方法中打印了HelloWorld這句話。(如果看不懂C代碼,建議百度一下,就算看不懂,憑我文字描述應該也是能懂一些的,不過應該不會看不懂吧)。
另外就是,這一個段落我寫的比較詳細,甚至比較囉嗦,主要是爲了沒用過Microsoft Visual C++或者沒學過C的人,能夠手把手的教他運行這個程序,大神可以略過。
好了,言歸正傳,我們還需引入java的lib環境,Tools-->Options-->Directories在底下的方框欄中加入你C:\PROGRAM FILES\JAVA\JDK1.6.0_04\INCLUDE和C:\PROGRAMFILES\JAVA\JDK1.6.0_04\INCLUDE\WIN32也就是java的JDK的include和include中win32兩個文件夾。然後Build-->Rebuild All。
你會發現,在你C++文件夾HelloDll中Debug中有一個HelloDll.dll文件。那麼這個文件就是我們需要的動態鏈接文件了。如果你將它打開,那麼你會發現大部分是亂碼。
(5)將得到的HelloDll.dll複製到你的java工程的包這一層下,然後運行java test.eHelloWorld,就會打印HelloWorld這句話了,如果在沒有這個dll文件時你就引進運行了,會拋出一個找不到動態鏈接文件的異常。
(6)這樣我們就實現了用java調用C語言的方法了。這就是動態鏈接了,在下一篇我將描述,怎樣用C語言的HHOOK鉤子去獲取windows底層鍵盤和鼠標的相應。
(二)怎樣用C語言獲取全局消息HHOOK
國際慣例,我先說一說這一章的思路,這一章主要是講Windows API中兩個鉤子函數,加上一些窗口處理。首先我利用API創建窗體,窗體消息交由WindowProc(自定義)去處理,在窗體創建時設置全局鉤子,在窗體銷燬時去除鉤子,而在鉤子內部,通過keyLisProc和mouseLisProc去處理鉤子獲取到的消息,
當鉤子獲取到鼠標或鍵盤時,賦予全局變量keyCode或mouseCode,最後通過javah生成的函數返回給java動態鏈接,實現java的jni全局監控
(1)在Windows的API中,有這麼一個函數
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch ( msg ) {
//當窗體創建的時候
case WM_CREATE:
//將系統鉤子設置
hKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyLisProc, all_hInst, 0);
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseLisProc, all_hInst, 0);
break;
//當窗體清除的時候
case WM_DESTROY:
//如果消息鉤子已經設置
if ( hKeyHook != NULL && hMouseHook != NULL ) {
//清除消息鉤子
UnhookWindowsHookEx(hKeyHook);
UnhookWindowsHookEx(hMouseHook);
}
//向系統發送關閉信息
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}
這就是windows在窗口消息中的響應方法,他規定了在窗體創建時設置鉤子,窗體消失時取消鉤子。而鉤子設置成功後,可以看到是由一個叫keyLisProc的函數去處理消息的
LRESULT CALLBACK keyLisProc(int nCode, WPARAM wParam, LPARAM lParam) {
//存儲按鍵信息的結構體
KBDLLHOOKSTRUCT* KeyInfo = NULL;
//如果按下了
if ( nCode >= 0 && wParam == WM_KEYDOWN ) {
KeyInfo = (KBDLLHOOKSTRUCT*)lParam;
//獲取到結構體中按鍵的KeyCode值
keyCode = KeyInfo->vkCode;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
這個就是那個消息處理,首先將獲取到鍵盤信息,存儲到KeyInfo結構體中,然後從中取出來付給KeyCode,而keyCode是一個全局變量,我們只需要在javah時生成的函數中,返回給java代碼就可以了,是不是相對來說,有點複雜了,但事實上思路很清晰,只要多分析,就不會太難理解。
#define WH_KEYBOARD_LL 13
#define WH_MOUSE_LL 14
typedef struct tagMSLLHOOKSTRUCT {
POINT pt;
DWORD mouseData;
DWORD flags;
DWORD time;
DWORD dwExtraInfo;
} MSLLHOOKSTRUCT, FAR *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;
typedef struct tagKBDLLHOOKSTRUCT {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
DWORD dwExtraInfo;
} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
在C語言這一塊,我只是講解了很多思路,因爲你如果不會這門 語言,我真的沒有辦法在這裏教會你,這是一個比較難以表達的事情,如果你真的很想搞懂,987706386是我的QQ,其實我思路已經說得比較明瞭,加上下載我上傳的文檔看一看,那就很容易理解。