Как получить список всех дескрипторов окон на Java (используя JNA) в macOS?

#java #macos #jna

#java #macos #jna

Вопрос:

Я использовал библиотеку JNA для получения всех видимых дескрипторов окон в Windows. Мне нужно сделать что-то подобное в macOS с использованием JNA.

Вот код для получения всех дескрипторов окон в Windows:

  public static List<HWND> findAll() {
    final List<HWND> windows = new LinkedList<>();
    User32.INSTANCE.EnumWindows(new User32.WNDENUMPROC() {
        @Override
        public boolean callback(HWND hWnd, Pointer arg) {
            if (User32.INSTANCE.IsWindowVisible(hWnd)) {
                windows.add(hWnd);
            }
            return true;
        } 
    }, null);
    return windows;
}
  

Какой эквивалентный код в macOS?

Ответ №1:

Вам нужно будет отобразить части Core Graphics Framework. Вы можете составить список окон, используя функцию CGWindowListCopyWindowInfo().

Для загрузки фреймворка вам нужно сопоставить CoreGraphics интерфейс, расширяющий Library класс JNA, и сопоставить нужную вам функцию:

 public interface CoreGraphics extends Library {
    CoreGraphics INSTANCE = Native.load("CoreGraphics", CoreGraphics.class);

    CFArrayRef CGWindowListCopyWindowInfo(int option, int relativeToWindow);
}
  

CFArrayRef Тип уже отображен в JNA в CoreFoundation классе. Выберите соответствующий параметр списка окон (вероятно kCGWindowListOptionAll , = 0). Если у вас уже был номер окна, вы могли бы использовать относительные ссылки, в противном случае вы будете использовать kCGNullWindowID (0) для второго параметра. Вызов его из вашего кода должен быть простым:

 CFArrayRef windowInfo = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(0, 0);
  

Это даст вам массив CFDictionaryRef объектов, представляющих окна. Вы можете выполнить итерацию массива, а затем использовать дополнительные методы в CFDictionaryRef классе для изучения этих объектов словаря: вы создадите CFString для ключей. Список обязательных ключей задокументирован здесь, а дополнительные ключи — здесь. Строки констант соответствуют имени переменной.

Это должно дать вам CFNumberRef для каждого номера окна (эквивалент «дескриптора»):

 // Set up keys for dictionary lookup
CFStringRef kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
CFStringRef kCGWindowOwnerPID = CFStringRef.createCFString("kCGWindowOwnerPID");
// Note: the Quartz name is rarely used
CFStringRef kCGWindowName = CFStringRef.createCFString("kCGWindowName");
CFStringRef kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");

// Iterate the array
int numWindows = windowInfo.getCount();
for (int i = 0; i < numWindows; i  ) {
    // For each array element, get the dictionary
    Pointer result = windowInfo.getValueAtIndex(i);
    CFDictionaryRef windowRef = new CFDictionaryRef(result);

    // Now get information from the dictionary.

    // Get a pointer to the result, in this case a CFNumber
    result = windowRef.getValue(kCGWindowNumber);
    // "Cast" the pointer to the appropriate type
    CFNumberRef windowNumber = new CFNumberRef(result);
    // CoreFoundation.INSTANCE.CFNumberGetType(windowNumber)
    // --> 4 = kCFNumberSInt64Type, signed 64 bit so use getLong()
    
    // Get a pointer to the result, in this case a CFNumber
    result = windowRef.getValue(kCGWindowOwnerPID);
    // "Cast" the pointer to the appropriate type
    CFNumberRef windowOwnerPID = new CFNumberRef(result);
    // CoreFoundation.INSTANCE.CFNumberGetType(windowOwnerPID)
    // --> 4 = kCFNumberSInt64Type, signed 64 bit so use getLong()

    // Get a pointer to the result, in this case a CFString
    result = windowRef.getValue(kCGWindowName);
    // "Cast" the pointer to the appropriate type
    // Optional key, check for null
    String windowName = result == null ? "" : new CFStringRef(result).stringValue();

    // Get a pointer to the result, in this case a CFString
    result = windowRef.getValue(kCGWindowOwnerName);
    // "Cast" the pointer to the appropriate type
    // Optional key, check for null
    String windowOwnerName = result == null ? "" : new CFStringRef(result).stringValue();

    // ... look up other keys if needed ...
    // use ProcessHandle with the PID to get start time

    // Output or add to List, etc.
    System.out.println(windowNumber.longValue() 
          " ("   windowOwnerName   ", pid=" 
          windowOwnerPID.longValue()
          "): "   windowName);
}

// CF references from "Copy" or "Create" must be released
// release the created key references
kCGWindowNumber.release();
kCGWindowOwnerPID.release();
kCGWindowName.release();
kCGWindowOwnerName.release();
// release the array
windowInfo.release();
  

Комментарии:

1. Как насчет CoreGraphics? CoreGraphics также есть в JNA?

2. Вы создадите свой собственный класс extending Library . Я включил синтаксис в свой ответ.

3. Как я могу получить название окна (title), имя процесса, идентификатор процесса и время запуска процесса?

4. Название окна находится в этом информационном словаре с использованием ключа kCGWindowName . Имя процесса-владельца указано в ключе kCGWindowOwnerName . Идентификатор процесса-владельца находится в kCGWindowOwnerPID . Чтобы узнать время начала, вы должны использовать PID для получения ProcessHandle в Java 9 .

5. Спасибо за помощь и время .. Надеюсь, это сработает на mac @Daniel Widdis