13行代码使用JAVA去除Win下任意软件标题栏

项目需求将VNC Viewer的标题栏隐藏,也就是将一个远程桌面视口无边框地嵌入屏幕规定区域之中,之前的做法是将VNC Viewer放到屏幕最顶部,设置其XY坐标为负,将标题栏隐藏到视界之外。这样做既不美观,也容易被触摸屏或者鼠标给拖下来。 可以使用下面这些代码,将任意软件的标题栏隐藏(前提是32位系统,32位应用): ``` package com.newflypig.win32; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HWND; public class DeleteWindowTitle { public static void main(String[] args) { HWND hWnd = User32.INSTANCE.FindWindow(null, "HMI - VNC Viewer"); System.out.println(hWnd == null); if(hWnd != null){ User32.INSTANCE.ShowWindow(hWnd, 1); int style = User32.INSTANCE.GetWindowLong(hWnd, User32.GWL_STYLE); style = style & ~User32.WS_CAPTION & ~User32.WS_SYSMENU & ~User32.WS_SIZEBOX; User32.INSTANCE.SetWindowLong(hWnd, User32.GWL_STYLE, style); User32.INSTANCE.SetWindowPos(hWnd, new HWND(new Pointer(-1)), 100, 100, 800, 600, 0x0040 | 0x0020); } } } ``` 依赖项:jna gradle配置文件: ``` apply plugin: "java" apply plugin: "war" apply plugin: "eclipse" sourceCompatibility = 1.8 version = '1.0' ext{ jna_version = "4.2.2" } configurations { provided } sourceSets { main.compileClasspath += configurations.provided test.compileClasspath += configurations.provided test.runtimeClasspath += configurations.provided } repositories { maven {url "http://maven.aliyun.com/nexus/content/groups/public" } } dependencies { compile( "net.java.dev.jna:jna:${jna_version}", "net.java.dev.jna:jna-platform:${jna_version}", ) } eclipse.classpath.plusConfigurations += [configurations.provided] ```

使用JAVA发送UDP报文实现WOL远程唤醒

WOL简介

远程开机也被称为远程唤醒技术(Wake on Lan: WOL),是指可以通过局域网、互联网或者通讯网实现远程开机,无论目标主机离用户有多远、处于什么位置,只要其与发送命令主机可以通信,就能够被随时启动,该技术被现在的大多数主板与网卡所支持。
使用之前,请确保打开网卡的远程唤醒功能:

实现原理

局域网内的任意一台PC可通过发送魔术包的方式远程唤醒目标主机,魔术包是用16进制表示的数据包,它由固定的前缀数据以及固定重复次数的目标主机MAC地址所组成。所谓固定前缀数据即6对“FF”,所谓固定重复次数即16次,也就是说魔术包是由12个“F”加重复16次的主机MAC地址组成,例如本文所用试验机的MAC地址为“74-86-7A-71-D5-0A”,所以使该机远程开机的魔术包为:

0xFFFFFFFFFFFF74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A74867A71D50A

在Windows系统中,主机的MAC地址可以通过在命令窗口中输入“ipconfig -all”命令查看。

编码与调试

首先下载标准版的 MagicPacket 输入目标主机的MAC地址,观察是否能顺利唤醒关机状态的目标主机。
如果一切正常,下载网络包嗅探工具smsniff,运行后,紧接着再用MagicPacket发送一次报文,这时候可以通过smsniff观察这次报文的结构。

然后我们编写JAVA代码,直至模仿出一样的报文,即可实现在JAVA代码中唤醒目标主机,今后可以移植到Android,树莓派等环境,实现更丰富的功能。代码如下:

package com.newflypig.wol;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;

public class WOL {
    /**
     * main方法,发送UDP广播,实现远程开机,目标计算机的MAC地址为:28D2443568A7
     */
    public static void main(String[] args) {
        String ip = "255.255.255.255";//广播IP地址
        String mac= "FCAA14D0C108";
        int port = 2304;//端口号
        //魔术包数据
        String magicPackage = "0xFFFFFFFFFFFF";
        for(int i = 0; i < 16; i++){
            magicPackage += mac;
        }
        //转换为2进制的魔术包数据
        byte[] command = hexToBinary(magicPackage);
        for(Byte b : command){
            System.out.print(b.byteValue());
        }

        //广播魔术包
        try {
            //1.获取ip地址
            InetAddress address = InetAddress.getByName(ip);
            //2.获取广播socket
            MulticastSocket socket = new MulticastSocket(port);
            //3.封装数据包
            /*public DatagramPacket(byte[] buf,int length
             *      ,InetAddress address
             *      ,int port)
             * buf:缓存的命令
             * length:每次发送的数据字节数,该值必须小于等于buf的大小
             * address:广播地址
             * port:广播端口
            */
            DatagramPacket packet = new DatagramPacket(command, command.length-1, address, port);
            //4.发送数据
            socket.send(packet);
            //5.关闭socket
            socket.close();
        } catch (UnknownHostException e) {
            //Ip地址错误时候抛出的异常
            e.printStackTrace();
        } catch (IOException e) {
            //获取socket失败时候抛出的异常
            e.printStackTrace();
        }
    }

    /**
     * 将16进制字符串转换为用byte数组表示的二进制形式
     * @param hexString:16进制字符串
     * @return:用byte数组表示的十六进制数
     */
    private static byte[] hexToBinary(String hexString){
        //1.定义变量:用于存储转换结果的数组
        byte[] result = new byte[hexString.length()/2];

        //2.去除字符串中的16进制标识"0X"并将所有字母转换为大写
        hexString = hexString.toUpperCase().replace("0X", "");

        //3.开始转换
        //3.1.定义两个临时存储数据的变量
        char tmp1 = '0';
        char tmp2 = '0';
        //3.2.开始转换,将每两个十六进制数放进一个byte变量中
        for(int i = 0; i < hexString.length(); i += 2){
            tmp1 = hexString.charAt(i);
            tmp2 = hexString.charAt(i+1);
            result[i/2] = (byte)((hexToDec(tmp1)<<4)|(hexToDec(tmp2)));
        }
        return result;
    }

    /**
     * 用于将16进制的单个字符映射到10进制的方法
     * @param c:16进制数的一个字符
     * @return:对应的十进制数
     */
    private static byte hexToDec(char c){
        int index = "0123456789ABCDEF".indexOf(c);
        return (byte)index;
    }
}

使用JNA寻找第三方窗体程序,并移动到指定位置

使用JNA寻找第三方窗体程序,并移动到指定位置 需求场景:在Windows环境下,查找标题为xxx的窗体,并将其固定在指定位置,如果查询不到则打开指定路径下的该程序。 使用JNA可以方便的调用平台相关的函数,这让JAVA能够通过JNA调用很多JVM沙盒以外的函数。 首先引入JNA的JAR包,这里使用gradle快速引入: ``` apply plugin: "java" apply plugin: "eclipse" sourceCompatibility = 1.8 version = '1.0' ext{ jna_version = "4.2.2" } sourceSets { main{ resources.srcDirs = ["src"] //引入资源文件,打包时才会将配置文件植入war文件 } } repositories { maven {url "https://maven.aliyun.com/nexus/content/groups/public" } //maven {url "https://uk.maven.org/maven2" } //mavenLocal() //mavenCentral() //jcenter() } dependencies { compile( "net.java.dev.jna:jna:${jna_version}", "net.java.dev.jna:jna-platform:${jna_version}" ) } task copyJars(type:Copy) { from configurations.runtime into 'dist' // 目标位置 } ``` JAVA中使用以下代码寻找窗体,并固定位置 ``` HWND hWnd = User32.INSTANCE.FindWindow(title,null); //使用标题寻找窗体 HWND hWnd = User32.INSTANCE.FindWindow(null,class); //使用类名寻找窗体 User32.INSTANCE.ShowWindow(hWnd, 1); //显示窗体,适用于最小化,或者处于z轴后面的窗体置前 //获取位置 WINDOWPLACEMENT wPlacement = new WINDOWPLACEMENT(); User32.INSTANCE.GetWindowPlacement(hWnd, wPlacement); if(x != wPlacement.rcNormalPosition.left || y != wPlacement.rcNormalPosition.top){ //控制位置 User32.INSTANCE.SetWindowPos(hWnd, new HWND(new Pointer(-1)), x, y, 0, 0, 0x0040 | 0x0001); } //如果并没有检索到当前窗体,则需要启动程序,有的程序可能需要参数,可直接在后面添加字符串 Runtime.getRuntime().exec("cmd /c \"" + filePath + "\""); ```

丁丁生于 1987.07.01 ,30岁,英文ID:newflydd

  • 现居住地 江苏 ● 泰州 ● 姜堰
  • 创建了 Jblog 开源博客系统
  • 坚持十余年的 独立博客 作者
  • 大学本科毕业后就职于 中国电信江苏泰州分公司,前两年从事Oracle数据库DBA工作,两年后公司精简技术人员,被安排到农村担任支局长(其本质是搞销售),于2016年因志向不合从国企辞职,在小城镇找了一份程序员的工作。
  • Git OSChina 上积极参与开源社区