【问题标题】:How can I get the window handle (hWnd) for a Stage in JavaFX?如何在 JavaFX 中获取 Stage 的窗口句柄 (hWnd)?
【发布时间】: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 添加了评论。
  • 如果您想提高其被修复的可能性,您可以投票支持该功能。

标签: java javafx-2 jna javafx-8 hwnd


【解决方案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。
        猜你喜欢
        • 2020-10-22
        • 2013-01-02
        • 2011-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-03
        相关资源
        最近更新 更多