Вам потребуется использовать API-интерфейсы Accessibility, которые имеют обычный C и находятся внутри инфраструктуры ApplicationServices. Например:
Сначала вы создаете объект приложения:
AXUIElementRef app = AXUIElementCreateApplication( targetApplicationProcessID );
Тогда вы получите окно от этого. Вы можете запросить список окон и перечислить, или вы можете получить самое переднее окно (посмотрите в AXAttributeConstants.h все имена атрибутов, которые вы будете использовать).
AXUIElementRef frontWindow = NULL;
AXError err = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, &frontWindow );
if ( err != kAXErrorSuccess )
// it failed -- maybe no main window (yet)
Теперь вы можете запросить уведомление через функцию обратного вызова C, когда свойство этого окна изменяется. Это четырехэтапный процесс:
Сначала вам понадобится функция обратного вызова для получения уведомлений:
void MyAXObserverCallback( AXObserverRef observer, AXUIElementRef element,
CFStringRef notificationName, void * contextData )
{
// handle the notification appropriately
// when using ObjC, your contextData might be an object, therefore you can do:
SomeObject * obj = (SomeObject *) contextData;
// now do something with obj
}
Далее вам нужен AXObserverRef, который управляет процедурой обратного вызова. Для этого требуется тот же идентификатор процесса, который вы использовали для создания элемента app выше:
AXObserverRef observer = NULL;
AXError err = AXObserverCreate( applicationProcessID, MyObserverCallback, &observer );
if ( err != kAXErrorSuccess )
// handle the error
Получив своего наблюдателя, следующий шаг - запросить уведомление о некоторых вещах. Смотрите AXNotificationConstants.h для полного списка, но для изменения окна вам, вероятно, понадобятся только эти два:
AXObserverAddNotification( observer, frontWindow, kAXMovedNotification, self );
AXObserverAddNotification( observer, frontWindow, kAXResizedNotification, self );
Обратите внимание, что последний параметр, который передает предполагаемый объект "self" в качестве contextData. Это не сохраняется, поэтому важно вызвать AXObserverRemoveNotification
, когда этот объект исчезнет.
Получив своего наблюдателя и добавив запросы на уведомления, вы теперь хотите прикрепить наблюдателя к своему циклу выполнения, чтобы вы могли отправлять эти уведомления асинхронным образом (или даже вообще):
CFRunLoopAddSource( [[NSRunLoop currentRunLoop] getCFRunLoop],
AXObserverGetRunLoopSource(observer),
kCFRunLoopDefaultMode );
AXUIElementRef
s являются объектами в стиле CoreFoundation, поэтому вам нужно использовать CFRelease()
, чтобы утилизировать их аккуратно. Например, для обеспечения чистоты вы должны использовать CFRelease(app)
после получения элемента frontWindow, так как вам больше не нужно приложение.
Примечание о сборке мусора: чтобы сохранить AXUIElementRef в качестве переменной-члена, объявите его так:
__strong AXUIElementRef frontWindow;
Это указывает сборщику мусора отслеживать эту ссылку на него. При назначении для совместимости с GC и не-GC используйте это:
frontWindow = (AXUIElementRef) CFMakeCollectable( CFRetain(theElement) );