如果您也想在MacOS平台上,监控、控制、获取别的程序信息,那么接下来的内容,将会对你有一些帮助。
Accessibility API
是一套MacOS平台下的Objective-C API
。使用这套API,可以让你在MacOS平台下,获取到各应用程序下的各种元素,包括窗体信息、窗体的按钮、窗体的操作事件等一系列东西.- 因为最近
Swift
大火,同时也是苹果的亲儿子,所以我接下里将阐述一些如何使用Swift
调用Accessibility API
。
宣传一下
在这里插播一下【DFAXUIElement】:我已经把Accessibility API
进行封装了,使得你可以快速的进行使用。库是swift版本同时也支持pod的安装。欢迎大家使用,如果有任何bug或者建议可以和我说。感谢大家的支持。
前置条件
-
根据苹果的尿性,这种可以跨程序进行操作的API,必须是有限制的,而且一般途径下,是无法通过正常方式进行商店上架的。而使用
Accessibility API
,您的APP就必须是非沙盒模式。而非沙盒模式的APP是无法发布到苹果商店的 -
而如果您正在开发MacOS的APP,同时使用了
Accessibility API
。那么巧您又必须上架到苹果商店。您可以参考stackoverflow
的文章,里面有解决方案。文章需要科学上网。跳转
1.非沙盒模式:
- 打开你的开发的APP的项目,然后打开
entitlements
文件,更改属性App Sandbox
为NO
.如下图:
2.Security & Privacy 中允许你的APP
- 必须在
System Preference
中->Security & Privacy
>Accessibility
中添加您的APP,并且勾选上.如下图:
再次宣传一下
WindowSnap
也是一个我基于Accessibility API
开发的一个APP,功能类似于Windows系统下的,鼠标拖动到屏幕某一个位置后,窗体自动进行分屏。这个APP完全开源并且会一直维护。传输门
确保了上述的前置条件都设置好了之后,您就可以快乐的使用Accessibility API
了.
Accessibility API包含的东西
AXUIElement
: 这个可以说是整个Accessibility API
最基础的元素了。他代表着程序里的每一个元素。AXError
: 几乎每一个Accessibility API
都会返回一个AXError
,从而通过此来表明,你调用的情况,包含成功、失败等状态。详情可看这个文章。传送门AXObserver
: 这是一个监听,可以根据指定的key,监听到这个key所对应的值的变化。详情可看这个文章。传送门AXValue
:这是一个特殊的value值,如果你的值是CGPoint
、CGSize
、CGRange
、CGRect
的时候,就要使用这个。详情看可看这个文章。传送门
而今天我们只介绍AXUIElement
,废话不多说,直接上代码
获取整个系统的AXUIElement
let systemRef:AXUIElement = AXUIElementCreateSystemWide()
获取Application
的AXUIElement
let applicationRef:AXUIElement = AXUIElementCreateApplication(pid)
只需要一个pid
就可以获取到指定程序的Application AXUIElemen
了。那么问题来了,pid
是什么?
pid
就是这个程序processIdentifier
,即进程ID
.因为进程每次重新打开都会变,所以我们就需要动态获取到进程的ID。关于如何获取进程ID,我们将在下一篇文章进行介绍。跳转
获取应用程序下所有的windows(窗口)
通过下述的方法,value就是该Application
下所有的windows
了,同时数组里面的每一个元素都是AXUIElement
类型
var value : AnyObject?
let axError:AXError = AXUIElementCopyAttributeValue(applicationRef!, kAXWindowsAttribute as CFString, &value)
if axError == .success{
if let windows = value as? [AXUIElement]{
print("all windows \(windows)")
}
}
可能现在很多人就会问了,kAXWindowsAttribute
这个是什么?
其实它是一个Atribute Name
,那我如何可以知道所有的值呢?你可以通过查阅AXAttributeConstants.h
那如果我想知道这个AXUIElement
,哪些Attribute Name
才是属于它的呢?那么你可以通过下面这个方法进行获取。
获取某个AXUIElement所有Attribute Name
var names : CFArray?
let error:AXError = AXUIElementCopyAttributeNames(applicationRef!, &names)
if let array = names as? [String]{
print("attribute name \(names)")
}
获取某个AXUIElement某个Attribute Name
的值
//windowRef是某一个window的AXUIElement
var position:AnyObject?
let error:AXError = AXUIElementCopyAttributeValue(window, kAXPositionAttribute as CFString, &position)
if error == .success{
//因为这个时候的position其实是一个AXValue的类型,所以需要对他进行进一步的处理
//从AXValue中获取值
if CFGetTypeID(position) == AXValueGetTypeID(){
let positionAXValue = position as! AXValue
if AXValueGetType(positionAXValue) == AXValueType.cgPoint{
var value = CGPoint.zero
let isSuceess = AXValueGetValue(positionAXValue, .cgPoint, &value)
if isSuceess{
print("i got the point , value is \(value)")
}else{
print("faile to get the point")
}
}
}
}
这样我们就获取了winodow的位置信息了,那我们如果想更改呢?
对某个AXUIElement的某个Attribute Name
的设值
var position = CGPoint.init(x: 100, y: 100)
//首先我们要创建一个AXValue,里面是包含着CGPoint
if let positionAXValue = AXValueCreate(AXValueType.cgPoint, &position){
//通过AXUIElementSetAttributeValue 对AXUIElement的attribute key【kAXPositionAttribute】设值
let error:AXError = AXUIElementSetAttributeValue(curWindowRef, kAXPositionAttribute as CFString, positionAXValue)
if error == .success {
print("set window position sucessed")
}
}
这样我们就完成了基本的获取和设置了。但有时候我们知道了某个Attribute Name
,但我怎么知道它是否是可进行设置呢?
判断某个AXUIElement的某个Attribute Name
的设值是否可设值
var attributeCanBeSet: DarwinBoolean = false;
let error:AXError = AXUIElementIsAttributeSettable(curWindowRef!, kAXPositionAttribute as CFString, &attributeCanBeSet)
if error == .success{
if attributeCanBeSet.boolValue{
print("attribute kAXPositionAttribute is write and set")
}else{
print("attribute kAXPositionAttribute is readonly")
}
}
那我们如果想对某个元素的事件,进行模拟操作呢?也是可以的。这里我们就拿窗口的最小化按钮,进行模拟点击。
获取window的最小化按钮:
var value:AnyObject?
//其中curWindowRef 是某一个window的AXUIElement
let error : AXError = AXUIElementCopyAttributeValue(curWindowRef!, kAXMinimizeButtonAttribute as CFString, &value)
if error == .success{
if value != nil && CFGetTypeID(value) == AXUIElementGetTypeID(){
let minimizedBtnRef:AXUIElement = value! as! AXUIElement
print("我是最小化按钮 \(minimizedBtnRef)")
}
}
模拟点击该按钮,以实现最小化功能
其中AXPressAction
是一个action的key,如果想知道更多,可以通过查阅AXActionConstants
minimizedBtnRef则是window最小化的按钮所对应的AXUIElement
let error:AXError = AXUIElementPerformAction(minimizedBtnRef, kAXPressAction as CFString)
if error == .success{
print("press sucessed")
}
那么一个AXUIElement
有多少个action key呢?都是什么呢?那么我们就可以进行如下代码,来获取该AXUIElement的所有action key
获取所有Action Keys
var actionNames:CFArray?
let error:AXError = AXUIElementCopyActionNames(minimizedBtnRef, &actionNames)
if error == .success{
print("all action keys is \(actionNames)")
}
这里基本就完成了对AXUIElement
的简单操作了。如果需要更深入了解,请参考苹果的开发文档。