JavaFX最小化未修饰的阶段

我有一个未修饰的JavaFX Stage,以及我自己的最小化,最大化和关闭按钮。 但遗憾的是,与装饰行为相比,单击Windows 7中的任务栏图标不会自动最小化舞台。

有没有办法通过单击任务栏图标来最小化纯Java代码的未修饰阶段? 如果不是,我怎么能用JNA做到这一点?

编辑:好的,我一直在尝试用JNA来解决这个问题,但是在C / C ++ / JNA旁边做了很多,我设置它有点麻烦。 如果有人帮我把各个部分放在一起,我将不胜感激。

到目前为止,这是我的代码:

public final class Utils { static { if (PlatformUtil.isWin7OrLater()) { Native.register("shell32"); Native.register("user32"); } } // Apparently, this is the event I am after public static final int WM_ACTIVATEAPP = 0x1C; public static void registerMinimizeHandler(Stage stage) { // Hacky way to get a pointer to JavaFX Window Pointer pointer = getWindowPointer(stage); WinDef.HWND hwnd = new WinDef.HWND(pointer); // Here's my minimize/activate handler WinUser.WindowProc windowProc = new MinimizeHandler(stage); Pointer magicPointer = ... set this to point to windowProc? // This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc? User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer); } } private static class MinimizeHandler implements WinUser.WindowProc { private Stage stage; private MinimizeHandler(Stage stage) { this.stage = stage; } @Override public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { if (uMsg == WM_ACTIVATEAPP) { System.out.println("ACTIVATE"); } return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam); } } 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; } } 

编辑2:我最终得到了这个,但是当我重新设置WNDPROC时,我的未修饰的窗口没有响应任何事件..我正在为一个独立的例子提供100点声望的赏金有了解决方案。 Windows(7+)只是可以,我甚至不知道这在其他平台上是如何表现的。

编辑3:嗯,我有点放弃了这一个……我把所有东西都设置正确,并收到了事件,但是在找出正确的事件要听的时候遇到了问题。

由于对这个问题有兴趣,如果有人想继续这个,这是我的最终代码(希望它应该“开箱即用”):

 public final class Utils { static interface ExtUser32 extends StdCallLibrary, User32 { ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary( "user32", ExtUser32.class, W32APIOptions.DEFAULT_OPTIONS); WinDef.LRESULT CallWindowProcW( Pointer lpWndProc, Pointer hWnd, int msg, WinDef.WPARAM wParam, WinDef.LPARAM lParam); int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException; } // Some possible event types public static final int WM_ACTIVATE = 0x0006; public static final int WM_ACTIVATEAPP = 0x1C; public static final int WM_NCACTIVATE = 0x0086; public static void registerMinimizeHandler(Stage stage) { Pointer pointer = getWindowPointer(stage); WinDef.HWND hwnd = new WinDef.HWND(pointer); long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC); MinimizeHandler handler = new MinimizeHandler(stage, old); ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler); } 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; } } private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback { private Pointer mPrevWndProc32; private Stage stage; private MinimizeHandler(Stage stage, long oldPtr) { this.stage = stage; mPrevWndProc32 = new Pointer(oldPtr); // Set up an event pump to deliver messages for JavaFX to handle Thread thread = new Thread() { @Override public void run() { int result; WinUser.MSG msg = new WinUser.MSG(); while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) { if (result == -1) { System.err.println("error in get message"); break; } else { System.out.println("got message: " + result); User32.INSTANCE.TranslateMessage(msg); User32.INSTANCE.DispatchMessage(msg); } } } }; thread.start(); } @Override public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) { if (uMsg == WM_ACTIVATEAPP) { // Window deactivated (wParam == 0)... Here's where I got stuck and gave up, // this is probably not the best event to listen to.. the app // does indeed get iconified now by pressing the task-bar button, but it // instantly restores afterwards.. if (wParam.intValue() == 0) { stage.setIconified(true); } return new WinDef.LRESULT(0); } // Let JavaFX handle other events return ExtUser32.INSTANCE.CallWindowProcW( mPrevWndProc32, hWnd.getPointer(), uMsg, wParam, lParam); } } } 

您只需设置适当的窗口样式即可。 它适用于XP,但在Windows 7 32位应该没问题。 我认为(但不能测试)如果你使用64位然后更改为Ptr窗口函数,即。 GetWindowLongPtr。

 import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinUser; import static com.sun.jna.platform.win32.WinUser.GWL_STYLE; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TextArea; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class JNATest extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { TextArea ta = new TextArea("output\n"); VBox root = new VBox(5,ta); Scene scene = new Scene(root,800,200); stage.setTitle("Find this window"); stage.setScene(scene); stage.show(); //gets this window (stage) long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); Pointer lpVoid = new Pointer(lhwnd); //gets the foreground (focused) window final User32 user32 = User32.INSTANCE; char[] windowText = new char[512]; HWND hwnd = user32.GetForegroundWindow(); //see what the title is user32.GetWindowText(hwnd, windowText, 512); //user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage String text=(Native.toString(windowText)); //see if it's the same pointer ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n"); //change the window style if it's the right title if (text.equals(stage.getTitle())){ //the style to change int WS_DLGFRAME = 0x00400000;//s/b long I think //not the same constant here?? ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME); int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style newStyle = newStyle & ~0x00040000;//WS_THICKFRAME user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); } } } 

我的猜测是你替换最后3行

  long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue(); long newStyleLong = oldStyleLong & ~ 0x00400000l; user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong)); 

对于64位。 我想我的User32.dll中没有这些function,所以我无法测试它。 那里有很多无关的代码,主要用于测试或教学。 一旦弄清楚要做什么,就要删除未使用的线。

PS。 不要添加newStyle = newStyle & ~0x00020000;//WS_MINIMIZEBOX 。 这是JavaFX不用于未修饰的样式标志之一。 这就是为什么没有最小化的原因。 也许如果您尝试设置未修饰的阶段并添加(使用|,而不是〜)最小化框标记,您将得到相同的结果。 有工具可以从任何窗口查找所有样式标志。

这是使用舞台的HWND改变未修饰阶段的最简单的代码量。

  public void start(Stage stage) { Scene scene = new Scene(new Pane(new Label("Hello World"))); stage.initStyle(StageStyle.UNDECORATED); stage.setTitle("Find this window"); stage.setScene(scene); stage.show(); long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow(); Pointer lpVoid = new Pointer(lhwnd); HWND hwnd = new HWND(lpVoid); final User32 user32 = User32.INSTANCE; int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE); System.out.println(Integer.toBinaryString(oldStyle)); int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX System.out.println(Integer.toBinaryString(newStyle)); user32.SetWindowLong(hwnd, GWL_STYLE, newStyle); } 

它会在之前和之后打印出样式标志,以便您可以查找设置的样式。

没有得到正确的问题..但这是解决方案

 @FXML private void minimize() { Stage stage = (Stage) minimize.getScene().getWindow(); stage.setIconified(true); }