Monitor the clipboard with JNA

I wanted to monitor the Windows clipboard with JNA, so I did a lot of research. Helpful site: https://stackoverflow.com/a/28693513 https://java-native-access.github.io/jna/4.2.0/com/sun/jna/platform/win32/WinUser.WindowProc.html https://www.codota.com/code/java/methods/com.sun.jna.platform.win32.User32/DefWindowProc http://kaitei.net/winapi/window-procedures/ https://github.com/EsotericSoftware/clippy/blob/master/src/com/esotericsoftware/clippy/Win.java https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats https://www.cnblogs.com/tangchun/p/9288626.html https://blog.csdn.net/tcjiaan/article/details/8951280 https://www.cnblogs.com/iszero/p/4020207.html https://reehappy.com/java009/

Rough flow:

  1. Create an invisible Window and set WindowProc to lpfnWndProc of Window.
  2. Call User32.AddClipboardFormatListener and get a Message when the clipboard changes.
  3. Chatch WM_CLIPBOARDUPDATE.
  4. At the end, call RemoveClipboardFormatListener to unregister the Listener.

Sample code. ** Validated with jna-5.5.0.jar **

MyUser32.java


package clipbord;

import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.win32.W32APIOptions;

final public class MyUser32 {

    static {
        Native.register(NativeLibrary.getInstance("user32", W32APIOptions.DEFAULT_OPTIONS));
    }

    static public final int WM_CLIPBOARDUPDATE = 0x31D;

    // https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
    static public final int CF_TEXT = 1;
    static public final int CF_UNICODETEXT = 13;
    static public final int CF_HDROP = 15;

    // Clipboard
    static public native boolean AddClipboardFormatListener(Pointer hWnd);
    static public native boolean RemoveClipboardFormatListener(Pointer hWnd);
    static public native boolean OpenClipboard(Pointer hWnd);
    static public native boolean CloseClipboard(Pointer hWnd);
    static public native boolean EmptyClipboard();
    static public native boolean IsClipboardFormatAvailable(int format);
    static public native Pointer GetClipboardData(int format);
    static public native Pointer SetClipboardData(int format, Pointer hMem);
    static public native Pointer GetClipboardOwner();
}

ClipboardWatcher.java


package clipbord;

import java.util.Scanner;

import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.WNDCLASSEX;
import com.sun.jna.platform.win32.WinUser.WindowProc;

public class ClipboardWatcher {

    public static void main(String[] args) {
        new ClipboardWatcher().start();
    }

    private Thread thread;
    private HWND _hWnd;

    final public void start() {
        this.thread = new Thread(this::myThread);
        this.thread.start();
        {
            System.out.println("watching clipboard. press any key to exit...");
            Scanner sc = new Scanner(System.in);
            sc.next();
            sc.close();
        }
        this.stop();
    }

    final public void stop() {
        boolean remove = MyUser32.RemoveClipboardFormatListener(this._hWnd.getPointer());
        System.out.println("RemoveClipboardFormatListener:" + remove);
        User32.INSTANCE.PostMessage(this._hWnd, User32.WM_QUIT, null, null);
        System.out.println("exit");
    }

    private final WindowProc callback = new WindowProc() {
        @Override
        public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
            //System.out.println(uMsg);
            switch (uMsg) {
            case WinUser.WM_CREATE:
                System.out.println("WM_CREATE");
                return new LRESULT(0);

            case WinUser.WM_DESTROY:
                System.out.println("WM_DESTROY");
                User32.INSTANCE.PostQuitMessage(0);
                return new LRESULT(0);

            case MyUser32.WM_CLIPBOARDUPDATE:
                // System.out.println("WM_CLIPBOARDUPDATE");
                MyUser32.OpenClipboard(hWnd.getPointer());
                if (/*MyUser32.IsClipboardFormatAvailable(MyUser32.CF_TEXT) ||*/ MyUser32.IsClipboardFormatAvailable(MyUser32.CF_UNICODETEXT)) {
                    /* do something here */
                    System.out.println(MyUser32.GetClipboardData(MyUser32.CF_UNICODETEXT).getWideString(0));
                }
                MyUser32.CloseClipboard(hWnd.getPointer());
                return new LRESULT(0);

            default:
                return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
            }
        }
    };

    private void myThread() {
        //WString className = new WString("myclass");
        String className = "myclass";
        WNDCLASSEX wx = new WNDCLASSEX();
        wx.clear();
        wx.lpszClassName = className;
        wx.lpfnWndProc = callback;

        if (User32.INSTANCE.RegisterClassEx(wx).intValue() != 0) {
            this._hWnd = User32.INSTANCE.CreateWindowEx(0, className, null, 0, 0, 0, 0, 0, null, null, null, null);
            boolean add = MyUser32.AddClipboardFormatListener(this._hWnd.getPointer());
            System.out.println("AddClipboardFormatListener:" + add);

            WinUser.MSG msg = new WinUser.MSG();
            msg.clear();

            while (User32.INSTANCE.GetMessage(msg, this._hWnd, 0, 0) > 0) {
                User32.INSTANCE.TranslateMessage(msg);
                User32.INSTANCE.DispatchMessage(msg);
            }
        }
    }
}

that's all

Recommended Posts

Monitor the clipboard with JNA
Monitor the internal state of Java programs with Kubernetes
Change the port with SpringBoot
Mock the constructor with PowerMock
With the error Cordova-> Looper.prepare ()
Simulate the simplex method with GUI
Automatically scroll the background with libGDX
Programming with ruby (on the way)
Follow the link with Selenium (Java)
Place in the middle with css
Easily monitor the indoor environment ~ ⑧ Postscript ~
Follow the Datomic tutorial with Datascript
The guy who tries-with-resources with kotlin
Image processing: Let's play with the image
Put Zabbix in Ubuntu with Docker and monitor Docker on the same host