如何在 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)?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 JavaFX 中监听 Stage 的 resize 事件?