I am trying to get the window owner names via CGWindowListCopyWindowInfo. So far I have managed to get the CFDictionaryRef but I am unable to use the right pointers for CFDictionaryGetValueIfPresent .
You should call it exactly as if you were to call it in C, ie. pass the key (which is itself a pointer), and pass a pointer to a previously-declared pointer, which will be populated with a pointer to the value (if it exists), i.e.:
let mut value: *const c_void = ptr::null();
CFDictionaryGetValueIfPresent(dict, key as *const c_void, &mut value);
By the way, you probably want to loop over the range 0..count. The .. operator creates a half-open interval, its upper bound is exclusive.
Do you get any sort of error message, or is it just segfaulting? I'd run the program under gdb (or whatever debugger you use) and print a backtrace when it intercepts the crash.
Okay, seems I had to use NSString instead of CFString. I had a look at the call stack and saw the last call was objectForKey which gave me the clue about the wrong object.
let key = NSString::from_str("kCGWindowOwnerName");
let key_ptr: *const NSString = &*key;
Thanks for the hint to backtrace it. Now I am getting the right pointer
No, that is wrong. More precisely, that's a red herring. NSString and CFString are toll-free bridged – they are basically two ways of referring to the same type.
The problem is that in the first snippet, you are trying to pass a pointer to the key. Why? You don't need that as an object is already represented by a pointer. Chances are the CFDictionary implementation is trying to call its hash method internally, but since the pointer you passed in is wrong (it points to a pointer, not an object!, it tries to interpret some garbage value as the class metadata or a function pointer, and crashes.
Just pass key itself, but that's already what I wrote in the first place when I provided the example. Anyway, it should work as either an NSString or as a CFString. It happened to work in your second snippet because you passed a pointer to the dereferenced key, ie. &*key, which is just key itself.
H2CO3, thanks for your detailed answer. I have tried passing key as you described but that gives a compiler error for key as *const c_void:
non-primitive cast: core_foundation::string::CFString as *const std::ffi::c_void
note: an as expression can only be used to convert between primitive types. Consider using the From trait
let key: CFString = CFString::new("kCGWindowOwnerName");
let mut value: *const c_void = std::ptr::null();
let b = unsafe { CFDictionaryGetValueIfPresent(dic, key as *const c_void, &mut value) };
The type should be CFStringRef. CoreFoundation objects are always accessed through pointers, so trying to get them by value doesn't really make sense or work.
If an immediate cast fails, you can use an intermediate cast through *const () or *const u8 or something like that (although look it up in the Nomicon, because I'm by no means an unsafe code expert). Don't use transmute, it's incredibly unsafe, and I'm not at all sure its usage is any correct here.
After I have added use core_foundation::base::*; I have been able to use the trait implementation ToVoid. I understand your criticism regarding transmute and have updated my code.
use core_graphics::display::*;
use core_foundation::string::*;
use core_foundation::number::*;
use core_foundation::base::*;
use std::ffi::{ CStr, c_void };
fn main() {
const OPTIONS: CGWindowListOption = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
let window_list_info = unsafe { CGWindowListCopyWindowInfo(OPTIONS, kCGNullWindowID) };
let count = unsafe { CFArrayGetCount(window_list_info) };
for i in 0..count {
let dic_ref = unsafe { CFArrayGetValueAtIndex(window_list_info, i as isize) as CFDictionaryRef };
let key = CFString::new("kCGWindowOwnerName");
let mut value: *const c_void = std::ptr::null();
if unsafe { CFDictionaryGetValueIfPresent(dic_ref, key.to_void(), &mut value) != 0 } {
let cf_ref = value as CFStringRef;
let c_ptr = unsafe { CFStringGetCStringPtr(cf_ref, kCFStringEncodingUTF8) };
if !c_ptr.is_null() {
let c_result = unsafe { CStr::from_ptr(c_ptr) };
let result = String::from(c_result.to_str().unwrap());
println!("{}", result)
}
}
}
unsafe {
CFRelease(window_list_info as CFTypeRef)
}
}