문제

We're building a JavaFX application in Windows, and we want to be able to do some things to manipulate how our application appears in the Windows 7/8 taskbar. This requires modifying a Windows variable called the "Application User Model ID".

We've already managed to do exactly what we want in Swing by using JNA, and we'd like to repeat our solution in JavaFX. Unfortunately, to do this, we need to be able to retrieve the hWnd (window handle) for each window in our application. This can be done in Swing/AWT via the JNA Native.getWindowPointer() method, which works with java.awt.Window, but I can't figure out a good way to do this with a javafx.stage.Window.

Does anyone know of any way to do get hWnd for a Stage?

도움이 되었습니까?

해결책

Here's a JavaFX2 version (uses Stage rather than Window):

private static Pointer getWindowPointer(Stage stage) {
    try {
        TKStage tkStage = stage.impl_getPeer();
        Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
        getPlatformWindow.setAccessible(true);
        Object platformWindow = getPlatformWindow.invoke(tkStage);
        Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
        getNativeHandle.setAccessible(true);
        Object nativeHandle = getNativeHandle.invoke(platformWindow);
        return new Pointer((Long) nativeHandle);
    } catch (Throwable e) {
        System.err.println("Error getting Window Pointer");
        return null;
    }
}

다른 팁

Add a dependency on JNA:

<dependency>
  <groupId>net.java.dev.jna</groupId>
  <artifactId>jna-platform</artifactId>
  <version>5.3.1</version>
</dependency>

Then give your Stage a distinct title ("MyStage" in this example), and then get the Window ID like this:

WinDef.HWND hwnd = User32.INSTANCE.FindWindow(null, "MyStage");

long wid = Pointer.nativeValue(hwnd.getPointer());

This will work on Windows regardless of JavaFX version.

com.sun.glass.ui.Window.getWindows.get(0).getNativeWindow

//

com.sun.glass.ui.Window.getFocusedWindow.getNativeWindow

The following method shows how you can get a native window handle (hWnd) for a JavaFX Stage (or Window) and then store it in a JNA Pointer object:

private static Pointer getWindowPointer(javafx.stage.Window window) {
    Pointer retval = null;
    try {
        Method getPeer = window.getClass().getMethod("impl_getPeer");
        final Object tkStage = getPeer.invoke(window);
        Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow");
        getPlatformWindow.setAccessible(true);
        final Object platformWindow = getPlatformWindow.invoke(tkStage);
        Method getNativeHandle = platformWindow.getClass().getMethod("getNativeHandle");
        retval = new Pointer((Long) getNativeHandle.invoke(platformWindow));
    } catch (Throwable t) {
        System.err.println("Error getting Window Pointer");
        t.printStackTrace();
    }
    return retval;
}

This solution is fragile and generally undesirable, since it uses reflection to access a bunch of private methods. But it gets the job done. Hopefully Oracle will eventually give us direct access to native window handles so we don't have to do this.

Of course, this code only works when you're running on MS Windows. Also, I've only tried it out with early release builds of JavaFX 8 (but I suspect it will work fine on JavaFX 2 as well. EDIT: looks like it doesn't work in JavaFX 2.)

The following code works for me in JavaFX 11 on Windows (I only need it there). I haven't tested it in any other version.

It's quite brittle, but manageable in my case, as I bundle the Java Runtime with the application so I always know what's underneath.

If you use Java 9 modules, you also need to open up the packages to your calling module:

--add-opens javafx.graphics/javafx.stage=com.example and --add-opens javafx.graphics/com.sun.javafx.tk.quantum=com.example

package com.example;

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.lang.reflect.Method;

public class FXWinUtil {

    public static WinDef.HWND getNativeHandleForStage(Stage stage) {
        try {
            final Method getPeer = Window.class.getDeclaredMethod("getPeer", null);
            getPeer.setAccessible(true);
            final Object tkStage = getPeer.invoke(stage);
            final Method getRawHandle = tkStage.getClass().getMethod("getRawHandle");
            getRawHandle.setAccessible(true);
            final Pointer pointer = new Pointer((Long) getRawHandle.invoke(tkStage));
            return new WinDef.HWND(pointer);
        } catch (Exception ex) {
            System.err.println("Unable to determine native handle for window");
            return null;
        }
    }
}

If you're using JNA (which is likely if you're doing hackish stuff like this), you can also profit from WinDef.HWND.

Solution for JavaFX 16, written on Kotlin (only Reflection)

fun getPointer(scene: Scene): Long {
    val tkStage = SceneHelper.getPeer(scene)

    val windowStage = tkStage.javaClass.getDeclaredMethod("getWindowStage")
        .apply { isAccessible = true }
        .invoke(tkStage)

    val platformWindow = windowStage.javaClass.getDeclaredMethod("getPlatformWindow")
        .apply { isAccessible = true }
        .invoke(windowStage)
    
    // Use fields 'ptr' and 'delegatePtr' instead of getNativeHandle() to avoid Platform.runLater
    val ptr = Window::class.java.getDeclaredField("ptr")
        .apply { isAccessible = true }[platformWindow] as Long

    val delegatePtr = Window::class.java.getDeclaredField("delegatePtr")
        .apply { isAccessible = true }[platformWindow] as Long

    return if (delegatePtr != 0L) delegatePtr else ptr
}

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top