I’m trying to patch an application that resizes windows using the accessibility API.
我正在尝试使用易访问性API来修补一个应用程序。
I need to maintain a dictionary with the previous sizes of windows. The key needs to identify the currently active window. At the moment, this active window is retrieved via NSAccessibilityFocusedWindowAttribute
upon the press of a hotkey.
我需要维护一个与以前的窗口大小相同的字典。关键需要识别当前活动窗口。目前,这个活动窗口是通过一个热键的媒体上的nsaccessibilityfocusedwindowsattribute检索的。
However, every time this method is called, the returned AXUIElementRef
which identifies the window is different! This of course means that I cannot use it as a dictionary key – the dictionary won’t find the corresponding entry.
但是,每次调用此方法时,返回的AXUIElementRef标识窗口就不同了!这当然意味着我不能把它当作字典键——字典找不到相应的条目。
The following code reproduces the problem:
下面的代码重现了这个问题:
-(IBAction)testWindowIdentification:(id)sender{
AXUIElementRef focusedApp;
AXUIElementRef focusedWindow;
AXUIElementCopyAttributeValue(_systemWideElement,
(CFStringRef) kAXFocusedApplicationAttribute,
(CFTypeRef*) &focusedApp);
AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp,
(CFStringRef) NSAccessibilityFocusedWindowAttribute,
(CFTypeRef*) &focusedWindow);
CFShow(focusedWindow);
}
_systemWideElement
has been initialised in the init
method using a call to AXUIElementCreateSystemWide()
.
_systemWideElement已在init方法中初始化,使用对AXUIElementCreateSystemWide()的调用。
The CFShow
statement clearly shows different IDs every time the method is called (even though the same window is active), which is useless for me:
每次调用该方法时,CFShow语句清楚地显示不同的id(尽管同一个窗口是活动的),这对我来说是无用的:
<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…
The documentation on AXUIElement
shows no method that retrieves a unique attribute for the UI element, and neither does that of the NSAccessibility
protocol. The unique PID is not enough for me, since a process can have multiple windows.
在AXUIElement上的文档没有显示为UI元素检索唯一属性的方法,也不显示NSAccessibility协议。对于我来说,唯一的PID是不够的,因为一个进程可以有多个窗口。
How can I retrieve some unique identifier of the active window in Cocoa?
如何检索Cocoa中活动窗口的唯一标识符?
(By the way, the real code is checking the return codes in the above calls; there is no error, the calls succeed.)
(顺便说一下,真正的代码是检查上述调用的返回代码;没有错误,调用成功。
2 个解决方案
#1
16
Rob Keniger has the right strategy with his answer here. The only thing missing from this answer (and indeed, the reason for the bounty placement) is a workable implementation that takes the current active window and translates it into a unique key suitable for indexing in the context of the current working application.
Rob Keniger在这里的答案是正确的。从这个答案中唯一缺少的东西(实际上,是悬置的原因)是一个可行的实现,它采用当前的活动窗口,并将其转换为适合于当前工作应用程序上下文中的索引的唯一键。
Rob's solution sketches this out through use of the CGWindowID
given in the context of Quartz Window Services. It is, of course, strongly implied that this window reference is only useful for your current application.
罗布的解决方案是通过在Quartz窗口服务的上下文中使用CGWindowID来解决这个问题的。当然,它强烈暗示这个窗口引用只对您当前的应用程序有用。
Getting this window reference is tricky, because no strong guarantees exist between the Accessibility API and Quartz Window Services. However, you can work around this in the following ways:
获取这个窗口引用是很麻烦的,因为在可访问性API和Quartz窗口服务之间没有强大的保证。但是,您可以通过以下方式来解决这个问题:
-
Use
extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);
, as documented here. This isn't guaranteed to work, but it works as a ground-floor test to get things started if it works in your version of OSX.使用外部“C”AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);这并不能保证工作,但是如果它在你的OSX版本中工作的话,它可以作为一种底层测试来启动。
-
Get the
CGWindowID
directly, using, for example,HIWindowGetCGWindowID()
. More details about selecting the active window and extracting the ID can be found in the reference manual for the Carbon Window Manager (warning: large PDF).直接获取CGWindowID,例如,HIWindowGetCGWindowID()。关于选择活动窗口和提取ID的更多细节可以在Carbon window Manager的参考手册中找到(警告:大型PDF)。
-
Catalog your
CGWindowID
set using something likeCGWindowListCreateDescriptionFromArray
, exactly as Rob suggested. The goal here is then to find some scheme for bridging the Accessibility API and Quartz, but this is conceivable by utilizing, for example, a callback bound to the context of your current active window. I honestly don't know an optimal example of this that's properly future-proofed, however.使用诸如CGWindowListCreateDescriptionFromArray之类的东西对您的CGWindowID进行编目,就像Rob建议的那样。这里的目标是找到一些用于连接可访问性API和Quartz的方案,但这可以通过使用,例如,绑定到当前活动窗口的上下文来实现。老实说,我不知道一个最好的例子,这是正确的未来证明。
Of the options, I recommend going with 2.
for your current needs, if you're unable to create some other decorator for your windows to uniquely identify them. It's currently defined in the legacy code base, but it will do what you desire.
在选项中,我建议使用2。对于您当前的需求,如果您无法为您的窗口创建其他的decorator,以惟一地标识它们。它目前在遗留代码库中定义,但它将实现您所希望的。
Best of luck with your application.
祝你的申请好运。
#2
8
I think you might be able to use the Quartz Window Services functions, specifically CGWindowListCreateDescriptionFromArray
to enumerate the currently active windows in a particular app.
我认为您可能可以使用Quartz窗口服务功能,特别是CGWindowListCreateDescriptionFromArray来枚举特定应用程序中当前活动的窗口。
This call is lower-level than AppKit and isn't going to tell you which is the active window, but it will give you window IDs that are unique for the current user session. It's not a great solution, but you could compare the window bounds information with what you receive from the accessibility APIs to associate windows with their real IDs.
这个调用的级别比AppKit低,它不会告诉您哪个是活动窗口,但是它会给您提供当前用户会话惟一的窗口id。这不是一个很好的解决方案,但是您可以将窗口边界信息与您从可访问性api获得的信息进行比较,从而将窗口与它们的真实id相关联。
#1
16
Rob Keniger has the right strategy with his answer here. The only thing missing from this answer (and indeed, the reason for the bounty placement) is a workable implementation that takes the current active window and translates it into a unique key suitable for indexing in the context of the current working application.
Rob Keniger在这里的答案是正确的。从这个答案中唯一缺少的东西(实际上,是悬置的原因)是一个可行的实现,它采用当前的活动窗口,并将其转换为适合于当前工作应用程序上下文中的索引的唯一键。
Rob's solution sketches this out through use of the CGWindowID
given in the context of Quartz Window Services. It is, of course, strongly implied that this window reference is only useful for your current application.
罗布的解决方案是通过在Quartz窗口服务的上下文中使用CGWindowID来解决这个问题的。当然,它强烈暗示这个窗口引用只对您当前的应用程序有用。
Getting this window reference is tricky, because no strong guarantees exist between the Accessibility API and Quartz Window Services. However, you can work around this in the following ways:
获取这个窗口引用是很麻烦的,因为在可访问性API和Quartz窗口服务之间没有强大的保证。但是,您可以通过以下方式来解决这个问题:
-
Use
extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);
, as documented here. This isn't guaranteed to work, but it works as a ground-floor test to get things started if it works in your version of OSX.使用外部“C”AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);这并不能保证工作,但是如果它在你的OSX版本中工作的话,它可以作为一种底层测试来启动。
-
Get the
CGWindowID
directly, using, for example,HIWindowGetCGWindowID()
. More details about selecting the active window and extracting the ID can be found in the reference manual for the Carbon Window Manager (warning: large PDF).直接获取CGWindowID,例如,HIWindowGetCGWindowID()。关于选择活动窗口和提取ID的更多细节可以在Carbon window Manager的参考手册中找到(警告:大型PDF)。
-
Catalog your
CGWindowID
set using something likeCGWindowListCreateDescriptionFromArray
, exactly as Rob suggested. The goal here is then to find some scheme for bridging the Accessibility API and Quartz, but this is conceivable by utilizing, for example, a callback bound to the context of your current active window. I honestly don't know an optimal example of this that's properly future-proofed, however.使用诸如CGWindowListCreateDescriptionFromArray之类的东西对您的CGWindowID进行编目,就像Rob建议的那样。这里的目标是找到一些用于连接可访问性API和Quartz的方案,但这可以通过使用,例如,绑定到当前活动窗口的上下文来实现。老实说,我不知道一个最好的例子,这是正确的未来证明。
Of the options, I recommend going with 2.
for your current needs, if you're unable to create some other decorator for your windows to uniquely identify them. It's currently defined in the legacy code base, but it will do what you desire.
在选项中,我建议使用2。对于您当前的需求,如果您无法为您的窗口创建其他的decorator,以惟一地标识它们。它目前在遗留代码库中定义,但它将实现您所希望的。
Best of luck with your application.
祝你的申请好运。
#2
8
I think you might be able to use the Quartz Window Services functions, specifically CGWindowListCreateDescriptionFromArray
to enumerate the currently active windows in a particular app.
我认为您可能可以使用Quartz窗口服务功能,特别是CGWindowListCreateDescriptionFromArray来枚举特定应用程序中当前活动的窗口。
This call is lower-level than AppKit and isn't going to tell you which is the active window, but it will give you window IDs that are unique for the current user session. It's not a great solution, but you could compare the window bounds information with what you receive from the accessibility APIs to associate windows with their real IDs.
这个调用的级别比AppKit低,它不会告诉您哪个是活动窗口,但是它会给您提供当前用户会话惟一的窗口id。这不是一个很好的解决方案,但是您可以将窗口边界信息与您从可访问性api获得的信息进行比较,从而将窗口与它们的真实id相关联。