在Java Swing中,如何获取窗口的Win32窗口句柄(hwnd)引用?

29

在Java 1.4中,您可以使用((SunToolkit) Toolkit.getDefaultToolkit()).getNativeWindowHandleFromComponent(),但现已删除。

看起来现在必须使用JNI来做到这一点。您有 JNI 代码和示例 Java 代码吗?

我需要这样做以调用 Win32 GetWindowLong 和 SetWindowLong API 调用,这可以通过 Jawin 库完成。

我希望得到非常精确的内容,以便我可以传递对 JDialog 或 JFrame 的引用并获取窗口句柄。

使用JNI实现Swing透明度可能与此相关。

6个回答

24

你不需要编写任何C/JNI代码。从Java开始:

import sun.awt.windows.WComponentPeer;

public static long getHWnd(Frame f) {
   return f.getPeer() != null ? ((WComponentPeer) f.getPeer()).getHWnd() : 0;
}

注意事项:

  • 这里使用了 sun.* 包。显然这不是公开的 API。但它很可能不会改变(我认为比上面的解决方案更不容易出错)。
  • 这将仅在 Windows 上编译和运行。你需要把它转换成反射代码才能实现可移植性。

1
Mike Rodent问道:“谢谢,这看起来真的很好……但是使用WComponentPeer时,我遇到了一个问题:“需要库rt.jar的访问限制”- rt.jar是我的OpenOffice API导入的一部分。鉴于sun.awt.windows类不是公共的,您如何像这样使用它们?” - rogerdpack
@mike: 反射可能有所帮助:http://comments.gmane.org/gmane.comp.video.mplayer.user/58067 @Jared你也许可以在Windows上编译它,但是在其他操作系统中不要运行那个特定的代码,这样可能会起作用。 - rogerdpack
getPeer()自JDK 1.1版本起已被弃用。 - glenneroo

15
这个小的JNI方法接受一个窗口标题并返回相应的窗口句柄。
JNIEXPORT jint JNICALL Java_JavaHowTo_getHwnd
     (JNIEnv *env, jclass obj, jstring title){
 HWND hwnd = NULL;
 const char *str = NULL;

 str = (*env)->GetStringUTFChars(env, title, 0);
 hwnd = FindWindow(NULL,str);
 (*env)->ReleaseStringUTFChars(env, title, str);
 return (jint) hwnd;
 }

在调用FindWindow之前,请确保将窗口标题设置为非常独特的内容(这样您就不会意外捕获具有相同标题的另一个窗口的hwnd - FindWindow调用不是进程特定的)。 - Kevin Day
这不够精确。我宁愿不指望窗口标题没有被另一个窗口使用。 - Sarel Botha
您可以将“NULL”替换为类名,以使搜索更精确。您可以使用特殊工具(如SPY++或WinID)确定窗口类名。 - RealHowTo
2
小例子很棒。 - user2171669

10
以下代码可以让你传递一个组件来获取它的窗口句柄 (HWND)。要确保一个组件有对应的窗口句柄,可以在组件上调用isLightWeight()方法,并验证其是否等于false。如果不是,尝试调用其父级组件的Component.getParent()方法。
Java代码:
package win32;
public class Win32 {
    public static native int getWindowHandle(Component c);
}

头文件 main.h:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class win32_Win32 */

#ifndef _Included_win32_Win32
#define _Included_win32_Win32
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     win32_Win32
 * Method:    getWindowHandle
 * Signature: (Ljava/awt/Component;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_win32_Win32_getWindowHandle
  (JNIEnv *, jclass, jobject);
#ifdef __cplusplus
}
#endif
#endif

源代码 main.c:

#include<windows.h>
#include <jni.h>
#include <jawt.h>
#include <jawt_md.h>

HMODULE _hAWT = 0;

JNIEXPORT jint JNICALL Java_win32_Win32_getWindowHandle
  (JNIEnv * env, jclass cls, jobject comp)
{
    HWND hWnd = 0;
    typedef jboolean (JNICALL *PJAWT_GETAWT)(JNIEnv*, JAWT*);
    JAWT awt;
    JAWT_DrawingSurface* ds;
    JAWT_DrawingSurfaceInfo* dsi;
    JAWT_Win32DrawingSurfaceInfo* dsi_win;
    jboolean result;
    jint lock;

    //Load AWT Library
    if(!_hAWT)
        //for Java 1.4
        _hAWT = LoadLibrary("jawt.dll");
    if(!_hAWT)
        //for Java 1.3
        _hAWT = LoadLibrary("awt.dll");
    if(_hAWT)
    {
        PJAWT_GETAWT JAWT_GetAWT = (PJAWT_GETAWT)GetProcAddress(_hAWT, "_JAWT_GetAWT@8");
        if(JAWT_GetAWT)
        {
            awt.version = JAWT_VERSION_1_4; // Init here with JAWT_VERSION_1_3 or JAWT_VERSION_1_4
            //Get AWT API Interface
            result = JAWT_GetAWT(env, &awt);
            if(result != JNI_FALSE)
            {
                ds = awt.GetDrawingSurface(env, comp);
                if(ds != NULL)
                {
                    lock = ds->Lock(ds);
                    if((lock & JAWT_LOCK_ERROR) == 0)
                    {
                        dsi = ds->GetDrawingSurfaceInfo(ds);
                        if(dsi)
                        {
                            dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
                            if(dsi_win)
                            {
                                hWnd = dsi_win->hwnd;
                            }
                            else {
                                hWnd = (HWND) -1;
                            }
                            ds->FreeDrawingSurfaceInfo(dsi);
                        }
                        else {
                            hWnd = (HWND) -2;
                        }
                        ds->Unlock(ds);
                    }
                    else {
                        hWnd = (HWND) -3;
                    }
                    awt.FreeDrawingSurface(ds);
                }
                else {
                    hWnd = (HWND) -4;
                }
            }
            else {
                hWnd = (HWND) -5;
            }
        }
        else {
            hWnd = (HWND) -6;
        }
    }
    else {
        hWnd = (HWND) -7;
    }
    return (jint)hWnd;

}

很抱歉在这里发表一篇非常老的帖子,但是每当我尝试获取组件(在我的情况下是java.awt.Canvas)的绘图表面(GetDrawingSurface)时,我一直会收到jvm.dll中的EXCEPTION_ACCESS_VIOLATION。我确保它不是轻量级的,并且已经在屏幕上可见。Java 1.6是否有任何变化或者在获取绘图表面之前需要做些其他事情? - pdinklag
没有头绪。尝试提出一个新问题以便更多人关注这个问题。 - Sarel Botha
pdinklag,你能够解决调用GetDrawingSurface时遇到的问题吗?我现在也遇到了同样的问题,jvm在DSGetDrawingSurface中崩溃。尝试了几个jvm(1.6和1.7)-仍然崩溃。 - Archie

6

1
实际上,getWindowPointer() 是为 Windows 设计的。根据他们的文档,方法 getWindowID() 是为 X11 设计的。 - Sarel Botha

1

1

这与Jared MacD的答案相同,但它使用反射,以便代码可以编译并在非Windows计算机上加载。当然,如果您尝试调用它,它将失败。

import java.awt.Frame;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WindowHandleGetter {
    private static final Logger log = LoggerFactory.getLogger(WindowHandleGetter.class);
    private final Frame rootFrame;

    protected WindowHandleGetter(Frame rootFrame) {
        this.rootFrame = rootFrame;
    }

    protected long getWindowId() {

        try {
            Frame frame = rootFrame;

            // The reflection code below does the same as this
            // long handle = frame.getPeer() != null ? ((WComponentPeer) frame.getPeer()).getHWnd() : 0;

            Object wComponentPeer = invokeMethod(frame, "getPeer");

            Long hwnd = (Long) invokeMethod(wComponentPeer, "getHWnd");

            return hwnd;

        } catch (Exception ex) {
            log.error("Error getting window handle");
        }

        return 0;
    }

    protected Object invokeMethod(Object o, String methodName) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {

        Class c = o.getClass();
        for (Method m : c.getMethods()) {
            if (m.getName().equals(methodName)) {
                Object ret = m.invoke(o);
                return ret;
            }
        }
        throw new RuntimeException("Could not find method named '"+methodName+"' on class " + c);

    }


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接