Java语言的Hook实现
引言:最近在玩完美时空的诛仙Online(不知道这里有没人有共同爱好的),这个游戏每晚七点会出现一个任务“新科试炼”。这个任务简单地说就是做选择题,范围小到柴米油盐,大到世界大千,所以多玩的YY上出现一个频道叫“诛仙答题频道”,这个频道会即时为玩家提供正确答案,所以当大家都上YY的时候,最终出来的成绩的高低并不取决于你的知识面,而是取决你家的网速及你的反应速度(答题越早,所获得的成绩越高)。我家的网速很好,可惜我的反应速度一般,所以从来没上过一次前十,因为每次听说YY上的答案后还要移动鼠标去点击相应的答案,这个过程平均需要0.5秒,这无疑是我成绩不高的根本所在。所以我想到了通过按下键盘上的某些按键实现将鼠标移动到指定位置并模拟鼠标键按下事件(以下简称模拟)。也许你会问:这还不简单,直接加个KeyListener不就完了?但是你想过没有,在模拟的时候,窗口的焦点是在游戏窗口,而不是Java程序的窗口(甚至连窗口都没有),所以我们需要一个监听所有进程的接口,这就是我接下要说的“Hook(钩子)”(了解外挂制作的人应该知道是什么东西,没看过?百度之)。不废话,进入正题:
首先,编写之前,我们要使用到一个第三方类库,它实现的功能就是让你直接调用API,而将对Window的调用交给它处理,下载地址是:http://feeling.sourceforge.net/
将包下载完后解压,并创建项目,结构如左图,lib下的三个dll文件要拷到window/system32下,下面就是编码了:首先我们定义一个抽象类来拦截键盘事件,如下:
- import org.sf.feeling.swt.win32.extension.hook.data.HookData;
- import org.sf.feeling.swt.win32.extension.hook.data.KeyboardHookData;
- import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;
- public abstract class KeyboardHookEventListener implements HookEventListener{
- public void acceptHookData(HookData arg0) {
- KeyboardHookData khd = ((KeyboardHookData) arg0);
- {
- if(khd.getTransitionState()) //处理按下事件
- {
- doPress(khd.getWParam());
- }
- else
- {
- doReleased(khd.getWParam()); //处理释放事件
- }
- }
- }
- public abstract void doPress(int keyNum);
- public abstract void doReleased(int keyNum);
- }
import org.sf.feeling.swt.win32.extension.hook.data.HookData;
import org.sf.feeling.swt.win32.extension.hook.data.KeyboardHookData;
import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;public abstract class KeyboardHookEventListener implements HookEventListener{public void acceptHookData(HookData arg0) {KeyboardHookData khd = ((KeyboardHookData) arg0);{if(khd.getTransitionState()) //处理按下事件{doPress(khd.getWParam());}else{doReleased(khd.getWParam()); //处理释放事件}}}public abstract void doPress(int keyNum);public abstract void doReleased(int keyNum);
}
接着再定义一个抽象类到拦截鼠标事件,如下:
- import org.sf.feeling.swt.win32.extension.hook.data.HookData;
- import org.sf.feeling.swt.win32.extension.hook.data.MouseHookData;
- import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;
- public abstract class MouseHookEventListener implements HookEventListener{
- public void acceptHookData(HookData hookData) {
- int x=((MouseHookData) hookData).getPointX();
- int y=((MouseHookData) hookData).getPointY();
- switch(hookData.getWParam())
- {
- case 513:
- doLeftPressed(x,y);
- break;
- case 514:
- doLeftReleased(x,y);
- break;
- case 516:
- doRightPressed(x,y);
- break;
- case 517:
- doRightReleased(x,y);
- break;
- case 519:
- doMiddlePressed(x,y);
- break;
- case 520:
- doMiddleReleased(x,y);
- break;
- default:
- }
- }
- protected abstract void doLeftPressed(int x,int y);
- protected abstract void doLeftReleased(int x,int y);
- protected abstract void doRightPressed(int x,int y);
- protected abstract void doRightReleased(int x,int y);
- protected abstract void doMiddlePressed(int x,int y);
- protected abstract void doMiddleReleased(int x,int y);
- }
import org.sf.feeling.swt.win32.extension.hook.data.HookData;
import org.sf.feeling.swt.win32.extension.hook.data.MouseHookData;
import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;public abstract class MouseHookEventListener implements HookEventListener{public void acceptHookData(HookData hookData) {int x=((MouseHookData) hookData).getPointX();int y=((MouseHookData) hookData).getPointY();switch(hookData.getWParam()){case 513:doLeftPressed(x,y);break;case 514:doLeftReleased(x,y);break;case 516:doRightPressed(x,y);break;case 517:doRightReleased(x,y);break;case 519:doMiddlePressed(x,y);break;case 520:doMiddleReleased(x,y);break;default:}}protected abstract void doLeftPressed(int x,int y);protected abstract void doLeftReleased(int x,int y);protected abstract void doRightPressed(int x,int y);protected abstract void doRightReleased(int x,int y);protected abstract void doMiddlePressed(int x,int y);protected abstract void doMiddleReleased(int x,int y);
}
至此,我们的项目底层架构已经完成,下面就是业务流程的控制问题了,在贴上我的代码之前,我觉得有必要先做一下诛仙答题跟项目的介绍(又要废话了,顺便帮老池免费作下广告,遇上我是他的福分,^-^)。
答题是这样的:首先,诛仙这个游戏是支持窗口化(且提供几个固定窗口大小供选择),而其中的答题窗口就是窗口中的窗口了(可拖动的)。7点,答题系统开启,每个玩家可选择进入答题窗口,等下一分钟才真正开始,这一分钟中,页面会显示出3个幸运星,但是没有题目........经过一番分析,可以确定用户要输入的有:当前使用的窗口大小及幸运星的位置(幸运星跟选项的位置不固定的,幸运星一确定,选项的位置也就知道了)。以下是关于这个业务的代码:
定义一个特定的鼠标事件响应,如下:
- import java.awt.Dimension;
- import java.util.LinkedList;
- import java.util.List;
- public class MyMouseHookEventListener extends MouseHookEventListener {
- private Dimension zeroDimension;
- private List<Dimension> dimensions=new LinkedList<Dimension>();
- private boolean needFetchZeroDimension=false;
- private String currentOffsetSeries="";
- public void resetZeroDimension()
- {
- this.needFetchZeroDimension=true;
- }
- public void resetDimensions(String dimensionSeries)
- {
- this.dimensions.clear();
- String[] dimStrs=dimensionSeries.split(",");
- for(int i=0;dimStrs!=null&&i<dimStrs.length/2;i++)
- {
- int width=Integer.parseInt(dimStrs[i*2])+(int)zeroDimension.getWidth();
- int height=Integer.parseInt(dimStrs[i*2+1])+(int)zeroDimension.getHeight();
- dimensions.add(new Dimension(width,height));
- }
- }
- public String getDimensionSeries()
- {
- String dimSeries="";
- for(Dimension dim:this.dimensions)
- {
- dimSeries=dimSeries+","+(int)(dim.getWidth()-zeroDimension.getWidth())+","+(int)(dim.getHeight()-zeroDimension.getHeight());
- }
- if(dimSeries.length()>0)
- {
- dimSeries=dimSeries.substring(1);
- }
- return dimSeries;
- }
- @Override
- protected void doLeftPressed(int x, int y) {}
- @Override
- protected void doLeftReleased(int x, int y) {}
- @Override
- protected void doMiddlePressed(int x, int y) {}
- @Override
- protected void doMiddleReleased(int x, int y) {}
- @Override
- protected void doRightPressed(int x, int y) {
- if(this.needFetchZeroDimension)
- {
- this.zeroDimension=new Dimension(x,y);
- resetDimensions(currentOffsetSeries);
- this.needFetchZeroDimension=false;
- System.out.println("幸运星位置已获取,关闭重置模式,\r\n现在你可以使用小键盘上的12345来实现鼠标事件模拟,如果你需要重新选择请按F11");
- }
- }
- @Override
- protected void doRightReleased(int x, int y) {}
- public void setCurrentOffsetSeries(String currentOffsetSeries) {
- this.currentOffsetSeries = currentOffsetSeries;
- }
- public List<Dimension> getDimensions() {
- return dimensions;
- }
- }
import java.awt.Dimension;
import java.util.LinkedList;
import java.util.List;public class MyMouseHookEventListener extends MouseHookEventListener {private Dimension zeroDimension;private List<Dimension> dimensions=new LinkedList<Dimension>();private boolean needFetchZeroDimension=false;private String currentOffsetSeries="";public void resetZeroDimension(){this.needFetchZeroDimension=true;}public void resetDimensions(String dimensionSeries){this.dimensions.clear();String[] dimStrs=dimensionSeries.split(",");for(int i=0;dimStrs!=null&&i<dimStrs.length/2;i++){int width=Integer.parseInt(dimStrs[i*2])+(int)zeroDimension.getWidth();int height=Integer.parseInt(dimStrs[i*2+1])+(int)zeroDimension.getHeight();dimensions.add(new Dimension(width,height));}}public String getDimensionSeries(){String dimSeries="";for(Dimension dim:this.dimensions){dimSeries=dimSeries+","+(int)(dim.getWidth()-zeroDimension.getWidth())+","+(int)(dim.getHeight()-zeroDimension.getHeight());}if(dimSeries.length()>0){dimSeries=dimSeries.substring(1);}return dimSeries;}@Overrideprotected void doLeftPressed(int x, int y) {}@Overrideprotected void doLeftReleased(int x, int y) {}@Overrideprotected void doMiddlePressed(int x, int y) {}@Overrideprotected void doMiddleReleased(int x, int y) {}@Overrideprotected void doRightPressed(int x, int y) {if(this.needFetchZeroDimension){this.zeroDimension=new Dimension(x,y);resetDimensions(currentOffsetSeries);this.needFetchZeroDimension=false;System.out.println("幸运星位置已获取,关闭重置模式,\r\n现在你可以使用小键盘上的12345来实现鼠标事件模拟,如果你需要重新选择请按F11");}}@Overrideprotected void doRightReleased(int x, int y) {}public void setCurrentOffsetSeries(String currentOffsetSeries) {this.currentOffsetSeries = currentOffsetSeries;}public List<Dimension> getDimensions() {return dimensions;}}
再定义一个运行类:
- import java.awt.AWTException;
- import java.awt.Robot;
- import java.awt.Toolkit;
- import java.awt.datatransfer.Clipboard;
- import java.awt.datatransfer.StringSelection;
- import java.awt.datatransfer.Transferable;
- import java.awt.event.InputEvent;
- import org.sf.feeling.swt.win32.extension.hook.Hook;
- public class ZhuXianSwifter {
- public static final int NUM_1=97;
- public static final int NUM_2=98;
- public static final int NUM_3=99;
- public static final int NUM_4=100;
- public static final int NUM_5=101;
- public static final int F_11=122;
- public static final int F_12=123;
- private static final String OFFSET_SERIES_640_480="-125,84,-125,107,-125,130,-125,152,44,0,20,0,0,0";
- private static final String OFFSET_SERIES_800_600="-156,105,-156,134,-156,163,-156,190,55,0,25,0,0,0";
- private static final String OFFSET_SERIES_1024_768="-200,138,-200,172,-200,211,-200,248,70,0,32,0,0,0";
- /**
- * 使用说明:
- * 1、启动后先选择所使用的分辨率,目前只支持640*480,800*600,1024*768;
- * 2、然后使用鼠标右键点击一下试炼答题窗口的第一个幸运星的中心点即可;
- * 3、使用小键盘的1234选择答案,使用5点星星(第一个使用完会自动用第二个),
- * 4、只支持命令行模式
- * 5、F11为取坐标模式,按F11开始,再次按F11结束,并将零坐标跟之前的偏移坐标复制到系统剪贴板
- * 6、按F12退出程序
- * @throws AWTException
- */
- public static void main(String[] args) throws AWTException {
- /*注册鼠标Hook*/
- final MyMouseHookEventListener mouseListener=new MyMouseHookEventListener();
- Hook.MOUSE.addListener(mouseListener);
- Hook.MOUSE.install();
- /*系统剪贴板*/
- final Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
- final Robot robot=new Robot();
- /*键盘监听器*/
- final KeyboardHookEventListener keyboardListener=new KeyboardHookEventListener(){
- private boolean haveChooseMode=false;
- private int count=0;
- @Override
- public void doPress(int keyNum) {
- String mode="";
- if(keyNum==F_12)
- {
- if(!mouseListener.getDimensionSeries().equals(""))
- {
- System.out.println("内容已经复制到系统剪贴板");
- Transferable text = new StringSelection(mouseListener.getDimensionSeries());
- systemClipboard.setContents(text,null);
- }
- System.out.println("------程序退出------");
- System.exit(0);
- }
- else if(keyNum==F_11)
- {
- haveChooseMode=false;
- count=0;
- System.out.println("启动重置模式");
- printChooseMode();
- }
- else
- {
- if(haveChooseMode==false)
- {
- switch(keyNum)
- {
- case NUM_1:
- mode="640*480";
- mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_640_480);
- break;
- case NUM_2:
- mode="800*600";
- mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_800_600);
- break;
- case NUM_3:
- mode="1024*768";
- mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_1024_768);
- break;
- default:
- System.out.println("请重新选择:");
- printChooseMode();
- return;
- }
- System.out.println("您选择了"+mode+"分辨率模式");
- haveChooseMode=true;
- mouseListener.resetZeroDimension();
- printFetchZeroCoordinate();
- }
- else
- {
- switch (keyNum) {
- case NUM_1:
- robot.mouseMove((int)mouseListener.getDimensions().get(0).getWidth(),(int)mouseListener.getDimensions().get(0).getHeight());
- robot.mousePress(InputEvent.BUTTON1_MASK);
- robot.mouseRelease(InputEvent.BUTTON1_MASK);
- break;
- case NUM_2:
- robot.mouseMove((int)mouseListener.getDimensions().get(1).getWidth(),(int)mouseListener.getDimensions().get(1).getHeight());
- robot.mousePress(InputEvent.BUTTON1_MASK);
- robot.mouseRelease(InputEvent.BUTTON1_MASK);
- break;
- case NUM_3:
- robot.mouseMove((int)mouseListener.getDimensions().get(2).getWidth(),(int)mouseListener.getDimensions().get(2).getHeight());
- robot.mousePress(InputEvent.BUTTON1_MASK);
- robot.mouseRelease(InputEvent.BUTTON1_MASK);
- break;
- case NUM_4:
- robot.mouseMove((int)mouseListener.getDimensions().get(3).getWidth(),(int)mouseListener.getDimensions().get(3).getHeight());
- robot.mousePress(InputEvent.BUTTON1_MASK);
- robot.mouseRelease(InputEvent.BUTTON1_MASK);
- break;
- case NUM_5:
- robot.mouseMove((int)mouseListener.getDimensions().get(4+count).getWidth(),(int)mouseListener.getDimensions().get(4+count).getHeight());
- robot.mousePress(InputEvent.BUTTON1_MASK);
- robot.mouseRelease(InputEvent.BUTTON1_MASK);
- count++;
- if(count==3)
- {
- count=0;
- }
- break;
- default:
- break;
- }
- }
- }
- }
- @Override
- public void doReleased(int keyNum) {}
- };
- Hook.KEYBOARD.addListener(keyboardListener);
- Hook.KEYBOARD.install(); // 註冊事件
- printChooseMode();
- }
- private static void printChooseMode()
- {
- System.out.println("请选择窗口大小:");
- System.out.println("NUM1:640*480");
- System.out.println("NUM2:800*600");
- System.out.println("NUM3:1024*768");
- }
- private static void printFetchZeroCoordinate()
- {
- System.out.println("请在第一个幸运星的中心上点击鼠标右键");
- }
- }
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;import org.sf.feeling.swt.win32.extension.hook.Hook;public class ZhuXianSwifter {public static final int NUM_1=97;public static final int NUM_2=98;public static final int NUM_3=99;public static final int NUM_4=100;public static final int NUM_5=101;public static final int F_11=122;public static final int F_12=123;private static final String OFFSET_SERIES_640_480="-125,84,-125,107,-125,130,-125,152,44,0,20,0,0,0";private static final String OFFSET_SERIES_800_600="-156,105,-156,134,-156,163,-156,190,55,0,25,0,0,0";private static final String OFFSET_SERIES_1024_768="-200,138,-200,172,-200,211,-200,248,70,0,32,0,0,0";/*** 使用说明:* 1、启动后先选择所使用的分辨率,目前只支持640*480,800*600,1024*768;* 2、然后使用鼠标右键点击一下试炼答题窗口的第一个幸运星的中心点即可;* 3、使用小键盘的1234选择答案,使用5点星星(第一个使用完会自动用第二个),* 4、只支持命令行模式* 5、F11为取坐标模式,按F11开始,再次按F11结束,并将零坐标跟之前的偏移坐标复制到系统剪贴板* 6、按F12退出程序* @throws AWTException */public static void main(String[] args) throws AWTException {/*注册鼠标Hook*/final MyMouseHookEventListener mouseListener=new MyMouseHookEventListener();Hook.MOUSE.addListener(mouseListener);Hook.MOUSE.install();/*系统剪贴板*/final Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); final Robot robot=new Robot();/*键盘监听器*/final KeyboardHookEventListener keyboardListener=new KeyboardHookEventListener(){private boolean haveChooseMode=false;private int count=0;@Overridepublic void doPress(int keyNum) {String mode="";if(keyNum==F_12){if(!mouseListener.getDimensionSeries().equals("")){System.out.println("内容已经复制到系统剪贴板");Transferable text = new StringSelection(mouseListener.getDimensionSeries());systemClipboard.setContents(text,null);}System.out.println("------程序退出------");System.exit(0);}else if(keyNum==F_11){haveChooseMode=false;count=0;System.out.println("启动重置模式");printChooseMode();}else{if(haveChooseMode==false){switch(keyNum){case NUM_1:mode="640*480";mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_640_480);break;case NUM_2:mode="800*600";mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_800_600);break;case NUM_3:mode="1024*768";mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_1024_768);break;default:System.out.println("请重新选择:");printChooseMode();return;}System.out.println("您选择了"+mode+"分辨率模式");haveChooseMode=true;mouseListener.resetZeroDimension();printFetchZeroCoordinate();}else{switch (keyNum) {case NUM_1:robot.mouseMove((int)mouseListener.getDimensions().get(0).getWidth(),(int)mouseListener.getDimensions().get(0).getHeight());robot.mousePress(InputEvent.BUTTON1_MASK);robot.mouseRelease(InputEvent.BUTTON1_MASK);break;case NUM_2:robot.mouseMove((int)mouseListener.getDimensions().get(1).getWidth(),(int)mouseListener.getDimensions().get(1).getHeight());robot.mousePress(InputEvent.BUTTON1_MASK);robot.mouseRelease(InputEvent.BUTTON1_MASK);break;case NUM_3:robot.mouseMove((int)mouseListener.getDimensions().get(2).getWidth(),(int)mouseListener.getDimensions().get(2).getHeight());robot.mousePress(InputEvent.BUTTON1_MASK);robot.mouseRelease(InputEvent.BUTTON1_MASK);break;case NUM_4:robot.mouseMove((int)mouseListener.getDimensions().get(3).getWidth(),(int)mouseListener.getDimensions().get(3).getHeight());robot.mousePress(InputEvent.BUTTON1_MASK);robot.mouseRelease(InputEvent.BUTTON1_MASK);break;case NUM_5:robot.mouseMove((int)mouseListener.getDimensions().get(4+count).getWidth(),(int)mouseListener.getDimensions().get(4+count).getHeight());robot.mousePress(InputEvent.BUTTON1_MASK);robot.mouseRelease(InputEvent.BUTTON1_MASK);count++;if(count==3){count=0;}break;default:break;}}}}@Overridepublic void doReleased(int keyNum) {}};Hook.KEYBOARD.addListener(keyboardListener);Hook.KEYBOARD.install(); // 註冊事件printChooseMode();}private static void printChooseMode(){System.out.println("请选择窗口大小:");System.out.println("NUM1:640*480");System.out.println("NUM2:800*600");System.out.println("NUM3:1024*768");}private static void printFetchZeroCoordinate(){System.out.println("请在第一个幸运星的中心上点击鼠标右键");}}
以上就是本项目的所以代码,运行时要先按小键盘的1/2/3选择使用的窗口大小,然后在第一个幸运星的中心点击下右键鼠标就可以了,之后你就可以用小键盘的1/2/3/4/5(5是幸运星)来选择你的答案了。
Java语言的Hook实现相关推荐
- 开源公司黄页 关于/ 阿里巴巴的50款开源软件[大部分为Java语言]
服务框架 Dubbo Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remot ...
- Java语言中的数据类型
Java语言是一种强调数据类型的语言,在声明任何变量时,必须将该变量定义为一种数据类型. Java中的数据类型包括基本类型和对象类型,基本类型总共有8种,其中4种整形.1种字符型.2种浮点型.1种布尔 ...
- java语言环境变量_JAVA语言环境变量的设置教程
本文主要向大家介绍了JAVA语言环境变量的设置教程,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. 安装JDK到目录,我这里是C:\Java 右键点击计算机属性 在系统变量里面建 JA ...
- java语言的实现机制_JAVA语言之Java NIO的工作机制和实现原理介绍
本文主要向大家介绍了JAVA语言之Java NIO的工作机制和实现原理介绍,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. 前言 本文只简单介绍NIO的原理实现和基本工作流程 I/O和 ...
- Java语言的基础知识9
第十一章(线程) 1.通过String name=Thread.currentThread().getName();来获取当前线程的名称. 2.多次启动一个线程或者启动一个已经运行的线程是非法的,会抛 ...
- 重塑云上的 Java 语言
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 Photo @ Toa Heftiba 文 |郁磊 音乐无国界, ...
- 四川大学java试题_四川大学2013年计算机(软件)学院Java语言程序设计期末考试试题B卷...
四川大学期末考试试题(闭卷) (2013 -2014学年第1学期) 课程号:课程名称: Java语言程序设计(B 卷)任课教师: 适用专业年级:学号:姓名: 一.单项选择题(本大题共20小题,每小题2 ...
- “坑爹”排行榜:Java语言最违反常识的功能点TOP 10
来自:互联网全栈架构 作为一门面向对象的编程语言,Java凭借其简单易用.功能强大的特点受到了广大编程爱好者的青睐,伴随着开源社区的推波助澜,Java语言更是席卷全球,势不可挡,在世界各地都有Java ...
- Java语言中的生僻知识
最近有一首名叫<生僻字>的流行歌曲火遍大江南北,创作者给佶屈聱牙的生僻字,配上了优美明快的旋律,竟然让歌曲变得琅琅上口.悦耳动听起来,平时不太常见的拒人于千里之外的这些汉字也不再那么陌生, ...
- java语言仅支持单重继承_java语言程序设计基础篇习题_复习题_第十一章
java语言程序设计基础篇习题_复习题_第十一章 11.1 下面说法是真是假?一个子类是父类的子集. 11.2 使用什么关键字来定义一个子类 11.3 什么是单一继承?什么是多重继承?java支持多重 ...
最新文章
- c语言中手机系统,一种手机课堂C语言编程系统的制作方法
- LeetCode686 Repeated String Match(字符串匹配)
- (chap4 IP协议) CIDR协议
- A*算法的c++实现+opencv动态显示
- API数据安全知多少【知识篇】
- vue动态点击切换css样式且子元素动态显示和隐藏
- hibernate自带的注解和jpa注解的冠希
- canvas 两个圆相交重叠区域颜色填充_「译」Canvas中的环绕规则 -Winding rules in Canvas...
- 经验分享 | 我是如何从小白到收获几个不错的offer!
- [QT][待解决问题]对话框ui载入卡顿问题
- 极客空间-MySQL实战45天-第三天
- matlab rloess,比较ggplot loess拟合和loess拟合函数
- 常见排序算法时间复杂度和空间复杂度
- 2021年安全员-C证考试及安全员-C证考试技巧
- Linux开放1521端口允许网络连接Oracle Listene
- J.P. Morgan Executes and Clears CDS and IRS Trades Via Bloomberg Professional
- 【明哥版】2020最新Android Studio Win10 安装教程
- 微信小技能:微信收藏置顶变身程序员的日常待办事项和月计划(跟我一起做计划)
- 衡量数据集中趋势的方法
- 电磁场的变化方式 工程电磁场 P27
热门文章
- Mysql 主从复制简易操作指南
- The Art of Prolog:Advanced Programming Techniques【译文】
- sudo使用之实现权限分配
- Zebra命令模式分析
- 快速H.264编码算法的研究及实现
- fatal error: highgui.h: No such file or directory
- Linux虚拟文件系统(内核初始化二)
- oracle服务器结构01
- TypeError: Fetch argument has invalid type class ‘numpy.float32‘, must be a string or Tensor
- 【快速幂】a^b%p问题