首页 > 系统相关 >Windows剪贴板小工具

Windows剪贴板小工具

时间:2022-10-09 17:11:24浏览次数:80  
标签:输出 剪贴板 窗口 Windows 方格 keybd 工具 event

前情提要:有时候写点东西会经常用到奇奇怪怪的符号,每次去找很麻烦。想写个小工具,把最近用过的符号列成一排,用的时候直接点就行。写得很怪,学习用的little toy罢了

GitHub链接

1. 环境

操作系统:Windows10 64位

IDE:Clion+IDEA

管理工具:Maven

UI:JavaFX

2. 分析

梦想中的界面:屏幕上一长条小方格,悬浮在屏幕顶层,点一下就能打印出对应内容。

进阶版界面:有固定功能,保存常用字符。可以拖动调节位置。但不是很想搞UI,能用就行。

拆分一下。首先是读入字符,可以借剪贴板用用。顺带一提win剪贴板有类似功能,win+v启动,但连续使用不是很方便。

那么大致过程就是:读取并监视剪贴板,显示到方格内。点击方格,读取光标位置,将内容输出。

读取并监视剪贴板java有相关API,希望有个回调函数什么的能及时把新内容显示上去。

显示方面,介于小方格太多找起来还是很麻烦,所以一共弄十来个差不多了,先假设12个。设置右键固定,固定内容不会被新内容覆盖。但如果全是固定内容就无法接受新的字符了。遇到困难,摆大烂。满就满,自己手动删除去吧。

然后是小方格内怎么显示,因为一开始是给特殊符号设计的,所以能装下一个字符就可以。其他的能显示多少显示多少。

读取光标位置后输出。有点超出这个程序的管理范围了,肯定要用系统API。换c语言大概方便一点。

 3. 实现

0. 起名  经过长达五分钟的摆烂思考,考虑到含义、和上一个小工具的呼应、同时又形象地描述了它的样子,决定起名为CatBar

1. 结构

工具类是抄的,负责窗口拖动;

Apprun是入口;

GUI是菜单加主体窗口;

ClipBar是那一排方格,方格叫ClipContent。

剪贴板部分管理是ClipManager,里面有棵ClipSortTree,负责找下一个能用的方格。

Priority是方格优先级,用来判断下一个新来的内容放哪比较好。

NativePutText是调用c语言函数的接口,要调的dll文件在resources里,就是libuntitled.dll。

2. C实现将指定内容输出到系统光标处

先进行一些学习。

1. Windows下剪贴板原理

The Clipboard is a holding place on your computer where you can temporarily store data (text, pictures, and so on). When you copy something, your selection is held on the Clipboard, where it remains until you copy something else or shut down your computer.

——Microsoft Support

剪切板操作API:剪切板操作

2. 将剪切板内容输出至光标处

美好的幻想:将内容作为参数传进去,再把指定内容输出到光标处。

找到了一段很有用的代码:发送候选文字到光标所在位置

#include <windows.h>
using namespace std;
int main()
{
    Sleep(3000);
    // 窗口句柄
    HWND wnd;
    // 获得当前激活的窗口句柄
    wnd = GetForegroundWindow();
    // 获取本身的线程ID
    DWORD SelfThreadId = GetCurrentThreadId();
    // 根据窗口句柄获取线程ID
    DWORD ForeThreadId = GetWindowThreadProcessId(wnd, NULL);
    // 附加线程
    AttachThreadInput(ForeThreadId, SelfThreadId, true);
    // 获取具有输入焦点的窗口句柄
    wnd = GetFocus();
    // 取消附加的线程
    AttachThreadInput(ForeThreadId, SelfThreadId, false);
    if (wnd) {
       // 发送消息
        PostMessageA(wnd, WM_CHAR, (WPARAM)'a', 0);
        PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'测', 0);
        PostMessageA(wnd, WM_IME_CHAR, (WPARAM)'试', 0);
    }
    return 0;
}

获取窗口的方式:获取窗口句柄(hwnd)

It's an abstract reference value to a resource, often memory or an open file, or a pipe.

——  What is a Windows Handle?

 线程间共享输入:AttachThreadInput

By using the AttachThreadInput function, a thread can share its input states (such as keyboard states and the current focus window) with another thread. Keyboard and mouse events received by both threads are processed in the order they were received until the threads are detached by calling AttachThreadInput a second time and specifying FALSE for the fAttach parameter.

——Microsoft Support

 输出要用Windows的消息传递机制,也就是进程间通信的一种。

消息机制原理

Windows消息机制应用

跑一跑这个程序,能在我点的地方输出字符,很快乐。现在想要定义个字符串,塞到WPRAM里输出。但普通的字符串不行,要CString,也就是VC里封装好的一个class。但这样一来不知道能不能输出图片什么的,而且也不想配VC的环境,看看有没有其他方法。然后看到了这个东西:

    keybd_event(17,0,0,0);//'ctrl' down
    keybd_event(86,0,0,0);//'v' down
    keybd_event(86,0,KEYEVENTF_KEYUP,0);//'v' up
    keybd_event(17,0,KEYEVENTF_KEYUP,0);

直接模拟键盘按键……那我把要输出的东西先setClipBpard,然后直接模拟按键,岂不是十分简单。感觉,有点低端……但确实好用。

把这个和UI对接了一下,发现一个问题:点击方格的时候焦点移动到了本身窗口上,这时候肯定是没法输出的,JavaFx也没有类似于e.preventDefault()的东西防止获得焦点。所以正常的顺序应该是:记录活跃窗口,点击,获取上一个活跃窗口句柄,输出。有些麻烦,很难不让人想用另一个快捷键:ctrl+Tab……

最后代码变成了这样:

    keybd_event(18,0,0,0);//'alt' down
    keybd_event(9,0,0,0);//'tab' down
    keybd_event(18,0,KEYEVENTF_KEYUP,0);//'alt' up
    keybd_event(9,0,KEYEVENTF_KEYUP,0);//'tab' up
    Sleep(50);
    keybd_event(17,0,0,0);//'ctrl' down
    keybd_event(86,0,0,0);//'v' down
    keybd_event(86,0,KEYEVENTF_KEYUP,0);//'v' up
    keybd_event(17,0,KEYEVENTF_KEYUP,0);
View Code

 中间停那50ms是为了等ctrl+Tab执行完……跑起来达成了目标,就是有时候会有按快捷键出现的选择窗口一闪而过……

以后再说吧……

3. Java调用C函数

本来呢都变成了模拟按键了,按理说Java也可以。但C程序写都写好了,正好学学怎么一起用:

首先写一个接口:

public class NativePutText {
    public static native void putText();
}

 native关键字就是说,我要用其他语言实现这个方法啦。

然后进terminal,执行 javac -h ./ ${PATH} ,注意./后面有空格。

就会获得一个没有什么用的.class和有用的.h,把.h拉出来放到CLion里。再从jdk里找另外四个.h文件:

 

 然后把Java生成的头文件里函数声明复制过来,直接往里写。记得给参数起名。

JNIEXPORT void JNICALL Java_NativePutText_putText
(JNIEnv *j, jclass){
    ...
}

 Build生成dll文件(在debug文件夹里),拉到resources下,完成。

接下来是Java调用dll。

先加载dll,路径用绝对路径:System.load(path);

然后调用就好了:NativePutText.putText();

4. Java操作剪贴板

这里就简单很多了,因为各个框架都有ClipBoard的包可以直接用。首先监听剪贴板:

new com.sun.glass.ui.ClipboardAssistance(com.sun.glass.ui.Clipboard.SYSTEM) {
            @Override
            public void contentChanged() {
               ...
            }
        };

 然后获取剪贴板:

    public static Clipboard clipboard = Clipboard.getSystemClipboard();

最后get呀set呀调用一下。getContent可以自定内容类型,支持PLAIN_TEXT、HTML、RTF、URL、IMAGE、FILES、DRAG_IMAGE、DRAG_IMAGE_OFFSET。但目前只想复制一些文本,所以用getString就可以了。

一个小bug是准备输出setContent的时候也属于剪切板内容变动,会被监听到然后新增内容。一个解决方法是添加set去重,感觉不是很好,但想不到别的方法了。

5. 排序规则及ClipSortTree

最后是新来文字怎么放的问题。格子有三种状态:空白(EMPTY),有字(FILLED),固定(FIXED)。我希望优先填满靠左边空白的,不要动固定的,对于filled,如果新内容来时已经没有空白格子,就放在左边第一个filled的位置。

然后很无聊地给12个格子上了个线段树……在pushup里大力分类讨论就是了:

class node{
    int num;
    int val;
}

public class ClipSortTree {
    node[] tr;

    ClipSortTree(int n){
        tr=new node[n*4];
        init(1,1,n);
    }

    public void pushup(int now){
        if(tr[now*2].val<=tr[now*2+1].val){
            tr[now].num=tr[now*2].num;
            tr[now].val=tr[now*2].val;
        }
        else{
            tr[now].num=tr[now*2+1].num;
            tr[now].val=tr[now*2+1].val;
        }
    }

    public void init(int now, int l, int r){
        tr[now]=new node();
        if(l==r){
            tr[now].num=l;
            tr[now].val=0;
            return;
        }
        int mid=(l+r)/2;
        init(now*2,l,mid);
        init(now*2+1,mid+1,r);
        pushup(now);
    }

    public void modify(int now,int l,int r,int x,int val){
        if(l==r){
            tr[now].val=val;
            return;
        }
        int mid=(l+r)/2;
        if(x<=mid) modify(now*2,l,mid,x,val);
        else modify(now*2+1,mid+1,r,x,val);
        pushup(now);
    }

    public int query(){
        return tr[1].val == Priority.FIXED ? -1 : tr[1].num;
    }
}
View Code

6. 写一点UI

本来想用Qt的,但还是写Java顺手啦,小应用JavaFX也可以的。新get到的一些技能:

  •     万物皆可CSS
  • stage.setAlwaysOnTop(true);  窗口始终悬浮在顶层
  • if(e.getButton() == MouseButton.PRIMARY)  判断左键还是右键
  • this.setOnContextMenuRequested(e -> this.menu.show(this, e.getScreenX(), e.getScreenY()));  右键出现菜单

7. 打包

打包过程意外地顺利,记得改dll文件路径。

4. 评测

成品:

点击灰色拖动,黑色关闭,右键菜单,有点简陋哈。

写这篇东西的时候已经用上了CatBar,感觉背景颜色还可以换换,白色容易和输入框融为一体。还有就是格子可能有点大?有点占地,不用的时候或许可以加一个托盘隐藏,或者贴边。

一点bug是有时候点击会输出失败,尤其是刚打开的时候。不知道是哪里的问题,比较神秘(学YWJ说话.jpg),但我猜和调用dll时sleep那里有关。

至于更多问题等基友用下来再看吧?

--TBC--

标签:输出,剪贴板,窗口,Windows,方格,keybd,工具,event
From: https://www.cnblogs.com/capterlliar/p/16757356.html

相关文章

  • windows开启启动bat脚本
    直接进入目录​​C:\ProgramData\Microsoft\Windows\StartMenu\Programs\StartUp​​将编写好的bat脚本文件放置到下面这个目录即可......
  • windows 驱动开发环境搭建
    参考链接:1.微软官方说明2.VS2019+WDK10关键点:1.按照微软官方对应的windows系统版本号安装对应的visualstudio,最好是专业版。2.安装visualstudio之后需要安装安......
  • 调试windows服务
    windows服务启动后,进程是system权限,一般od等无法附加,通常有2个办法 方法1(不完美): 找到服务对应的进程pe文件,直接拖入od载入,如果直接跑起来会闪退,因为服务进程肯定......
  • Win10自带的备份工具备份系统
       Windows操作系统经过从win98,win2000,winxp,win7,win8到win10的不断更新和完善,功能已经非常强大、完备了。但伴随着微软把重点转移到云端,对更新维护不再保留专门的......
  • 手把手教你玩转 Gitea|在 Windows 系统上安装 Gitea
    Gitea支持在Windows系统上安装和使用。Gitea本身作为一个单体应用程序,即点即用,如需长期驻留作为后台服务并开机运行就要依靠Windows服务工具sc.exe。通过本文,你将......
  • 九大顶级静态代码分析工具
    https://zhuanlan.zhihu.com/p/448512219C++、DevOps、DevSecOps、敏捷开发、速度和左移策略,这些话题总是说不完道不尽,但这些也都与静态代码分析工具息息相关。这样看......
  • DateUtil 获取几分钟前时间 :获取当前时间戳 减去 时间毫秒数,获得前几分钟时间戳 时
    DateUtil获取几分钟前时间:获取当前时间戳减去时间毫秒数,获得前几分钟时间戳时间工具类DateUtil###前言在Android开发过程中,我们经常会用到时间相关方法。这里我......
  • wireshark网络封包抓包工具导入/导出pcap文件
    1、Wireshark导入文件打开Wiresharkwiki,点击SampleCaptures,可以看到Wireshark官方上传的一些pcap文件。点击SampleCaptures后,可以看到文件后缀名有cap,pcap,pcapng,pc......
  • 不会这 9 种常用的软件工具!你敢说你会网络安全
    网络安全专家,不是你认为的那种搞破坏的网络安全专家。网络安全专家,即“ethicalhackers”,是一群专门模拟网络安全专家攻击,帮助客户了解自己网络的弱点,并为客户提出改进建议......
  • 多线程篇4:工具类
    一、Semaphore、Exchanger、CountDownLatch、CyclicBarrier、PhaserJDK中提供了⼀些线程通信⼯具类以供开发者使⽤。这样的话我们在遇到⼀些常⻅的应⽤场景时就可以使⽤这......