简体   繁体   中英

Get titles of Non-Java Windows in Java

Window.getWindows();

Doesn't get all the open windows, just the java ones. Is there a way to get all the windows the operating system has open. I'm making a java taskbar.

There is no solution using core Java, but the problem can be solved using JNI or for an easier time, JNA. As noted in the comments, no solution (that I know of) will be platform independent.

For instance, this demo program uses JNA to enumerate all Windows with title in a Windows platform, but will also include windows that are not top-level windows and even windows that aren't visible:

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);
      }
   }


}

Addition to @Hovercraft Full Of Eels , if you also have windows which are titled with unreadable characters and get "?" printed for those characters, try to:

  • convert byte[] to string by using different encoding (as I understood)

see below:

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

Possible encodings instead of "windows-1254 (which worked well for Turkish characters )":

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

You can now call into native Windows APIs to read the titles of non-java windows with pure-Java code using Foreign Memory API and code bindings generated by a compatible jextract .

This example works with the preview code in JDK19 release and is the equivalent to Hovercraft Full Of Eels answer:

/** 
 * 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());
    }
}

The standalone jextract is used to generate bindings for calling native code. You need to create a header User32.h referencing suitable Windows API headers:

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

Run jextract to generate bindings for a suitable set of Windows APIs:

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%

You can also save symbols to a file for easy editing, and re-generate by replacing use of %JEXTRACT_SYMBOLS% above by @your.symbols .

See also:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM