如何在 JavaFX 中获取 Stage 的窗口句柄 (hWnd)?

Posted

技术标签:

【中文标题】如何在 JavaFX 中获取 Stage 的窗口句柄 (hWnd)?【英文标题】:How can I get the window handle (hWnd) for a Stage in JavaFX? 【发布时间】:2013-02-08 16:11:24 【问题描述】:

我们正在 Windows 中构建 JavaFX 应用程序,我们希望能够做一些事情来控制我们的应用程序在 Windows 7/8 任务栏中的显示方式。这需要修改一个名为“Application User Model ID”的 Windows 变量。

我们已经通过使用JNA 成功地在 Swing 中完成了我们想要做的事情,并且我们想在 JavaFX 中重复我们的解决方案。不幸的是,为此,我们需要能够检索应用程序中每个窗口的hWnd(窗口句柄)。这可以通过 JNA Native.getWindowPointer() 方法在 Swing/AWT 中完成,该方法适用于 java.awt.Window,但我无法找到使用 javafx.stage.Window 的好方法。

有谁知道有什么方法可以通过hWnd 获取Stage

【问题讨论】:

您能否看看这个功能请求,并说,它是否包含您感兴趣的内容? javafx-jira.kenai.com/browse/RT-24249 @Alexander:这似乎有正确的想法,尽管我怀疑他们会实现任何让我们用任务栏做我们需要做的事情(我们有一个需要显示的应用程序)任务栏中有几个不同的图标。)我认为我们确实需要 hWnd。不过感谢您的链接,我已对该 RFE 添加了评论。 如果您想提高其被修复的可能性,您可以投票支持该功能。 【参考方案1】:

JavaFX 16 的解决方案,在 Kotlin 上编写(仅反射)

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


【讨论】:

【参考方案2】:

以下代码适用于 Windows 上的 JavaFX 11(我只需要它)。我没有在任何其他版本中测试过。

它很脆弱,但在我的情况下是可以管理的,因为我将 Java 运行时与应用程序捆绑在一起,所以我总是知道下面是什么。

如果您使用 Java 9 模块,您还需要为调用模块打开包:

--add-opens javafx.graphics/javafx.stage=com.example--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;
        
    


如果您使用的是 JNA(如果您正在做类似这样的骇人听闻的事情,这很可能),您也可以从 WinDef.HWND 中获利。

【讨论】:

【参考方案3】:

添加对 JNA 的依赖:

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

然后给您的Stage 一个不同的标题(在本例中为“MyStage”),然后像这样获取窗口 ID:

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

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

无论 JavaFX 版本如何,这都适用于 Windows。

【讨论】:

【参考方案4】:

以下方法展示了如何获取 JavaFX Stage(或 Window)的本机窗口句柄 (hWnd),然后将其存储在 JNA Pointer 对象中:

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;

这个解决方案很脆弱,通常不受欢迎,因为它使用反射来访问一堆私有方法。但它可以完成工作。希望 Oracle 最终能让我们直接访问本机窗口句柄,这样我们就不必这样做了。

当然,此代码仅在您在 MS Windows 上运行时才有效。另外,我只在 JavaFX 8 的早期版本中进行了尝试(但我怀疑它在 JavaFX 2 上也能正常工作。编辑:看起来它在 JavaFX 2 中不起作用。)

【讨论】:

JavaFX 2 用户在这里。 Windows 7,jdk1.7.0_21。 m.invoke( window ) 返回null。解决方案不起作用。 添加了 JavaFX2 版本作为单独的答案。 FWIW。 其实这已经是JavaFX2版本了。 Window 是 Stage 的超类。【参考方案5】:
com.sun.glass.ui.Window.getWindows.get(0).getNativeWindow

//

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

【讨论】:

这对我有用。没有反射,没有额外的依赖,只是一个简单的调用方法。【参考方案6】:

这是一个 JavaFX2 版本(使用 Stage 而不是 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;
    

【讨论】:

至少从 1.8_66 开始,我认为这不再有效。 TKStage 中似乎不再存在“getPlatformWindow”。 看起来它已被移至 WindowStage 类。有人知道怎么去吗? 呃,WindowStage 是 TKStage 的子类。该方法是通过反射找到的,但是在调用该方法时我得到了一个 InvocationException。

以上是关于如何在 JavaFX 中获取 Stage 的窗口句柄 (hWnd)?的主要内容,如果未能解决你的问题,请参考以下文章

通过代码关闭 fxml 窗口,javafx

JavaFX - 实现管理多个Stage窗口切换及源码解析

JavaFX如何实现模态窗口

如何在 JavaFX 中监听 Stage 的 resize 事件?

JavaFX实战:模拟电子琴弹奏效果,鼠标弹奏一曲piano送给大家

JavaFX实战:模拟电子琴弹奏效果,鼠标弹奏一曲piano送给大家