【前言】
这几天用Java做个锁屏软件需要屏蔽系统热键,就是Win+D,Ctrl+Alt+Del等,
网上找了好多,发现这篇文章:
java运用jni调用dll(含源码)实现屏蔽系统热键和任务栏
http://hi.baidu.com/nowgame/blog/item/4530e11f20f289fee1fe0ba1.html
但是对于只懂Java的人无疑是一种折磨,
好不容易跟着文章做下来又报错,总之很是痛苦,
今天又看了篇文章:
http://wenku.baidu.com/view/51bf0d96daef5ef7ba0d3c54.html
结合这两篇文章终于实现了Java屏蔽系统热键了。
下面写出来,图文结合,宣泄一下这几天的郁闷。
【说明】
1.本文都是在eclipse下开发的,dos下可以自己尝试。
2.以下源码都是上面第一篇文章中的
【1】编写Java文件,编译出class文件,javah出.h文件
——Java源码
package com.uikoo9.JLocker;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
*
* @author Administrator
*/
public class ShieldHotKey {
static{
//下面这部分是为了增加灵活性,dll可以放到jar包中
try
{
File file = File.createTempFile("shieldHK", ".dll");
FileOutputStream fout = new FileOutputStream(file);
InputStream in = ShieldHotKey.class.getResourceAsStream("shieldHK.dll");
byte[] b = new byte[1024];
int len = 0;
while((len = in.read(b)) != -1){
fout.write(b, 0, len);
}
fout.flush();
in.close();
fout.close();
System.load(file.getAbsolutePath());
}
catch (Exception e) {}
// System.load("D:/shieldHK.dll");
}
public static native void Attach();//启动屏蔽
public static native void Detach();//关闭屏蔽
}
说明:1.类ShieldHotKey是建在com.uikoo9.JLocker包下的,
2.将原来的System.load("D:/shieldHK.dll");改写是为了增加灵活性。
——Eclipse自动会生成.class文件,找到它,如图:
——cmd下,到这个bin的这一层,输入已下命令生成.h文件:
生成的.h文件:
【2】VC下生成dll文件
——在VC下新建一个dll工程,具体见下图:
说明:1.选择Win32 Dynamic-Link Library工程,
2.工程名就是将来生成的dll名称,但是无关紧要可以更改。
3.点确定之后选一个空的dll工程,完成。
——找到这个工程在电脑上的地方:
——将以下三个.h文件都复制到上面工程文件夹中
第一个.h文件:com_uikoo9_JLocker_ShieldHotKey.h,就是刚才生成的.h文件;
第二个.h文件:jni.h,在jdk下include文件夹下;
第三个.h文件:jni_md.h,在jdk下include文件夹下的win32文件夹中;
——VC中导入.h文件
在FileView窗口中,右键Header Files文件夹选添加文件到目录,将上面的三个文件都导入。
——VC中编写cpp文件
VC中新建——文件——C++ Source File ——文件名随便起
代码如下:
/* Replace "dll.h" with the name of your header */
#include "shieldHK.h"
#define _WIN32_WINNT 0x0500 //Use WH_KEYBOARD_LL
#include <windows.h>
#include <stdio.h>
//SAS window句柄
HWND hSASWnd = NULL;
//原有SAS window回调函数地址
FARPROC FOldProc = NULL;
//起屏蔽作用的新SAS window回调函数
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//枚举所有窗体句柄的回调函数
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam);
//Dll所创建线程的句柄
HANDLE hThread = NULL;
//Dll所创建线程的ID
DWORD dwThreadId = 0;
//Dll所创建线程的线程函数
DWORD WINAPI ThreadFunc();
//_H钩子句柄
HHOOK hHook = NULL;
//_H低级键盘钩子回调函数
LRESULT CALLBACK KeyboardProc(int,WPARAM,LPARAM);
//对外输出字符串
char szOutput[36];
BOOL APIENTRY Attach()
{
switch(DLL_PROCESS_ATTACH)
{
case DLL_PROCESS_ATTACH:
sprintf(szOutput,"Dll成功加载于 %d 号进程。",GetCurrentProcessId());
OutputDebugString(szOutput);
//创建更替SAS window回调函数的线程
if(FOldProc == NULL)
hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
break;
case DLL_PROCESS_DETACH:
sprintf(szOutput,"Dll成功卸载。",GetCurrentProcessId());
//MessageBox(NULL, szOutput, "ZZ", MB_ICONINFORMATION | MB_OK);
OutputDebugString(szOutput);
//恢复原有SAS window的回调函数
if(FOldProc != NULL)
SetWindowLong(hSASWnd,GWL_WNDPROC,long(FOldProc));
//_H卸载低级键盘钩子
if(hHook != NULL)
{
if(!UnhookWindowsHookEx(hHook))
{
OutputDebugString("Unhook failed..");
//__leave;
break;
}
OutputDebugString("键盘钩子成功取消");
}
TerminateThread(hThread,1);
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
BOOL APIENTRY Detach()
{
switch(DLL_PROCESS_DETACH)
{
case DLL_PROCESS_ATTACH:
sprintf(szOutput,"Dll成功加载于 %d 号进程。",GetCurrentProcessId());
OutputDebugString(szOutput);
//创建更替SAS window回调函数的线程
if(FOldProc == NULL)
hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
break;
case DLL_PROCESS_DETACH:
sprintf(szOutput,"Dll成功卸载。",GetCurrentProcessId());
//MessageBox(NULL, szOutput, "ZZ", MB_ICONINFORMATION | MB_OK);
OutputDebugString(szOutput);
//恢复原有SAS window的回调函数
if(FOldProc != NULL)
SetWindowLong(hSASWnd,GWL_WNDPROC,long(FOldProc));
//_H卸载低级键盘钩子
if(hHook != NULL)
{
if(!UnhookWindowsHookEx(hHook))
{
OutputDebugString("Unhook failed..");
//__leave;
break;
}
OutputDebugString("键盘钩子成功取消");
}
TerminateThread(hThread,1);
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
//Dll所创建线程的线程函数
DWORD WINAPI ThreadFunc()
{
//打开Winlogon桌面
HDESK hDesk = OpenDesktop("Winlogon",0,FALSE,MAXIMUM_ALLOWED);
//枚举桌面所有窗体
EnumDesktopWindows(hDesk,(WNDENUMPROC)EnumWindowsProc,0);
//修改SAS window的回调函数
if(hSASWnd != NULL)
{
FOldProc = (FARPROC)SetWindowLong(hSASWnd,GWL_WNDPROC,long(SASWindowProc));
}
CloseHandle(hDesk);
//_H同一桌面上进程之间只能发送窗口消息。无法跨进程与其他桌面发送它们。
//_H同样,Windows消息是限制应用程序定义挂钩。
//_H特定桌面中运行的进程挂钩过程将〈〈只获得针对同一桌面上创建窗口消息。〉〉
//_H详见http://support.microsoft.com/kb/171890/zh-cn
//_H所以,这里必须设置钩子所在线程的桌面为Default桌面
//_H才能使得钩子所在线程能接收到Default桌面的消息
hDesk = OpenDesktop("Default",0,FALSE,MAXIMUM_ALLOWED);
SetThreadDesktop(hDesk);
CloseHandle(hDesk);
//_H设置低级键盘钩子,屏蔽非SAS window的热键
//_H需要#define _WIN32_WINNT 0x0500
hHook = SetWindowsHookEx(WH_KEYBOARD_LL,KeyboardProc,GetModuleHandle(NULL),0);
if (hHook == NULL)
{
OutputDebugString("Set hook failed..");
//__leave;
return 1;
}
OutputDebugString("键盘钩子成功设置");
//_H在非GUI线程中使用消息钩子必须主动接收并分发收到的消息
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
//枚举所有窗体句柄的回调函数
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
char ClassBuf[128];
//获得当前窗体的显示文本
GetWindowText(hwnd,ClassBuf,sizeof(ClassBuf));
//在"Winlogon"桌面中查询窗口"SAS window"。
if(strstr(ClassBuf,"SAS window")!=NULL)
{
//返回SAS window句柄
hSASWnd = hwnd;
return FALSE;
}
return TRUE;
}
//起屏蔽作用的新SAS window回调函数
LRESULT CALLBACK SASWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if(uMsg == WM_HOTKEY)
{
//屏蔽所有WM_HOTKEY消息
OutputDebugString("All SAS window's hotkeys are disabled");
return 1;
WORD wKey = HIWORD(lParam);
WORD wModifier = LOWORD(lParam);
bool IsCtrlDown = ((wModifier & VK_CONTROL) != 0);
bool IsAltDown = ((wModifier & VK_MENU) != 0);
bool IsShiftDown = ((wModifier & VK_SHIFT) != 0);
//Ctrl + Alt + Del组合键
if(IsCtrlDown && IsAltDown && wKey == VK_DELETE)
{
return 1; //屏蔽
}
//Ctrl + Shift + Esc组合键,这个组合键将显示任务管理器,可根据需要是否屏蔽。
else if(IsCtrlDown && IsShiftDown && wKey == VK_ESCAPE)
{
// Do nothing
}
}
return CallWindowProc((WNDPROC)FOldProc,hwnd,uMsg,wParam,lParam);
}
//_H低级键盘钩子回调函数
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_KEYDOWN: case WM_SYSKEYDOWN:
//case WM_KEYUP: case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
if (p->vkCode == VK_F12)
{
//实现模拟按键代码
MessageBox(GetForegroundWindow(),"I'm in position..","ZZ",MB_OK);
}
//屏蔽ALT+TAB
else if ((p->vkCode == VK_TAB) && ((p->flags & LLKHF_ALTDOWN) != 0))
{
OutputDebugString("ALT+TAB is disabled");
return 1;
}
//屏蔽ALT+ESC
else if ((p->vkCode == VK_ESCAPE) && ((p->flags & LLKHF_ALTDOWN) != 0))
{
OutputDebugString("ALT+ESC is disabled");
return 1;
}
//屏蔽CTRL+ESC
else if ((p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
{
OutputDebugString("CTRL+ESC is disabled");
return 1;
}
//屏蔽CTRL+SHIFT+ESC,(SAS window中也已屏蔽)
else if ((p->vkCode == VK_ESCAPE) &&
((GetKeyState(VK_CONTROL) & 0x8000) != 0) &&
((GetKeyState(VK_SHIFT) & 0x8000) != 0))
{
OutputDebugString("CTRL+SHIFT+ESC is disabled");
return 1;
}
//屏蔽ALT+F4
else if ((p->vkCode == VK_F4) && ((p->flags & LLKHF_ALTDOWN) != 0))
{
OutputDebugString("ALT+F4 is disabled");
return 1;
}
//屏蔽左右windows键
else if (p->vkCode == VK_LWIN || p->vkCode == VK_RWIN)
{
OutputDebugString("windows key is disabled");
return 1;
}
//此处无法屏蔽CTRL+ALT+DEL,已在SAS window中屏蔽
else if ((p->vkCode == VK_DELETE) &&
((GetKeyState(VK_CONTROL) & 0x8000) != 0) &&
((GetKeyState(VK_MENU) & 0x8000) != 0 ))
return 1;
break;
}
}
return CallNextHookEx(hHook,nCode,wParam,lParam);
}
JNIEXPORT void JNICALL Java_shieldHK_ShieldHotKey_Attach
(JNIEnv *env, jclass obj){
Attach();
}
JNIEXPORT void JNICALL Java_shieldHK_ShieldHotKey_Detach
(JNIEnv *env, jclass obj){
Detach();
}
——对几个文件的修改
将com_uikoo9_JLocker_ShieldHotKey.h中的#include <jni.h>改为#include "jni.h"
将test.cpp中的#include "shieldHK.h"改为#include "com_uikoo9_JLocker_ShieldHotKey.h",也就是上面的.h文件
test.cpp的末尾两个方法的代码如下:
JNIEXPORT void JNICALL Java_shieldHK_ShieldHotKey_Attach
(JNIEnv *env, jclass obj){
Attach();
}
JNIEXPORT void JNICALL Java_shieldHK_ShieldHotKey_Detach
(JNIEnv *env, jclass obj){
Detach();
}
com_uikoo9_JLocker_ShieldHotKey.h中的两个方法代码如下:
JNIEXPORT void JNICALL Java_com_uikoo9_JLocker_ShieldHotKey_Attach
(JNIEnv *, jclass);
/*
* Class: com_uikoo9_JLocker_ShieldHotKey
* Method: Detach
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_uikoo9_JLocker_ShieldHotKey_Detach
(JNIEnv *, jclass);
将test.cpp中的两个方法名改为com_uikoo9_JLocker_ShieldHotKey.h中两个对应的方法名,
否则在eclipse中会报错。
——VC中编译cpp文件生成dll文件
对test.cpp先Compile再Build,生成dll文件
生成的dll文件在vc项目的debug文件夹中
【3】Eclipse中使用
——将testdll.dll文件改名为shieldHK.dll,复制到eclipse中java代码处,如图:
——编写test.java测验一下能行不,代码:
package com.uikoo9.JLocker;
public class Test
{
public static void main(String[] args)
{
try
{
ShieldHotKey.Attach();
Thread.sleep(5000);//5秒内键盘热键都被屏蔽了。
}
catch (Exception e) {}
}
}
【后记】
成功了,
从想到到实现,过程是痛苦的,结果是欣喜的,
有不懂得可以留言。
感谢上面两篇文章!