有没有办法将Java Swing应用程序作为本机HWND窗口的子窗口?

有没有办法将Java Swing应用程序作为本机HWND窗口的子窗口?,java,scala,swing,Java,Scala,Swing,我发现情况正好相反:在JPanel中嵌入一个HWND窗口,但这显然不是我需要的 根据有效的Windows屏幕保护程序,它支持/p标志,将屏幕保护程序生成为指定窗口的子窗口(而不是全屏或默认情况下的任何操作)。例如,这用于在屏幕保护程序选择工具中渲染预览 旁注:我正在使用Scala,所以也许我可以使用一些奇怪的Scala API。我的主窗口只是一个带有 setSize(getToolkit.getScreenSize) 设置未装饰(真实) 全屏显示。如何将Java Swing应用程序作为Wind

我发现情况正好相反:在
JPanel
中嵌入一个HWND窗口,但这显然不是我需要的

根据有效的Windows屏幕保护程序,它支持
/p
标志,将屏幕保护程序生成为指定窗口的子窗口(而不是全屏或默认情况下的任何操作)。例如,这用于在屏幕保护程序选择工具中渲染预览

旁注:我正在使用Scala,所以也许我可以使用一些奇怪的Scala API。我的主窗口只是一个带有

setSize(getToolkit.getScreenSize)
设置未装饰(真实)

全屏显示。

如何将Java Swing应用程序作为Windows native HWND的子应用程序/设置屏幕保护程序预览面板

注意:这里的代码示例不会编译并省略错误代码检查,只是为了向您展示在Windows原生HWND父级中停靠任何Java Swing框架所需的Windows API调用顺序-用于您自己在JNI/JNA或JDK16+中的实现

首先计算出父窗口
长hWndParent
。 对于Windows屏幕保护程序,这将通过“/C”或“/P”(上/下)加上父HWND参数传递到“SCR”可执行文件中。 它可以有空格或“:”分隔标志和HWND,或作为两个参数flag+HWND so 确保将传递到
main(String[]args)
的数字中的长hWndParent句柄解析为:

args = {"/P 1234567"}; 
// OR {"/c", "1234567"} 
// OR {"/c:1234567"}
创建Java Swing UI—JFrame或不带标题栏的子类。必须使其可见,以便检索HWND

JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setVisible(true);
找到您的Java Swing HWND
长hWndChild
。关于获取Swing组件的HWND,还有其他的SO帖子,但是我调用了
EnumWindows
API。 您必须向
EnumWindows
提供回调
WNDENUMPROC lpEnumFunc
函数。我的示例传入Java进程PID,因此它也适用于任何本机Windows进程ID:

/**
 * EnumWindows function (winuser.h)
 * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows
 * https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498(v=vs.85)
 * BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
 */
/**
 * GetWindowThreadProcessId function (winuser.h)
 * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
 * DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
 */
这是在JDK中检索HWND的调用的精简概要:

class User32 {
    private final List<Long> hWnds = new ArrayList<>();
    // BOOL EnumWindowsProc(HWND hwnd, LPARAM lParam);
    int EnumWindowsCallback(long hwnd, long pidToFind) { // MemoryAddress in Panama
        // Set up lpdwProcessId+ use with:
        int threadId = (int)GetWindowThreadProcessId(hwnd, lpdwProcessId);
        long procId = // read address from lpdwProcessId
        if(procId == pidToFind) {
            hWnds.add(hwnd);
        }
        return TRUE;
    }
    long getHWND(long findPid) {
        // Set up lpEnumFunc as pointer to this.EnumWindowsCallback:
        EnumWindows(lpEnumFunc, findPid);
        return hWnds.get(0);
    }
}
long findPid = ProcessHandle.current().pid();
long hWndChild = new User32().getHWND(findPid);
停靠预览面板/Java Swing组件
long hWndChild
以匹配Windows本机
hwndpresent
-这确保了Z顺序正确:

dockToParent(hWndChild, hWndParent);

public static void dockToParent(long hWndChild, long hWndNewParent) {

    // Set the preview window as the parent of this window
    /**
     * Set Windows Parent
     * SetParent function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent
     * HWND SetParent(HWND hWndChild, HWND hWndNewParent);
     */
    SetParent(hWndChild, hWndNewParent);

    // Make child window so it will close when the parent dialog closes
    int GWL_STYLE = -16;
    
    /**
     * GetWindowLongPtrW function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
     * LONG_PTR GetWindowLongPtrW(HWND hWnd, int  nIndex);
     */
    long gwl = (long)GetWindowLongPtrW(hWndChild, GWL_STYLE);

    // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
    int WS_CHILD = 0x40000000;
    
    /**
     * SetWindowLongW function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw
     * LONG SetWindowLongW(HWND hWnd, int  nIndex, LONG dwNewLong);
     */
    int prev = (int)SetWindowLongW(hWndChild, GWL_STYLE, (int)gwl | WS_CHILD);
}

此时,您的Java Swing框架应该固定在Windows HWND父窗口内,它会停留在窗口顶部,随着窗口移动,并在该窗口关闭时消失。

如何将Java Swing应用程序制作为Windows原生HWND/设置屏幕保护程序预览面板的子窗口

注意:这里的代码示例不会编译并省略错误代码检查,只是为了向您展示在Windows原生HWND父级中停靠任何Java Swing框架所需的Windows API调用顺序-用于您自己在JNI/JNA或JDK16+中的实现

首先计算出父窗口
长hWndParent
。 对于Windows屏幕保护程序,这将通过“/C”或“/P”(上/下)加上父HWND参数传递到“SCR”可执行文件中。 它可以有空格或“:”分隔标志和HWND,或作为两个参数flag+HWND so 确保将传递到
main(String[]args)
的数字中的长hWndParent句柄解析为:

args = {"/P 1234567"}; 
// OR {"/c", "1234567"} 
// OR {"/c:1234567"}
创建Java Swing UI—JFrame或不带标题栏的子类。必须使其可见,以便检索HWND

JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setVisible(true);
找到您的Java Swing HWND
长hWndChild
。关于获取Swing组件的HWND,还有其他的SO帖子,但是我调用了
EnumWindows
API。 您必须向
EnumWindows
提供回调
WNDENUMPROC lpEnumFunc
函数。我的示例传入Java进程PID,因此它也适用于任何本机Windows进程ID:

/**
 * EnumWindows function (winuser.h)
 * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumwindows
 * https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498(v=vs.85)
 * BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
 */
/**
 * GetWindowThreadProcessId function (winuser.h)
 * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
 * DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
 */
这是在JDK中检索HWND的调用的精简概要:

class User32 {
    private final List<Long> hWnds = new ArrayList<>();
    // BOOL EnumWindowsProc(HWND hwnd, LPARAM lParam);
    int EnumWindowsCallback(long hwnd, long pidToFind) { // MemoryAddress in Panama
        // Set up lpdwProcessId+ use with:
        int threadId = (int)GetWindowThreadProcessId(hwnd, lpdwProcessId);
        long procId = // read address from lpdwProcessId
        if(procId == pidToFind) {
            hWnds.add(hwnd);
        }
        return TRUE;
    }
    long getHWND(long findPid) {
        // Set up lpEnumFunc as pointer to this.EnumWindowsCallback:
        EnumWindows(lpEnumFunc, findPid);
        return hWnds.get(0);
    }
}
long findPid = ProcessHandle.current().pid();
long hWndChild = new User32().getHWND(findPid);
停靠预览面板/Java Swing组件
long hWndChild
以匹配Windows本机
hwndpresent
-这确保了Z顺序正确:

dockToParent(hWndChild, hWndParent);

public static void dockToParent(long hWndChild, long hWndNewParent) {

    // Set the preview window as the parent of this window
    /**
     * Set Windows Parent
     * SetParent function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent
     * HWND SetParent(HWND hWndChild, HWND hWndNewParent);
     */
    SetParent(hWndChild, hWndNewParent);

    // Make child window so it will close when the parent dialog closes
    int GWL_STYLE = -16;
    
    /**
     * GetWindowLongPtrW function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
     * LONG_PTR GetWindowLongPtrW(HWND hWnd, int  nIndex);
     */
    long gwl = (long)GetWindowLongPtrW(hWndChild, GWL_STYLE);

    // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
    int WS_CHILD = 0x40000000;
    
    /**
     * SetWindowLongW function (winuser.h)
     * https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongw
     * LONG SetWindowLongW(HWND hWnd, int  nIndex, LONG dwNewLong);
     */
    int prev = (int)SetWindowLongW(hWndChild, GWL_STYLE, (int)gwl | WS_CHILD);
}

此时,您的Java Swing框架应该固定在Windows HWND父窗口中,它会停留在窗口顶部,随着窗口移动,并在窗口关闭时消失。

据我所知,没有用于此的Java API。我设法用WindowsAPI的函数来实现它,但它不是很稳定。移动或调整本机窗口的大小以及切换到另一个运行的应用程序会在屏幕上产生奇怪的效果(我记得)。@Abra你介意分享这些代码吗?它总比什么都没有好,而且我对Windows API不太熟悉。我再也没有了。既然它不能解决我的问题,我觉得没有理由坚持下去(好吧,我想我将学习Windows的API,然后使用新的巴拿马API/外部内存使用新发布的JDK16可以做到这一点。除了@Abra提到的将面板放置为对话框的子级的
SetParent
,您还需要调用
GetClientRect
GetWindowLongPtrW
SetWindowLongW
以确保预览面板随Windows屏幕保护程序对话框移动。据我所知,没有Java API可用于此操作。我使用Windows API的函数成功地完成了此操作,但不太稳定。移动或调整本机窗口的大小以及切换到其他运行的应用程序会在屏幕上造成奇怪的效果(我记得)。@Abra你介意分享这些代码吗?总比没有好,而且我对Windows API不太熟悉。我已经没有了。因为它不能解决我的问题,我觉得没有理由坚持下去(好吧,我想我将学习Windows的API,然后使用新的巴拿马API/外部内存使用新发布的JDK16可以做到这一点。除了@Abra提到的将面板放置为对话框的子级的
SetParent
,您还需要调用
GetClientRect
GetWindowLongPtrW
SetWindowLongW
以确保预览面板随