Skip to content

Commit e2fc4b1

Browse files
Seeker14491Byron
authored andcommitted
Use ShellExecute rather than start.exe on windows
The code is adapted from rustup. Using ShellExecute is more efficient and allows much better error handling. It also avoids some bugs, which is the reason rustup switched to using this method. Fixes #16
1 parent 88ddb6f commit e2fc4b1

File tree

2 files changed

+54
-14
lines changed

2 files changed

+54
-14
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ documentation = "http://byron.github.io/open-rs"
1313
test = false
1414
doc = false
1515
name = "open"
16+
17+
[target.'cfg(windows)'.dependencies]
18+
winapi = { version = "0.3", features = ["shellapi"] }

src/lib.rs

+51-14
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,15 @@
3737
//! }
3838
//! # }
3939
//! ```
40+
#[cfg(windows)]
41+
extern crate winapi;
42+
43+
#[cfg(not(target_os = "windows"))]
44+
use std::process::Command;
45+
4046
use std::ffi::OsStr;
4147
use std::io;
42-
use std::process::{Command, ExitStatus};
48+
use std::process::ExitStatus;
4349

4450
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
4551
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
@@ -59,22 +65,53 @@ pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
5965

6066
#[cfg(target_os = "windows")]
6167
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
62-
Command::new("cmd")
63-
.arg("/C")
64-
.arg("start")
65-
.arg("")
66-
.arg({
67-
let path_ref = path.as_ref();
68-
match path_ref.to_str() {
69-
Some(s) => s.replace("&", "^&"),
70-
None => path_ref,
71-
}
72-
})
73-
.spawn()?
74-
.wait()
68+
use winapi::ctypes::c_int;
69+
use winapi::um::shellapi::ShellExecuteW;
70+
use std::os::windows::ffi::OsStrExt;
71+
use std::os::windows::process::ExitStatusExt;
72+
use std::ptr;
73+
74+
const SW_SHOW: c_int = 5;
75+
76+
let path = windows::convert_path(path.as_ref())?;
77+
let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
78+
let result = unsafe {
79+
ShellExecuteW(
80+
ptr::null_mut(),
81+
operation.as_ptr(),
82+
path.as_ptr(),
83+
ptr::null(),
84+
ptr::null(),
85+
SW_SHOW,
86+
)
87+
};
88+
if result as c_int > 32 {
89+
Ok(ExitStatus::from_raw(0))
90+
} else {
91+
Err(io::Error::last_os_error())
92+
}
7593
}
7694

7795
#[cfg(target_os = "macos")]
7896
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
7997
Command::new("open").arg(path.as_ref()).spawn()?.wait()
8098
}
99+
100+
#[cfg(windows)]
101+
mod windows {
102+
use std::io;
103+
use std::ffi::OsStr;
104+
use std::os::windows::ffi::OsStrExt;
105+
106+
pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
107+
let mut maybe_result: Vec<_> = path.encode_wide().collect();
108+
if maybe_result.iter().any(|&u| u == 0) {
109+
return Err(io::Error::new(
110+
io::ErrorKind::InvalidInput,
111+
"path contains NUL byte(s)",
112+
));
113+
}
114+
maybe_result.push(0);
115+
Ok(maybe_result)
116+
}
117+
}

0 commit comments

Comments
 (0)