Получение имен владельцев окон через CGWindowListCopyWindowInfo в Rust - PullRequest
0 голосов
/ 07 февраля 2020

Я пытаюсь получить имена владельцев окон через CGWindowListCopyWindowInfo в Rust. Пока мне удалось получить 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 и буду признателен за любую помощь.

Ответы [ 2 ]

1 голос
/ 09 февраля 2020

В дополнение к ответу 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)
    }
}
1 голос
/ 08 февраля 2020

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) };

Аргументу значения просто нужен необработанный двойной указатель для сохранения результата. Создайте нулевой указатель:

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

и передайте изменяемую ссылку на него (&mut value), чтобы получить *mut *const c_void.


Есть несколько ошибок и странностей в вашем коде ржавчины. Вот прокомментированный, рабочий пример:

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;
}

Отказ от ответственности I Я также относительно новичок в ржавчине, и это может быть идиоматизм c решение

...