【问题标题】:How can my app detect a change to another app's window?我的应用程序如何检测到另一个应用程序窗口的更改?
【发布时间】:2010-10-25 15:02:36
【问题描述】:

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

【问题讨论】:

    标签: objective-c cocoa macos


    【解决方案1】:

    您需要使用位于 ApplicationServices 框架内的可访问性 API,它们是纯 C 语言。例如:

    首先你创建一个应用对象:

    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”元素时使用的进程 ID 相同:

    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 );
    

    AXUIElementRefs 是 CoreFoundation 风格的对象,所以你需要使用CFRelease() 来干净地处理它们。举个例子,为了整洁,一旦你获得了 frontWindow 元素,你就可以使用CFRelease(app),因为你不再需要这个应用程序了。

    关于 Garbage-Collection 的说明:要将 AXUIElementRef 保留为成员变量,请按如下方式声明:

    __strong AXUIElementRef frontWindow;
    

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

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

    【讨论】:

    【解决方案2】:

    进一步研究出现了“石英显示服务”

    我需要的有趣功能是 CGRegisterScreenRefreshCallback。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-03
      • 1970-01-01
      • 1970-01-01
      • 2011-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多