Уникальная идентификация активного окна в 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, поскольку у процесса может быть несколько окон.

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

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

2 ответа

Решение

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

Решение Роба делает набросок этого через использование CGWindowID дано в контексте услуг кварцевых окон. Конечно, настоятельно подразумевается, что эта ссылка на окно полезна только для вашего текущего приложения.

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

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

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

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

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

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

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

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

Другие вопросы по тегам