以下是照抄原文:
【前言】
这几天用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) {}
- }
- }
【后记】
成功了,
从想到到实现,过程是痛苦的,结果是欣喜的,
有不懂得可以留言。
感谢上面两篇文章!
照抄结束,以下是在这过程中遇到的一些问题
首先关于win8.1系统使用VC++6.0程序崩溃问题,其实解决办法很简单找到vc++6.0安装文件夹,然后按以下路径进入
Microsoft Visual Studio→Common→MSDev98→Bin然后在Bin中找到 MSDEV.EXE并将其重命名为MSDEV3.EXE,然后再右键属性,以兼容模式运行这个程序,如下图
(顺便吐槽下,反正我就是不明白了这是什么玄学机制,你在背后只能加个数字3,如果你加其他数字的话,呵呵,依旧崩溃,所以我把这个归类于见鬼类别 )
在能运行VC++6.0后,你是不是觉得就没问题了?
图样图森破啊!
你以为这就结束了么?真的无法理解vc6.0的兼容问题
你在打开程序后,当你右键想要添加文件到目录的时候,那么你会发现一个很蛋疼的问题,那就是
解决完文件导入问题后,你就可以按开头文章的步骤去编写dll文件了(但是还有一个我不确定的东西,可能会导致你在java中应用dll文件时发生java.lang.UnsatisfiedLinkError错误!这个在后面我会提到)
当你编写完dll文件导出后,如果你是准备将其运用在32位的java上,那差不多就没问题了,但是!!!!!我是准备运用在64位的java之上,所以这时候我苦逼的发现,vc6.0的编译环境只有32位,换句话说只能编译出32位的dll动态库(其实还有另外的解决办法,那就是用vc6.0+sdk2003来模拟64位编译环境,但是微软早已经停止提供sdk2003的下载服务了,我唯一在这里:http://demon.tw/software/microsoft-platform-sdk-febrary-2003.html 找到了sdk2003的下载,但是不知道什么原因,在我这台机子上安装不了sdk2003,所以这个方法对我无效TAT,顺带吐槽下,基本我现在对VC6.0的看法是,这是一个已经被别人遗弃的孩子,但是大学校园依旧把他当作家里的顶梁柱看(顶梁柱?应该可以这么比喻吧)) 所以说,这时候,我毅然决然的决定。。。。。。换个vc工具。。。。
然后百度了下 ,发现貌似vc2005以上的版本都支持64位的编译环境,而我正好有下vs2010express版,所以就安装了,满怀我能解决的信心打开vc2010express折腾了半天之后,我蛋疼的发现,或许是因为这是学习版,所以,它并没有64位的编译环境设置(眼泪流了下来。。。),所以我有苦逼的在网络上找到了Visual Studio 2010 Ultimate,也就是最终版,才配置了64位的编译环境。。。。。。
(ps:顺带一提,由于vc2010默认使用的Unicode字符集,所以字符会报错,这时候点击项目→属性页→配置属性→常规,找到字符集选项,下拉菜单,选择使用多字节字符集,点击应用就可以解决了。)
编译环境设置完,我们这时候就可以开始编写dll动态库了(其实就是ctrl+c和ctrl+v),编写完之后,预编译的时候,你就会发现另一个蛋疼的问题了(warn不用管,没影响),这时候系统会提示
error C2065: “GWL_WNDPROC”: 未声明的标识符 的错误。(明明在32位编译环境没问题,为毛到x64上就出事了,而且我并没有学过C++关于这个的部分,你叫我怎么改错! )
于是,我又去请求度娘,然而度娘并没有回应我的呼唤,我觉得可能是因为没几个人碰到过这些问题吧,于是我转投必应娘的怀抱,在MSDN论坛的某个提问上找到了解决办法(那哥们遇到的问题跟我差不多,都是调用win32的声明,顺便挂地址:https://social.msdn.microsoft.com/Forums/vstudio/en-US/c62b1bf5-6a03-4c66-b24b-ea9fec3f2695/error-c2065-gwluserdata-undeclared-identifier?forum=vcgeneral),还有个很蛋疼的问题是,回答的人的代码打错了,正确的应该是GWLP_而不是GWPL_所以这也导致了我又蛋疼的遭遇报错。。。()
在历尽无数困难后,我终于编译得到了一个64位的dll动态库,于是我高高兴兴的扔进myeclipse里去运行了,然后,myeclipse就开始报错了,错误是 java.lang.UnsatisfiedLinkError ,可以简单理解为是找不到库文件,明明我已经把库放进去了,为什么又找不到了?
release重新编译后dll,放入myeclipse中,嗯,能用了,能运行了,然后我满怀欣喜的试用了下,嗯,alt+tab禁用了,win键禁用了,嗯ctrl+alt+del。。。。。。诶,为什么任务管理器跳了出来!卧槽!,然后回顾了作者的文章,发现,作者也说了”xp下可以屏蔽所有按键“所以在win7及以上的版本,因为系统调用优先度的问题,并不可以屏蔽调用任务管理器的热键
所以说,我之前花费了那么多时间,究竟意义何在啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
什么都不多说了,让我一个人静静。