Skip to content

Commit

Permalink
Replace winapi dependency with windows
Browse files Browse the repository at this point in the history
Since normpath was [added][1], opener has depended on both winapi (directly)
and windows-sys (transitively).

This commit replaces usage of winapi with the windows crate so that it
only depends on a single windows API crate. Additionally, the windows
crate includes some functions that were manually linked in opener, so
those manual links were able to be cleaned up.

[1]: 3b48ee0
  • Loading branch information
skipkayhil committed Dec 19, 2023
1 parent ff6e259 commit 1f942b5
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 75 deletions.
10 changes: 7 additions & 3 deletions opener/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ maintenance = { status = "passively-maintained" }
reveal = [
"dep:url",
"dep:dbus",
"winapi/shtypes",
"winapi/objbase",
"windows/Win32_System_Com",
"windows/Win32_UI_Shell_Common"
]

[dev-dependencies]
Expand All @@ -33,7 +33,11 @@ url = { version = "2", optional = true }

[target.'cfg(windows)'.dependencies]
normpath = "1"
winapi = { version = "0.3", features = ["shellapi"] }
windows = { version = "0", features = [
"Win32_Foundation",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging"
] }

[package.metadata.docs.rs]
all-features = true
Expand Down
23 changes: 11 additions & 12 deletions opener/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ use normpath::PathExt;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::path::PathBuf;
use std::{io, ptr};
use winapi::ctypes::c_int;
use winapi::um::shellapi::ShellExecuteW;
use std::io;
use windows::core::{w, PCWSTR};
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::Shell::ShellExecuteW;
use windows::Win32::UI::WindowsAndMessaging::SW_SHOW;

#[cfg(feature = "reveal")]
mod reveal;
Expand All @@ -27,21 +29,18 @@ pub(crate) fn open(path: &OsStr) -> Result<(), OpenError> {
}

pub(crate) fn open_helper(path: &OsStr) -> Result<(), OpenError> {
const SW_SHOW: c_int = 5;

let path = convert_path(path).map_err(OpenError::Io)?;
let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
operation.as_ptr(),
path.as_ptr(),
ptr::null(),
ptr::null(),
HWND(0),
w!("open"),
PCWSTR::from_raw(path.as_ptr()),
PCWSTR::null(),
PCWSTR::null(),
SW_SHOW,
)
};
if result as c_int > 32 {
if result.0 > 32 {
Ok(())
} else {
Err(OpenError::Io(io::Error::last_os_error()))
Expand Down
71 changes: 11 additions & 60 deletions opener/src/windows/reveal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@ use super::convert_path;
use crate::OpenError;
use normpath::PathExt;
use std::path::Path;
use std::{io, ptr, thread};
use winapi::shared::minwindef::{DWORD, UINT};
use winapi::shared::ntdef::PCWSTR;
use winapi::shared::winerror::HRESULT;
use winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
use winapi::um::objbase::COINIT_MULTITHREADED;
use winapi::um::shtypes::{
PCIDLIST_ABSOLUTE, PCUITEMID_CHILD_ARRAY, PIDLIST_ABSOLUTE, PIDLIST_RELATIVE,
};
use std::{io, thread};
use windows::core::PCWSTR;
use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED};
use windows::Win32::UI::Shell::{ILCreateFromPathW, SHOpenFolderAndSelectItems};

pub(crate) fn reveal(path: &Path) -> Result<(), OpenError> {
let path = path.to_owned();
Expand All @@ -22,57 +17,13 @@ pub(crate) fn reveal(path: &Path) -> Result<(), OpenError> {
}

fn reveal_in_thread(path: &Path) -> io::Result<()> {
to_io_result(unsafe { CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED) })?;
let item_id_list = ItemIdList::new(path)?;
// Because the cidl argument is zero, SHOpenFolderAndSelectItems opens the singular item
// in our item id list in its containing folder and selects it.
to_io_result(unsafe { SHOpenFolderAndSelectItems(item_id_list.0, 0, ptr::null(), 0) })?;
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }?;
// The ILCreateFromPathW function expects a canonicalized path.
// Unfortunately it does not support NT UNC paths (which std::path::canonicalize returns)
// so we use the normpath crate instead.
let path = convert_path(path.normalize()?.as_os_str())?;
let item_id_list = unsafe { ILCreateFromPathW(PCWSTR::from_raw(path.as_ptr())) };
unsafe { SHOpenFolderAndSelectItems(item_id_list, None, 0) }?;
unsafe { CoUninitialize() };
Ok(())
}

fn to_io_result(hresult: HRESULT) -> io::Result<()> {
if hresult >= 0 {
Ok(())
} else {
// See the HRESULT_CODE macro in winerror.h
Err(io::Error::from_raw_os_error(hresult & 0xFFFF))
}
}

struct ItemIdList(PIDLIST_ABSOLUTE);

impl ItemIdList {
fn new(path: &Path) -> io::Result<Self> {
// The ILCreateFromPathW function expects a canonicalized path.
// Unfortunately it does not support NT UNC paths (which std::path::canonicalize returns)
// so we use the normpath crate instead.
let path = convert_path(path.normalize()?.as_os_str())?;
let result = unsafe { ILCreateFromPathW(path.as_ptr()) };
if result.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(ItemIdList(result))
}
}
}

impl Drop for ItemIdList {
fn drop(&mut self) {
unsafe { ILFree(self.0) }
}
}

#[link(name = "Shell32")]
extern "C" {
fn ILCreateFromPathW(pszPath: PCWSTR) -> PIDLIST_ABSOLUTE;

fn ILFree(pidl: PIDLIST_RELATIVE);

fn SHOpenFolderAndSelectItems(
pidlFolder: PCIDLIST_ABSOLUTE,
cidl: UINT,
apidl: PCUITEMID_CHILD_ARRAY,
dwFlags: DWORD,
) -> HRESULT;
}

0 comments on commit 1f942b5

Please sign in to comment.