В Java Swing как получить ссылку дескриптора окна Win32 (hwnd) на окно? - PullRequest
27 голосов
/ 22 декабря 2008

В Java 1.4 вы могли использовать ((SunToolkit) Toolkit.getDefaultToolkit ()). GetNativeWindowHandleFromComponent (), но это было удалено.

Похоже, вы должны использовать JNI, чтобы сделать это сейчас. У вас есть код JNI и пример кода Java для этого?

Мне нужно это для вызова вызовов Win32 GetWindowLong и SetWindowLong API, что можно сделать через библиотеку Jawin.

Мне бы хотелось что-то очень точное, чтобы я мог передать ссылку на JDialog или JFrame и получить дескриптор окна.

Прозрачность колебания с использованием JNI может быть связана.

Ответы [ 7 ]

21 голосов
/ 17 сентября 2010

Вам не нужно писать код 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. Вам нужно было бы превратить это в код отражения, чтобы он был переносимым.
14 голосов
/ 23 декабря 2008

Этот маленький метод 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;
 }

UPDATE:

С JNA это немного проще. Я сделал маленький пример , который находит дескриптор и использует его для вывода программы на передний план.

10 голосов
/ 05 января 2009

Следующий код позволяет вам передать Компонент, чтобы получить дескриптор окна (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

Источник C 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;

}
5 голосов
/ 05 февраля 2009

Оба вышеперечисленных метода работают просто отлично, но оба возвращают HWND как java int (32 бита). это хорошо для 32-битной платформы, но вряд ли ваше приложение будет работать на 64-битной платформе. Я бы изменил возвращаемый тип на long (64 бит), так как он будет работать правильно как в 64, так и в 32 битных системах (вам нужно будет только перекомпилировать DLL)

4 голосов
/ 27 апреля 2011

Я нашел это: http://jna.java.net/javadoc/com/sun/jna/Native.html#getWindowID(java.awt.Window)

JNA позволяет вам вызывать нативные библиотеки без необходимости писать нативный код jni. Оказывается, в самой библиотеке есть метод, который берет Window и выдает int, предположительно дескриптор (или указатель?), Который, надеюсь, работает на всех платформах.

1 голос
/ 03 августа 2012

Это то же самое, что и ответ Джареда Макда, но в нем используется отражение, чтобы код мог компилироваться и загружаться на компьютер не из 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);

    }


}
1 голос
/ 02 июля 2011

В библиотеке JNA мы видим, что использование Native AWT в Java 5 и 6 UnsatisfiedLinkError при запуске без заголовка, поэтому используйте динамическое связывание. См метод Java_com_sun_jna_Native_getWindowHandle0 в https://github.com/twall/jna/blob/master/native/dispatch.c.

...