我的应用程序如何检测到另一个应用程序窗口的更改? [英] How can my app detect a change to another app's window?

查看:31
本文介绍了我的应用程序如何检测到另一个应用程序窗口的更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Mac 上的 Cocoa 中,我想检测属于另一个应用程序的窗口何时被移动、调整大小或重新绘制.我该怎么做?

In Cocoa on the Mac, I'd like to detect when a window belonging to another app is moved, resized, or repainted. How can I do this?

推荐答案

您需要使用 Accessibility API,它们是纯 C 语言,位于 ApplicationServices 框架内.例如:

You would need to use the Accessibility APIs, which are plain-C, located inside the ApplicationServices framework. For instance:

首先创建一个应用程序对象:

First you create an application object:

AXUIElementRef app = AXUIElementCreateApplication( targetApplicationProcessID );

然后你就可以从这里得到窗口了.您可以请求窗口列表并进行枚举,也可以获取最前面的窗口(在 AXAttributeConstants.h 中查找您将使用的所有属性名称).

Then you get the window from this. You can request the window list and enumerate, or you can get the frontmost window (look in AXAttributeConstants.h for all the attribute names you'd use).

AXUIElementRef frontWindow = NULL;
AXError err = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, &frontWindow );
if ( err != kAXErrorSuccess )
    // it failed -- maybe no main window (yet)

现在您可以在此窗口的属性更改时通过 C 回调函数请求通知.这是一个四步过程:

Now you can request notification via a C callback function when a property of this window changes. This is a four-step process:

首先你需要一个回调函数来接收通知:

First you need a callback function to receive the notifications:

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,它管理回调例程.这需要与您用于创建上面的应用程序"元素相同的进程 ID:

Next you need an AXObserverRef, which manages the callback routine. This requires the same process ID you used to create the 'app' element above:

AXObserverRef observer = NULL;
AXError err = AXObserverCreate( applicationProcessID, MyObserverCallback, &observer );
if ( err != kAXErrorSuccess )
    // handle the error

获得观察者后,下一步是请求通知某些事情.有关完整列表,请参阅 AXNotificationConstants.h,但对于窗口更改,您可能只需要这两个:

Having got your observer, the next step is to request notification of certain things. See AXNotificationConstants.h for the full list, but for window changes you'll probably only need these two:

AXObserverAddNotification( observer, frontWindow, kAXMovedNotification, self );
AXObserverAddNotification( observer, frontWindow, kAXResizedNotification, self );

请注意,最后一个参数传递了一个假定的self"对象作为 contextData.这不会被保留,所以当这个对象消失时调用 AXObserverRemoveNotification 很重要.

Note that the last parameter there is passing an assumed 'self' object as the contextData. This is not retained, so it's important to call AXObserverRemoveNotification when this object goes away.

获得观察者并添加通知请求后,您现在想要将观察者附加到您的 runloop,以便您可以以异步方式(或实际上完全异步)发送这些通知:

Having got your observer and added notification requests, you now want to attach the observer to your runloop so you can be sent these notifications in an asynchronous manner (or indeed at all):

CFRunLoopAddSource( [[NSRunLoop currentRunLoop] getCFRunLoop],
                    AXObserverGetRunLoopSource(observer),
                    kCFRunLoopDefaultMode );

AXUIElementRefs 是 CoreFoundation 风格的对象,所以你需要使用 CFRelease() 来清理它们.例如,为了这里的清洁,您将在获得 frontWindow 元素后使用 CFRelease(app),因为您将不再需要该应用程序.

AXUIElementRefs are CoreFoundation-style objects, so you need to use CFRelease() to dispose of them cleanly. For cleanliness here, for example, you would use CFRelease(app) once you've obtained the frontWindow element, since you'll no longer need the app.

关于垃圾收集的注意事项:要将 AXUIElementRef 作为成员变量,请像这样声明:

A note about Garbage-Collection: To keep an AXUIElementRef as a member variable, declare it like so:

__strong AXUIElementRef frontWindow;

这会指示垃圾收集器跟踪对它的引用.分配时,为了兼容GC和非GC,使用这个:

This instructs the garbage collector to keep track of this reference to it. When assigning it, for compatibility with GC and non-GC, use this:

frontWindow = (AXUIElementRef) CFMakeCollectable( CFRetain(theElement) );

这篇关于我的应用程序如何检测到另一个应用程序窗口的更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆