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

Получение номера окна через API совместимости с OSX

Я работаю над приложением, которое перемещает окна сторонних приложений на экране.

Чтобы получить обзор всех открытых в настоящее время окон, я использую

CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);

Возвращает массив словарей, определяющих каждое открытое окно. Здесь возвращен примерный словарь:

{
    kCGWindowAlpha = 1;
    kCGWindowBounds =         {
        Height = 442;
        Width = 475;
        X = 3123;
        Y = "-118";
    };
    kCGWindowIsOnscreen = 1;
    kCGWindowLayer = 0;
    kCGWindowMemoryUsage = 907184;
    kCGWindowName = Untitled;
    kCGWindowNumber = 7328;
    kCGWindowOwnerName = TextEdit;
    kCGWindowOwnerPID = 20706;
    kCGWindowSharingState = 1;
    kCGWindowStoreType = 2;
    kCGWindowWorkspace = 3;
},

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

Теперь я использую PID (kCGWindowOwnerPID) для создания объекта доступности для оконного приложения:

AXUIElementRef app = AXUIElementCreateApplication(pid);

Затем следуя извлечению списка всех окон, которые приложение открыло с помощью AXUIElementCopyAttributeValues:

NSArray *result;

AXUIElementCopyAttributeValues(
                               (AXUIElementRef) app, 
                               kAXWindowsAttribute,
                               0,
                               99999,
                               (CFArrayRef *) &result
                               );

Это работает и возвращает массив AXUIElements. Вот где я застрял. Кажется, нет вызова API для извлечения окна "Окно" объекта доступности. Есть ли способ либо

a) Найдите объект Window Accessibility (в конечном итоге перейдите по массиву и найдите нужное окно)

или

b) В противном случае явно соответствует окно, описанное в массиве, возвращаемом CGWindowListCopyWindowInfo, в объекты доступности, возвращаемые AXUIElementCopyAttributeValues?

4b9b3361

Ответ 1

В итоге мы наняли выделенного разработчика специальных возможностей для этой задачи.

Оказывается, нет никакого способа сделать это, не используя недокументированные API (в нашем случае не годится).

К счастью, существует практическое решение:

Прокрутите все открытые окна приложения. Получите свою позицию, размер и название:

AXUIElementCopyAttributeValue(target, kAXPositionAttribute, CFTypeRef*)&posValue);
AXUIElementCopyAttributeValue(target, kAXSizeAttribute, (CFTypeRef*)&sizeValue);
AXUIElementCopyAttributeValue(target, kAXTitleAttribute, (CFTypeRef*)&titleValue);

Затем преобразуйте положение и размер в фактические значения CGPoint и CGSize:

AXValueGetValue(posValue, kAXValueCGPointType, &point);
AXValueGetValue(sizeValue, kAXValueCGSizeType, &size);

Сравните размер, положение и заголовок с значениями, возвращаемыми объектом в CGWindowListCopyWindowInfo(). Если они совпадают, вы можете с уверенностью утверждать, что это окно, которое вы искали, и использовать уже открытый AXUIElement (target в нашем случае) для его работы.

Накладные расходы для прокрутки всех открытых окон оказываются незначительными в OSX. Достаточно низкая максимальная скорость открытия окна одновременно.

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

Ответ 2

Существует частная функция для получения номера окна CG для данного объекта AX для окна: _AXUIElementGetWindow. Подробнее о SO обсуждение Уникальное определение активного окна в OS X Похоже, что нет публичного API для выполнения задачи со 100% -ной вероятностью. Идентификация окон по заголовку и кадру (как описано в ответе выше) будет работать в 99,9% случаев.