Подтвердить что ты не робот

Уникальное определение активного окна OS X

Я пытаюсь установить приложение, которое изменяет размеры окон с помощью API доступности.

Мне нужно поддерживать словарь с предыдущими размерами окон. Ключ должен идентифицировать текущее активное окно. На данный момент это активное окно извлекается через NSAccessibilityFocusedWindowAttribute после нажатия горячей клавиши.

Однако, каждый раз, когда вызывается этот метод, возвращаемый AXUIElementRef, который идентифицирует окно, отличается! Это, конечно, означает, что я не могу использовать его в качестве словарного ключа - словарь не найдет соответствующую запись.

Следующий код воспроизводит проблему:

-(IBAction)testWindowIdentification:(id)sender{
    AXUIElementRef focusedApp;
    AXUIElementRef focusedWindow;

    AXUIElementCopyAttributeValue(_systemWideElement,
                                  (CFStringRef) kAXFocusedApplicationAttribute,
                                  (CFTypeRef*) &focusedApp);
    AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp,
                                  (CFStringRef) NSAccessibilityFocusedWindowAttribute,
                                  (CFTypeRef*) &focusedWindow);
    CFShow(focusedWindow);
}

_systemWideElement был инициализирован в методе init, используя вызов AXUIElementCreateSystemWide().

Оператор CFShow четко показывает разные идентификаторы при каждом вызове метода (даже при том, что одно и то же окно активно), что бесполезно для меня:

<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…

Документация на AXUIElement не отображает метод, который извлекает уникальный атрибут для элемента пользовательского интерфейса, а также протокола NSAccessibility. Единственного PID для меня недостаточно, так как процесс может иметь несколько окон.

Как получить уникальный идентификатор активного окна в Cocoa?

(Кстати, реальный код проверяет коды возврата в вышеуказанных вызовах, нет ошибки, вызовы преуспевают).

4b9b3361

Ответ 1

Роб Кенигер имеет правильную стратегию с своим ответом здесь. Единственное, чего не хватает в этом ответе (и, действительно, причина размещения баунти), - это выполнимая реализация, которая принимает текущее активное окно и переводит его в уникальный ключ, подходящий для индексирования в контексте текущего рабочего приложения.

Решение Rob набросает это с помощью CGWindowID, заданного в контексте Quartz Window Services. Разумеется, подразумевается, что эта ссылка на окно полезна только для вашего текущего приложения.

Получение этой ссылки на окно сложно, потому что нет надежных гарантий между API Accessibility и Quartz Window Services. Однако вы можете обойти это следующим образом:

  • Используйте extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);, здесь. Это не гарантированно работает, но он работает как тест на первом этаже, чтобы начать работу, если он работает в вашей версии OSX.

  • Получить CGWindowID напрямую, используя, например, HIWindowGetCGWindowID(). Более подробную информацию о выборе активного окна и извлечении идентификатора можно найти в справочное руководство для Window Window Manager (предупреждение: большой PDF).

  • Закажите каталог CGWindowID, используя что-то вроде CGWindowListCreateDescriptionFromArray, точно так же, как предложил Роб. Цель здесь заключается в том, чтобы найти некоторую схему для преодоления API Accessibility и Quartz, но это возможно, используя, например, обратный вызов, связанный с контекстом вашего текущего активного окна. Я, честно говоря, не знаю оптимального примера этого, который, однако, был бы надежно защищен будущим.

Из вариантов я рекомендую перейти с 2. для ваших текущих потребностей, если вы не можете создать какой-либо другой декоратор для своих окон, чтобы однозначно идентифицировать их. В настоящее время он определен в базе устаревшего кода, но он будет делать то, что вы хотите.

Желаем удачи в вашем приложении.

Ответ 2

Я думаю, что вы могли бы использовать функции Quartz Window Services, в частности CGWindowListCreateDescriptionFromArray, чтобы перечислять текущие активные окна в определенном приложении.

Этот вызов является более низким, чем AppKit, и не собирается сообщать вам, что является активным окном, но он даст вам идентификаторы окон, которые являются уникальными для текущего сеанса пользователя. Это не отличное решение, но вы можете сравнить информацию о границах окон с тем, что вы получаете от API доступности, чтобы связать окна с их реальными идентификаторами.