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

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  で検証**

```java: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();
}
```

```java: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);
            }
        }
    }
}
```


以上