簡體   English   中英

在 Java 中獲取非 Java Windows 的標題

[英]Get titles of Non-Java Windows in Java

Window.getWindows();

沒有得到所有開放的 windows,只有 java。 有沒有辦法獲取操作系統已打開的所有windows。 我正在制作一個 java 任務欄。

沒有使用核心 Java 的解決方案,但可以使用 JNI 或更簡單的 JNA 來解決問題。 如評論中所述,沒有任何解決方案(據我所知)是獨立於平台的。

例如,這個演示程序使用 JNA 枚舉 Windows 平台中所有帶有標題的窗口,但也會包括非頂級窗口甚至不可見的窗口:

import java.util.ArrayList;
import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary;

public class EnumAllWindowNames {
   static interface User32 extends StdCallLibrary {
      User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);

      interface WNDENUMPROC extends StdCallCallback {
         boolean callback(Pointer hWnd, Pointer arg);
      }

      boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer userData);
      int GetWindowTextA(Pointer hWnd, byte[] lpString, int nMaxCount);
      Pointer GetWindow(Pointer hWnd, int uCmd);
   }

   public static List<String> getAllWindowNames() {
      final List<String> windowNames = new ArrayList<String>();
      final User32 user32 = User32.INSTANCE;
      user32 .EnumWindows(new User32.WNDENUMPROC() {

         @Override
         public boolean callback(Pointer hWnd, Pointer arg) {
            byte[] windowText = new byte[512];
            user32.GetWindowTextA(hWnd, windowText, 512);
            String wText = Native.toString(windowText).trim();
            if (!wText.isEmpty()) {
               windowNames.add(wText);
            }
            return true;
         }
      }, null);

      return windowNames;
   }

   public static void main(String[] args) {
      List<String> winNameList = getAllWindowNames();
      for (String winName : winNameList) {
         System.out.println(winName);
      }
   }


}

除了@Hovercraft Full Of Eels ,如果您還有標題為不可讀字符的窗口得到“?” 為這些字符打印,嘗試:

  • 通過使用不同的編碼將 byte[] 轉換為字符串(據我所知)

見下文:

String wText = Native.toString(windowText, "windows-1254").trim();

可能的編碼而不是“windows-1254(這對土耳其字符很有效)”:

  • UTF-8編碼
  • UTF-16
  • ASCII碼
  • ISO-8859-1
  • ...

您現在可以調用本機 Windows API,使用 Foreign Memory API 和由兼容的jextract生成的代碼綁定,通過純 Java 代碼讀取非 Java windows 的標題。

此示例適用於 JDK19 版本中的預覽代碼,相當於 Hovercraft Full Of Eels 答案:

/** 
 * Run with Windows JDK19 and preview enabled:
 * jdk-19\bin\java --enable-native-access=ALL-UNNAMED --enable-preview -cp your.jar EnumWindowsDemo
 */
public class EnumWindowsDemo {
    // Wide to Java String
    private static String toJavaString(MemorySegment wide, int len) {
        final CharBuffer cb = wide.asByteBuffer().order(ByteOrder.nativeOrder()).asCharBuffer();
        return cb.limit(len).toString();
    }
    public static void main(String[] args) {
        // TODO: You could use someParam to detect specific hWnd inside the callback
        long someParam = 0x1234;

        final int isRunning = User32_h.TRUE();
        final int maxChars = 1024;

        System.out.format("EnumWindows Panama 19 Demo Java %s%n", System.getProperty("java.runtime.version"));

        HashMap<Long,String> windowNames = new HashMap<>();
        try(MemorySession arena = MemorySession.openConfined()) {
        // JDK20 try(Arena arena = Arena.openConfined()) {
            MemorySegment lpString = arena.allocateArray(JAVA_CHAR, maxChars);

            // Windows callback WNDENUMPROC(MemorySegment hwnd, long lParam)
            WNDENUMPROC callback = (hWnd, lParam) -> {
                int used = User32_h.GetWindowTextW(hWnd, lpString, maxChars);
                if (used > 0) {
                    String text = toJavaString(lpString, used);
                    System.out.format("hWnd:%x lParam:%x => `%s`%n", hWnd.toRawLongValue(), lParam, text);
                    // JDK20: System.out.format("hWnd:%x lParam:%x => `%s`%n", hWnd.address(), lParam, text);
                    if (used >= maxChars) {
                        System.out.format("*** Oops: increase maxChars > %d, or call GetWindowTextLength first ***%n", used);
                    }
                    windowNames.put(hWnd.toRawLongValue(), text);
                    // JDK20: windowNames.put(hWnd.address(), text);
                }
                return isRunning;
            };

            final MemorySegment ewProc = WNDENUMPROC.allocate(callback, arena);
            // JDK20: final MemorySegment ewProc = WNDENUMPROC.allocate(callback, arena.scope());

            // Note that someParam is passed to the callback as lParam value
            int rc = User32_h.EnumWindows(ewProc, someParam);
            System.out.format("EnumWindows rc:%s%n", rc == 0 ? "FAIL":"OK");
            if (rc == 0) {
                throw new RuntimeException("EnumWindows failed rc="+rc);
            }
        }
        System.out.format("Found windowNames: %s%n", windowNames.toString());
    }
}

獨立的 jextract 用於生成調用本機代碼的綁定。 您需要創建一個 header User32.h引用合適的 Windows API 標頭:

echo #include ^<shlobj_core.h^> > User32.h

運行 jextract 為一組合適的 Windows API 生成綁定:

set JEXTRACT_ARGS=--source -luser32 User32.h -t somepackage --output java
set JEXTRACT_SYMBOLS=--include-macro TRUE --include-function EnumWindows --include-function GetWindowTextW --include-function GetWindowTextLengthW --include-typedef  WNDENUMPROC
jextract %JEXTRACT_ARGS% %JEXTRACT_SYMBOLS%

您還可以將符號保存到文件中以便於編輯,並通過將上%JEXTRACT_SYMBOLS%的使用替換為@your.symbols來重新生成。

也可以看看:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM