LoginSignup
2
1

More than 3 years have passed since last update.

JNA で クリップボードを監視

Last updated at Posted at 2021-01-06

JNA で Windows の clipbord を監視したいので、いろいろ調べました。
参考になったサイト:
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/

大まかの流れ:

  1. 見えない Window を作り、Window の lpfnWndProc に WindowProc を設定。
  2. User32.AddClipboardFormatListener を呼び出し、クリップボードが変化時に Message が飛んでくるようになる。
  3. WM_CLIPBOARDUPDATE をチャッチ。
  4. 終了時に、RemoveClipboardFormatListener を呼び出し、Listener 登録解除。

サンプルコード。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);
            }
        }
    }
}

以上

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1