去年寒假回家闲着无事,就在家中敲敲代码。主要是用java语言写一些小的运用:放大镜,屏幕截图
简单画图工具、取色器什么的,,最后整合成一个小型的软件。不过在这个过程中,发现java中实现屏幕
截图、取色的时候对于鼠标的操作还是显得有些不够,上面的功能一般是事先得到整个屏幕,之后再
在该图片上操作。这使得界面软件必须处于激活状态,很不方便。
其实,采用上面的那种操作实现功能也并不见得有多不好,不过兴趣使然,想看看java借助一些第
三方jar能否实现与windows底层的交互。因此,上网查了一下,总结如下:
通过查找,了解到了可以通过"鼠标钩子"实现,何为"钩子",下面为"扒来的":
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗
口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之
前处理它。钩子机制允许应用程序截获处理window消息或特定事件。钩子实际上是一个处理消息的程
序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达 目的窗口前,钩子程序就
先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以
不作处理而继续传递该消息,还可以强制结束消息的传递。
Hook API是指Windows开放给程序员的编程接口,使得在用户级别下可以对操作系统进行控制,
也就是一般的应用程序都需要调用API来完成某些功能,Hook API的意思就是在这些应用程序调用
真正的系统API前可以先被截获,从而进行一些处理再调用真正的API来完成功能。
1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。
(2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。
(3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。
(4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。
(5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息
说了这么多概念性的东西,但是我们怎样用java实现鼠标钩子呢?
这就需要借助jna来操作了
JNA(Java Native Access )提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。
既然知道了实现的方法,那就动手吧,下面是我实现的代码,当然也有借鉴网上这方面的资料.
MouseLLHook.java
package com.kiritor; import java.awt.AWTException; import java.awt.Color; import java.awt.Cursor; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Robot; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.*; import com.sun.jna.platform.win32.WinUser.*; import com.sun.jna.platform.win32.WinDef.HMODULE; import com.sun.jna.platform.win32.WinDef.LRESULT; import com.sun.jna.platform.win32.WinDef.WPARAM; import com.sun.jna.platform.win32.WinUser.HHOOK; import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT; import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc; public class MouseLLHook { private boolean flag; public ColorFrame getColorFrame() { return colorFrame; } public void setColorFrame(ColorFrame colorFrame) { this.colorFrame = colorFrame; } private static ColorFrame colorFrame = null; // 鼠标钩子函数里判断按键类型的常数 public static Point mousepoint; public static MouseInfo mouseInfo = null; static Color pixel = new Color(0, 0, 0); static Integer R = 0, G = 0, B = 0; static Integer X = 0, Y = 0; public static final int WM_LBUTTONUP = 514; public static final int WM_LBUTTONDOWN = 513; public static final int WM_RBUTTONUP = 517; public static final int WM_RBUTTONDOWN = 516; public static final int WM_MOUSEHWHEEL = 526; public static final int WM_MOUSEWHEEL = 522; public static final int WM_MOUSEMOVE = 512; static HHOOK mouseHHK, keyboardHHK;// 鼠标、键盘钩子的句柄 static LowLevelMouseProc mouseHook;// 鼠标钩子函数 static LowLevelKeyboardProc keyboardHook;// 键盘钩子函数 public MouseLLHook(ColorFrame colorFrame) { this.colorFrame=colorFrame; } // 安装钩子 static void setHook() { HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null); mouseHHK = User32.INSTANCE.SetWindowsHookEx(WinUser.WH_MOUSE_LL, mouseHook, hMod, 0); mouseHHK.nativeType(); keyboardHHK = User32.INSTANCE.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0); } // 卸载钩子 static void unhook() { User32.INSTANCE.UnhookWindowsHookEx(keyboardHHK); User32.INSTANCE.UnhookWindowsHookEx(mouseHHK); } public void getColor(String[] args) { keyboardHook = new LowLevelKeyboardProc() { @Override // 该函数参数的意思参考:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx public LRESULT callback(int nCode, WPARAM wParam, KBDLLHOOKSTRUCT lParam) { int w = wParam.intValue(); // 按下alt键时w=.WM_SYSKEYDOWN; 按下其他大部分键时w=WinUser.WM_KEYDOWN if (w == WinUser.WM_KEYDOWN || w == WinUser.WM_SYSKEYDOWN) System.out.println("key down: vkCode = " + lParam.vkCode); else if (w == WinUser.WM_KEYUP || w == WinUser.WM_SYSKEYUP) System.out.println("key up: vkCode = " + lParam.vkCode); // 如果按下'q'退出程序,'q'的vkCode是81 if (lParam.vkCode == 81) { unhook(); System.err.println("program terminated."); System.exit(0); } return User32.INSTANCE.CallNextHookEx(keyboardHHK, nCode, wParam, lParam.getPointer()); } }; mouseHook = new LowLevelMouseProc() { @Override // 该函数参数的意思参考:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644986(v=vs.85).aspx public LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT lParam) { switch (wParam.intValue()) { case WM_MOUSEMOVE: //System.out.print("mouse moved:"); break; case WM_LBUTTONDOWN: { try { Robot robot = new Robot(); mousepoint = mouseInfo.getPointerInfo().getLocation(); // System.out.println(mousepoint); pixel = robot.getPixelColor(mousepoint.x, mousepoint.y); X = mousepoint.x; Y = mousepoint.y; R = pixel.getRed(); G = pixel.getGreen(); B = pixel.getBlue(); String s16 = "#" + Integer.toHexString(R) + Integer.toHexString(G) + Integer.toHexString(B);// 得到颜色的十六进制表示。 Color col = new Color(R, G, B); if(colorFrame.isFlag()==false) { colorFrame.getRedText().setText(R.toString()); colorFrame.getGreenText().setText(G.toString()); colorFrame.getBlueText().setText(B.toString()); colorFrame.getColor16Text().setText(s16); colorFrame.getNewColor().setBackground(col); colorFrame.setFlag(true); } colorFrame.setVisible(true); } catch (AWTException ex) { ex.printStackTrace(); } } break; case WM_LBUTTONUP: //System.out.print("mouse left button up"); break; case WM_RBUTTONUP: //System.out.print("mouse right button up:"); break; case WM_RBUTTONDOWN: //System.out.print("mouse right button down:"); break; case WM_MOUSEWHEEL: //System.out.print("mouse wheel rotated:"); break; } //System.out.println("(" + lParam.pt.x + "," + lParam.pt.y + ")"); return User32.INSTANCE.CallNextHookEx(mouseHHK, nCode, wParam, lParam.getPointer()); } }; setHook(); int result; MSG msg = new MSG(); // 消息循环 // 实际上while循环一次都不执行,这些代码的作用我理解是让程序在GetMessage函数这里阻塞,不然程序就结束了。 while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) { if (result == -1) { System.err.println("error in GetMessage"); unhook(); break; } else { User32.INSTANCE.TranslateMessage(msg); User32.INSTANCE.DispatchMessage(msg); } } unhook(); } }
package com.kiritor; import com.sun.jna.Structure; import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.LRESULT; import com.sun.jna.platform.win32.WinDef.WPARAM; import com.sun.jna.platform.win32.WinUser.HOOKPROC; import com.sun.jna.platform.win32.WinUser.POINT; interface LowLevelMouseProc extends HOOKPROC { LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT lParam); } public class MOUSEHOOKSTRUCT extends Structure { public class ByReference extends MOUSEHOOKSTRUCT implements Structure.ByReference { }; public POINT pt; public HWND hwnd; public int wHitTestCode; public ULONG_PTR dwExtraInfo; }ColorFrame.java
package com.kiritor; import java.awt.AWTException; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Menu; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.Rectangle; import java.awt.Robot; import java.awt.SystemTray; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JWindow; public class ColorFrame extends JWindow { private boolean flag= false; public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } private JLabel color16 = null; private JLabel color16Text = null; private JLabel red = null; private JLabel redText = null; private JLabel green = null; private JLabel greenText = null; private JLabel blue = null; private JLabel blueText = null; private JPanel newColor = null; private JLabel newLabel = null; private int xx; private int yy; private boolean isDraging; private TrayIcon trayIcon; private JPanel bgJPanel = null; private ImageIcon bg = new ImageIcon(getClass().getClassLoader() .getResource("images/bg.png")); /** * 剪贴板工作区 */ // 复制到剪贴板的按钮 private JLabel jiantieban = null; private ImageIcon jiantiebanIcon = new ImageIcon(getClass() .getClassLoader().getResource("images/jiantieban.png")); private ImageIcon jiantiebanIcon_entered = new ImageIcon(getClass() .getClassLoader().getResource("images/jiantieban_entered.png")); // 重新取色的按钮 private JLabel getAgain = null; private ImageIcon getAgainIcon = new ImageIcon(getClass().getClassLoader() .getResource("images/getagain.png")); private ImageIcon getAgainIcon_entered = new ImageIcon(getClass() .getClassLoader().getResource("images/getagain_entered.png")); // 取消操作的按钮 private JLabel cancel = null; private ImageIcon cancelIcon = new ImageIcon(getClass().getClassLoader() .getResource("images/cancel.png")); private ImageIcon cancel_entered = new ImageIcon(getClass() .getClassLoader().getResource("images/cancel_entered.png")); public ColorFrame(){} public ColorFrame(int x,int y) { setLocation(x, y); } public void showUI() { init(); setSize(150, 150); setLayout(null); setAlwaysOnTop(true); addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { requestFocus(); isDraging = true; xx = e.getX(); yy = e.getY(); } public void mouseReleased(MouseEvent e) { isDraging = false; } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub setFlag(true);//窗体内部暂时设置为不能取色 } }); addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { if (isDraging) { int left = getLocation().x; int top = getLocation().y; setLocation(left + e.getX() - xx, top + e.getY() - yy); } } }); setVisible(true); // // TestEvent t = new TestEvent(this); // t.setLayout(null); // t.add(bgJPanel); // this.getContentPane().add( t); add(bgJPanel); //setLocationRelativeTo(null); } public void init() { bgJPanel = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(bg.getImage(), 0, 0, null); this.setOpaque(false); } }; bgJPanel.setOpaque(false); bgJPanel.setLayout(null); bgJPanel.setBounds(0, 0, 150, 150); newColor = new JPanel(); newLabel = new JLabel("颜色"); newLabel.setFont(new Font("华文楷体", 0, 12)); newLabel.setBounds(30, 10, 30, 20); newColor.setBounds(20, 35, 50, 50); newColor.setBackground(Color.gray); red = new JLabel("Red:"); red.setFont(new Font("华文楷体", 0, 12)); red.setBounds(80, 35, 30, 10); redText = new JLabel("sdf"); redText.setFont(new Font("华文楷体", 0, 12)); redText.setBounds(115, 35, 30, 10); green = new JLabel("Green:"); green.setFont(new Font("华文楷体", 0, 12)); green.setBounds(80, 53, 40, 10); greenText = new JLabel("sdf"); greenText.setFont(new Font("华文楷体", 0, 12)); greenText.setBounds(115, 53, 30, 10); blue = new JLabel("Blue:"); blue.setFont(new Font("华文楷体", 0, 12)); blue.setBounds(80, 71, 30, 10); blueText = new JLabel("sdf"); blueText.setFont(new Font("华文楷体", 0, 12)); blueText.setBounds(115, 71, 30, 10); color16 = new JLabel("十六进制:"); color16.setBounds(20, 95, 45, 10); color16.setFont(new Font("华文楷体", 0, 10)); color16Text = new JLabel("#aaaa"); color16Text.setBounds(70, 95, 60, 10); color16Text.setFont(new Font("华文楷体", 0, 12)); color16Text.setForeground(Color.red); /** 工作区按钮初始化的地方 */ //剪贴板操作 jiantieban = new JLabel(jiantiebanIcon_entered); jiantieban.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub setClipboard(color16Text.getText()); } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub jiantieban.setIcon(jiantiebanIcon); setFlag(true); } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub jiantieban.setIcon(jiantiebanIcon_entered); } }); jiantieban.setBounds(10, 115, 40, 20); //重新拾取操作 getAgain = new JLabel(getAgainIcon_entered); getAgain.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub setFlag(false); setVisible(false); } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub getAgain.setIcon(getAgainIcon); } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub getAgain.setIcon(getAgainIcon_entered); } }); getAgain.setBounds(55, 115, 40, 20); //取消操作按钮的操作 cancel = new JLabel(cancel_entered); cancel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { setVisible(false); } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub cancel.setIcon(cancelIcon); } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub cancel.setIcon(cancel_entered); } }); cancel.setBounds(100, 115, 40, 20); bgJPanel.add(newLabel); bgJPanel.add(newColor); bgJPanel.add(red); bgJPanel.add(redText); bgJPanel.add(green); bgJPanel.add(greenText); bgJPanel.add(blue); bgJPanel.add(blueText); bgJPanel.add(color16); bgJPanel.add(color16Text); bgJPanel.add(jiantieban); bgJPanel.add(getAgain); bgJPanel.add(cancel); } public JPanel getNewColor() { return newColor; } public void setNewColor(JPanel newColor) { this.newColor = newColor; } public JLabel getColor16Text() { return color16Text; } public void setColor16Text(JLabel color16Text) { this.color16Text = color16Text; } public JLabel getRedText() { return redText; } public void setRedText(JLabel redText) { this.redText = redText; } public JLabel getGreenText() { return greenText; } public void setGreenText(JLabel greenText) { this.greenText = greenText; } public JLabel getBlueText() { return blueText; } public void setBlueText(JLabel blueText) { this.blueText = blueText; } public void getColor(String[] args) { new MouseLLHook(this).getColor(null); } public static void main(String[] args) { ColorFrame colorFrame = new ColorFrame(); colorFrame.showUI(); MouseLLHook hook = new MouseLLHook(colorFrame); hook.getColor(null); } /*复制到剪贴板中去*/ public void setClipboard(String str) { StringSelection ss = new StringSelection(str); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); } }由于一些图片文件丢失,界面效果图就不贴了!
该程序要用到的jar包
http://download.csdn.net/detail/kiritor/5115277