通过Rust中的CGWindowListCopyWindowInfo获取窗口所有者名称

问题描述 投票:0回答:2

我正在尝试通过Rust中的CGWindowListCopyWindowInfo获取窗口所有者名称。到目前为止,我已经成功获取了CFDictionaryRef,但是我无法为CFDictionaryGetValueIfPresent使用正确的指针。

这是方法签名:https://docs.rs/CoreFoundation-sys/0.1.4/CoreFoundation_sys/dictionary/fn.CFDictionaryGetValueIfPresent.html

const options: CGWindowListOption = kCGWindowListOptionOnScreenOnly + kCGWindowListExcludeDesktopElements;
const ptr_window_list_info: CFArrayRef = unsafe { CGWindowListCopyWindowInfo(options, kCGNullWindowID) as CFArrayRef };

const count = unsafe { CFArrayGetCount(ptr_window_list_info) };

for i in 0..count-1 {
    let dic_ref = CFArrayGetValueAtIndex(ptr_window_list_info, i) as CFDictionaryRef;
    //let key = CFString::new("kCGWindowOwnerName");
    let b = CFDictionaryGetValueIfPresent(dic_ref, ?, ?);
    println!("window owner name: {}", value);
}

我对Rust还是陌生的,将不胜感激。

macos rust core-foundation
2个回答
1
投票

CFDictionaryGetValueIfPresent看起来像这样:

Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef theDict, const void *key, const void **value);

在core_foundation中,看起来像这样:

pub unsafe extern "C" fn CFDictionaryGetValueIfPresent(
    theDict: CFDictionaryRef, 
    key: *const c_void, 
    value: *mut *const c_void
) -> Boolean

我相信密钥希望是CFStringRef;在C语言中,有一个宏可以简化操作(CFSTR("cstring")),在这里我们必须使用稍长的形式:

let c_key = CString::new("kCGWindowOwnerName").unwrap();
let cf_key = unsafe { 
    CFStringCreateWithCString(std::ptr::null(), c_key.as_ptr(), kCFStringEncodingUTF8) 
};

这给我们CFStringRef,在core_foundation中是这样定义的:

pub type CFStringRef = *const __CFString;

它已经是*const指针,但不是我们需要的类型。您可以改为c_void

let key_ptr: *const c_void = unsafe { std::mem::transmute(cf_key) };

value参数只需要一个原始双指针来存储结果。创建一个空指针:

let mut value: *const c_void = std::ptr::null();

并传递可变的引用给它(&mut value)以得到*mut *const c_void


您的rust代码中有一些错误和奇怪之处。这是一个经过评论的有效示例:

use core_graphics::display::*;
use core_foundation::string::*;
use std::ffi::{ CStr, CString, c_void };

fn main() {
    // CGWindowListOption is a bitmask, combine the flags with bitwise OR
    const OPTIONS: CGWindowListOption = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
    // No need to specify the type or use 'as'; CFArrayRef is the return type from CGWindowListCopyWindowInfo
    let window_list_info = unsafe { CGWindowListCopyWindowInfo(OPTIONS, kCGNullWindowID) };
    // Don't use const here, CFArrayGetCount returns CFIndex (long)
    let count: i64 = unsafe { CFArrayGetCount(window_list_info) };

    for i in 0..count-1 {
        // Here we need the 'as', CFArrayGetValueAtIndex just returns void*
        let dic_ref = unsafe { CFArrayGetValueAtIndex(window_list_info, i) as CFDictionaryRef };

        // Create a CString from the key name we are interested in
        let c_key = CString::new("kCGWindowOwnerName").unwrap();
        // Create a CFString, needs to be released with `CFRelease`. I leave that as an exercise for the reader.
        let cf_key = unsafe { CFStringCreateWithCString(std::ptr::null(), c_key.as_ptr(), kCFStringEncodingUTF8) };

        // cf_key is a CFStringRef, which is a type alias to *const __CFString
        // We transmute it into *const c_void since that is what CFDictionaryGetValueIfPresent wants
        let key_ptr: *const c_void = unsafe { std::mem::transmute(cf_key) };

        // A raw void* to hold the result
        let mut value: *const c_void = std::ptr::null();

        if unsafe { CFDictionaryGetValueIfPresent(dic_ref, key_ptr, &mut value) != 0 } {
            // CFDictionaryGetValueIfPresent returned true; that means value must point to a CFStringRef
            let cf_ref = value as core_foundation::string::CFStringRef;
            // Get a pointer to a C-string buffer with the characters from the CFString
            let c_ptr = unsafe { CFStringGetCStringPtr(cf_ref, kCFStringEncodingUTF8) };

            // The value may be null; don't pass it to CStr::from_ptr
            if c_ptr.is_null() { continue; }

            // Wrap the pointer in a rust CStr so we can convert a str
            let c_value = unsafe { CStr::from_ptr(c_ptr) };
            println!("{}", c_value.to_str().unwrap());
        }
    }
}

供参考,这是C中的相同示例:

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CGWindowListOption options = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements;
        CFArrayRef windows = CGWindowListCopyWindowInfo(options, kCGNullWindowID);

        CFIndex count = CFArrayGetCount(windows);

        for (int i = 0; i < count; i++)
        {
            CFDictionaryRef windowDict = CFArrayGetValueAtIndex(windows, i);
            CFStringRef key = CFStringCreateWithCString(NULL, "kCGWindowOwnerName", kCFStringEncodingUTF8);
            const void* value = nil;

            if (CFDictionaryGetValueIfPresent(windowDict, key, &value) == YES)
            {
                const char* c_value = CFStringGetCStringPtr(value, kCFStringEncodingUTF8);

                NSLog(@"window: %s", c_value);
            }

            CFRelease(key);
        }
    }
    return 0;
}

Disclaimer我也对锈病还比较陌生,这可能不是惯用的解决方法


0
投票

除了TheNextman的答案外,我还修改了代码,以避免使用transmute。

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!("window owner name: {}", result)
            }

        }

    }

    unsafe {
        CFRelease(window_list_info as CFTypeRef)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.