Как получить путь выбранного файла в текущей вкладке Проводника Windows на Rust?

Вопрос или проблема

Конечная цель:

  • Открыть Проводник (несколько вкладок) в win11.
  • Выбрать файл
  • Нажать клавишу пробела, чтобы получить путь к выбранному файлу в вашей программе

Текущий прогресс:
Все выбранные пути к файлам получены.

Что я пробовал:

  1. С помощью IShellWindows

Не удалось получить путь к файлу, который в данный момент выбран на вкладке Проводника.

fn get_select_file_path(hwnd: HWND) -> Option<String> {
    // hwnd это дескриптор активного окна, полученный через WindowsAndMessaging::GetForegroundWindow();
        unsafe {
            // Инициализация библиотеки COM
            let com = CoInitializeEx(None, COINIT_DISABLE_OLE1DDE);
            if com.is_err() {
                return None;
            }

            let hr: Result<IShellWindows, windows::core::Error> =
                CoCreateInstance(&ShellWindows, None, CLSCTX_LOCAL_SERVER);
            // let hr = CoCreateInstance(&ShellWindows, None, CLSCTX_INPROC_HANDLER);
            if hr.is_err() {
                println!("Ошибка создания IShellWindows");
                CoUninitialize(); // Очистка COM
                return None; // Ошибка создания IShellWindows
            }
            let shell_windows = hr.unwrap();

            let mut target_path = None;
            let count = shell_windows.Count().unwrap_or_default();

            for i in 0..count {
                let variant = VARIANT::from(i);
                let window: IDispatch = shell_windows.Item(&variant).ok()?;
                let web_browser: IWebBrowser2 = window.cast().ok()?;
                // Проверка, соответствует ли окно текущему активному окну
                let item_hwnd = web_browser.HWND().ok()?;
                if item_hwnd.0 != hwnd.0 as isize {
                    continue;
                }
                let a = web_browser.LocationURL().ok()?;
                println!("web_browser Путь: {:?}", a);
                
                // Получение представления папки через IWebBrowser2 и выбор выбранных элементов
                let document = web_browser.Document().ok()?;
                let folder_view: IShellFolderViewDual3 = document.cast().ok()?;
                let selected_items = folder_view.SelectedItems().ok()?;
                let count = selected_items.Count().ok()?;
                
                if count > 0 {
                    let item = selected_items.Item(&VARIANT::from(0)).ok()?;
                    let path = item.Path().ok()?;
                    target_path = Some(path.to_string());
                    break
                }
            }
            // Очистка COM
            CoUninitialize();
            target_path
        }
    }
  1. С помощью IUIAutomation

Я получаю имя файла, выбранного на текущей вкладке Проводника, но не могу получить его путь.

unsafe fn find_tab_items(ui_automation: &IUIAutomation, element: &IUIAutomationElement, file_name: &mut String) -> Result<(), WError> {
        // Поиск дочерних элементов
        let condition = ui_automation.CreatePropertyCondition(UIA_ControlTypePropertyId, &VARIANT::from(UIA_ListItemControlTypeId.0))?;
        let list_element = element.FindAll(TreeScope_Descendants, &condition)?;
        let count = list_element.Length()?;

        for i in 0..count {
            let item = list_element.GetElement(i)?;

            // Попытка получить SelectionItemPattern
            let selection_pattern: IUIAutomationSelectionItemPattern = item.GetCurrentPatternAs(UIA_SelectionItemPatternId)?;

            // Проверка, выбран ли элемент
            let is_selected = selection_pattern.CurrentIsSelected()?;
            if is_selected.as_bool().eq(&true) {
                // Получение имени файла или пути
                let tmp_name = item.GetCurrentPropertyValue(UIA_NamePropertyId)?;
                *file_name = tmp_name.to_string();
                break
            }

        }
        Ok(())
    }

Ожидается:

  • Получает путь к файлу, выбранному на текущей вкладке в Проводнике.

Ответ или решение

Для того чтобы получить путь к выбранному файлу в текущей вкладке проводника Windows с использованием языка программирования Rust, необходимо использовать несколько компонентов Windows API и COM (Component Object Model). Ниже приводится более подробный подход к решению этой задачи, основываясь на вашем текущем прогрессе и опыте работы с IShellWindows и IUIAutomation.

Предварительная настройка и инициализация COM

Прежде всего, убедитесь, что вы инициализируете COM-библиотеку перед тем, как вызывать методы из нее. Это можно сделать с помощью функции CoInitializeEx. Ваша функция уже включает этот шаг, что хорошо.

Использование IShellWindows

Вы используете интерфейс IShellWindows, чтобы получить доступ к открытым окнам проводника. Однако важно понимать, что IShellWindows не дает прямого доступа к выбранному элементу. Вам нужно будет использовать IWebBrowser2 и затем получить доступ к текущему выбранному элементу через IWebBrowser2::Document.

Основная логика, которую вы можете использовать, состоит в том, чтобы:

  1. Итерировать по всем открытым окнам проводника.
  2. Для каждого окна проверять, совпадает ли его дескриптор (HWND) с текущим активным окном.
  3. Если совпадает, получать текущий документ из IWebBrowser2.
  4. Затем получать доступ к выбранным элементам.

Пример кода

Вот пример функции для получения пути к выбранному файлу:

fn get_selected_file_path(hwnd: HWND) -> Option<String> {
    unsafe {
        // Инициализация COM
        let com = CoInitializeEx(None, COINIT_DISABLE_OLE1DDE);
        if com.is_err() {
            return None;
        }

        // Получение экземпляра IShellWindows
        let shell_windows: IShellWindows = match CoCreateInstance(&ShellWindows, None, CLSCTX_LOCAL_SERVER) {
            Ok(instance) => instance,
            Err(_) => {
                CoUninitialize();
                return None;
            }
        };

        // Перебор всех открытых окон
        let count = shell_windows.Count().unwrap_or_default();
        for i in 0..count {
            let window: IDispatch = shell_windows.Item(&VARIANT::from(i)).ok()?;
            let web_browser: IWebBrowser2 = window.cast().ok()?;
            let item_hwnd = web_browser.HWND().ok()?;
            if item_hwnd.0 != hwnd.0 as isize {
                continue;
            }

            // Получение документа и выбранных элементов
            let document = web_browser.Document().ok()?;
            let folder_view: IShellFolderViewDual3 = document.cast().ok()?;
            let selected_items = folder_view.SelectedItems().ok()?;
            let num_selected = selected_items.Count().unwrap_or_default();

            if num_selected > 0 {
                let selected_item = selected_items.Item(&VARIANT::from(0)).ok()?;
                let path = selected_item.Path().ok()?;
                CoUninitialize();
                return Some(path.to_string());
            }
        }

        // Завершение работы с COM
        CoUninitialize();
        None
    }
}

Использование IUIAutomation

Если вы хотите использовать IUIAutomation, чтобы найти имя выбранного файла, вы можете продолжить с этого подхода. Однако вам потребуется дополнительно оформить механизм для получения полного пути на основе имени файла. Это может включать в себя добавление логики для поиска родительской директории и комбинирования имени файла с путем к директории.

Завершение

Эти шаги предоставляют вам возможность получить путь к выбранному файлу в текущей вкладке Windows Explorer. Убедитесь, что у вас есть необходимые зависимости для работы с COM и что ваш проект настроен правильно для работы с Windows API.

Следуя этим рекомендациям, вы сможете успешно извлечь путь к выбранному файлу в проводнике. Если у вас возникнут дополнительные вопросы или проблемы, не стесняйтесь обращаться за помощью.

Оцените материал
Добавить комментарий

Капча загружается...