1

I am looking at the docs for CFStringGetCString and AXUIElementCopyAttributeValue.

CFStringGetCString takes the param buffer: UnsafeMutablePointer<Int8>! AXUIElementCopyAttributeValue takes the param value: UnsafeMutablePointer<CFTypeRef?>

For the latter, I can do a call like this:

var value: CFTypeRef?
let err = AXUIElementCopyAttributeValue(element, attribute as CFString, &value);

This satisfies the doc asking for an UnsafeMutablePointer of type CFTypeRef.

However I can't get the same logic to apply by doing

let buffer: Int8!
CFStringGetCString(attribute as! CFString, &buffer, 2048, CFStringBuiltInEncodings.UTF8.rawValue)

I also tried

let buffer: Int8?
CFStringGetCString(attribute as! CFString, &buffer!, 2048, CFStringBuiltInEncodings.UTF8.rawValue)

Either way, it complains about using buffer before it's initialized, even though it never complained about value in the working method with similar param requirements.

All the working examples I've seen for CFStringGetCString are using objective-c like syntax with *. Not sure what the proper swift way is here.

I also tried this way to get the value I wanted:

let app = AXUIElementCreateSystemWide();
var valueString = "";

var value: CFTypeRef?
// An exception on execution happens here when passing app
// Passing in the element I clicked instead of app
// yields error -25205 (attributeunsupported)
let err = AXUIElementCopyAttributeValue(app, "AXFocusedApplication" as CFString, &value);
if (err == AXError.success) {
    valueString = value! as! NSString as String;
} else {
    print("ERROR!");
    print(err.rawValue);
}

return valueString;
steveax
  • 17,527
  • 6
  • 44
  • 59
Dave Stein
  • 8,653
  • 13
  • 56
  • 104
  • Does this help https://stackoverflow.com/questions/47487193/accessing-objective-c-pointers-in-swift ? – Martin R Apr 02 '19 at 17:30
  • @MartinR that basically shows how to do what I did, which worked for `AXUIElementCopyAttributeValue`. There is some extra complication between an `Int8` and `CFString` for `UnsafeMutablePointer` I can't figure out. I can't really initialize to `nil` in this case. – Dave Stein Apr 02 '19 at 17:36
  • There should be no need to use CFStringGetCString in Swift. You can (conditionally) *cast* the value to `String`. – What do you need the C string for? – Martin R Apr 02 '19 at 17:39
  • @MartinR I posted the solution I had originally tried before looping back to `CFStringGetCString` which is what I had seen used in C++ – Dave Stein Apr 02 '19 at 17:46
  • Did you try `if let valueString = value as? String { print(valueString) }` (as in the referenced Q&A)? – Martin R Apr 02 '19 at 17:47
  • @MartinR that is not the issue. I get an error before attempting to do anything to `valueString`. `err` is returned from `AXUIElementCopyAttributeValue` with the code I mentioned in sample above. `valueString` is not used in that method. – Dave Stein Apr 02 '19 at 17:50
  • Then I don't understand your question. If `AXUIElementCopyAttributeValue()` returns an error, what has that to do with how to call `CFStringGetCString()`? – Martin R Apr 02 '19 at 17:53
  • @MartinR I tried getting the value via `AXUIElementCopyAttributeValue`, but it doesn't support this attribute. That is the thing being used in the article you posted. When that failed, I figured I can use `CFStringGetCString`, but am stumbling over the proper creation of the buffer. – Dave Stein Apr 02 '19 at 17:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191115/discussion-between-martin-r-and-dave-stein). – Martin R Apr 02 '19 at 18:00

1 Answers1

1

Why are you torturing yourself with CFStringGetCString? If you have a CFString in Swift, you can cast it to a String and get a C string from that:

let cString: [Int8] = (cfString as String).cString(using: .utf8)!

Note also that the value of the kAXFocusedApplicationAttribute is not a CFString. It is an AXUIElement.

Here's my playground test:

import Foundation
import CoreFoundation

let axSystem = AXUIElementCreateSystemWide()
var cfValue: CFTypeRef?
AXUIElementCopyAttributeValue(axSystem, kAXFocusedApplicationAttribute as CFString, &cfValue)
if let cfValue = cfValue, CFGetTypeID(cfValue) == AXUIElementGetTypeID() {
    let axFocusedApplication = cfValue
    print(axFocusedApplication)
}

The first time I executed this playground, I got a system dialog box telling me that I need to give Xcode permission to control my computer. I went to System Preferences > Security & Privacy > Privacy > Accessibility, found Xcode at the bottom of the list, and turned on its checkbox.

Here's the output of the playground:

<AXUIElement Application 0x7fb2d60001c0> {pid=30253}

I assume you're on macOS since the AXUI API is only available on macOS. If you just want the name of the front application as a string, you can do this:

if let frontAppName = NSWorkspace.shared.frontmostApplication?.localizedName {
    print(frontAppName)
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I was gonna come back and write that I learned about that `kAXFocusedApplicationAttribute` constant but you beat me to updating the question :) – Dave Stein Apr 04 '19 at 15:26