如何将Swift数组转换为CFArray?

3

我正在尝试使用Swift在Mac OS X应用程序中捕获窗口列表。CGWindowListCreateImageFromArray函数需要一个CFArray。我尝试了几种方法,这是我最接近的方法。或者有更好的方法来转换数组吗?

import Cocoa

// Example swift array of CGWindowID's
var windowIDs = [CGWindowID]();
windowIDs.append(1);
windowIDs.append(2);

// Convert to CFArray using CFArrayCreate
let allocator = kCFAllocatorDefault
let numValues = windowIDs.count as CFIndex
let callbacks: UnsafePointer<CFArrayCallBacks> = nil
var values: UnsafeMutablePointer<UnsafePointer<Void>> = nil

/* how do I convert windowIDs to UnsafeMutablePointer<UnsafePointer<Void>> for the values? */

let windowIDsCFArray = CFArrayCreate(allocator, values,  numValues, callbacks);

let capture = CGWindowListCreateImageFromArray(CGRectInfinite, windowIDsCFArray, CGWindowImageOption(kCGWindowListOptionOnScreenOnly));
3个回答

5

只要将CGWindowID设置为CFTypeRef,您就可以使用数组初始化UnsafeMutablePointer

var windows: [CFTypeRef] = [1, 2]
var windowsPointer = UnsafeMutablePointer<UnsafePointer<Void>>(windows)
var cfArray = CFArrayCreate(nil, windowsPointer, windows.count, nil)

这是不正确的,很容易被忽略。它确实转换为CGWindowListCreateImageFromArray所期望的指针类型,但如果您尝试使用CFArrayGetValueAtIndex(cfArray, …).hashValuecfArray获取值,您会发现它们分别为12的窗口ID是311567 - 这些是将被捕获的窗口ID,而不是12。它们是相对较小的数字,因此在许多情况下将创建错误内容的图像。 - Ian Bytchek
@IanBytchek,你能解释一下它是如何发生的吗?也许实际上hashValue不是窗口ID? - allenlinli
@kellanburket 我该如何将CGWindowID转换为CFTypeRef? - allenlinli

2

Ian的答案转换成Swift 4:

let windows = [CGWindowID(17), CGWindowID(50), CGWindowID(59)]
let pointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: windows.count)
for (index, window) in windows.enumerated() {
    pointer[index] = UnsafeRawPointer(bitPattern: UInt(window))
}
let array: CFArray = CFArrayCreate(kCFAllocatorDefault, pointer, windows.count, nil)

let capture = CGImage(windowListFromArrayScreenBounds: CGRect.infinite, windowArray: array, imageOption: [])!
let image: NSImage = NSImage(cgImage: capture, size: NSSize.zero)

Swift.print(image)

1
回复内容存在敏感词^**$()方法是一种优雅的方法。
var windows: [CGWindowID] = [CGWindowID(1), CGWindowID(2)]
var array: CFArray = windows.map({NSNumber(unsignedInt: $0)}) as CFArray

然而,这并不反映实际的CGWindowListCreateImageFromArray问题。以下是解决该问题的有效方法:

let windows: [CGWindowID] = [CGWindowID(17), CGWindowID(50), CGWindowID(59)]
let pointer: UnsafeMutablePointer<UnsafePointer<Void>> = UnsafeMutablePointer<UnsafePointer<Void>>.alloc(windows.count)

for var i: Int = 0, n = windows.count; i < n; i++ {
    pointer[i] = UnsafePointer<Void>(bitPattern: UInt(windows[i]))
}

let array: CFArray = CFArrayCreate(kCFAllocatorDefault, pointer, windows.count, nil)
let capture: CGImage = CGWindowListCreateImageFromArray(CGRectInfinite, array, CGWindowImageOption.Default)!
let image: NSImage = NSImage(CGImage: capture, size: NSZeroSize)

Swift.print(image) // <NSImage 0x7f83a3d16920 Size={1440, 900} Reps=("<NSCGImageSnapshotRep:0x7f83a3d2dea0 cgImage=<CGImage 0x7f83a3d16840>>")>

我不是 ObjC 的专家,如果有错误请纠正,但是通过玩 SonOfGrab 示例并查看下面特定的代码片段,我理解最终指针结构包含窗口 id(UInt32),不在内存单元格中(UnsafePointer 实例的 memory 属性),而是在内存地址中(hashValue 属性)。
const void *windowIDs[2]; 
windowIDs[0] = 10; 
windowIDs[1] = 20; 

这很有趣,因为值并没有存储在内存中,而是存储在地址描述符中,最早的架构是32位,UInt32 值完美地适配了地址指针。也许在内存是限制因素的时代,这种方法十分明智和出色。但在2016年的 Swift 中发现了这一点,让我感到绝望。

更糟糕的是,它在Xcode 7.2的 playground 中由于内存处理方式的原因,与某些窗口ID无法运行,但在实际应用程序中可以工作。


它在Xcode 7.3 playground中失败了。我该如何让它工作? - allenlinli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接