Уникальная идентификация активного окна в OS X - PullRequest
28 голосов
/ 15 сентября 2011

Я пытаюсь установить приложение , которое изменяет размеры окон с помощью 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 ]

17 голосов
/ 13 апреля 2012

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

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

Получить эту ссылку на окно довольно сложно, поскольку между 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. для ваших текущих потребностей, если вы не можете создать какой-то другой декоратор для своих окон, чтобы однозначно идентифицировать их. В настоящее время он определен в устаревшей кодовой базе, но он будет делать то, что вы хотите.

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

9 голосов
/ 15 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...