Skip to content

Instantly share code, notes, and snippets.

@Larusso
Created October 30, 2019 08:13
Show Gist options
  • Save Larusso/89a5079bdc8bfe07a203cbabeba4f9ec to your computer and use it in GitHub Desktop.
Save Larusso/89a5079bdc8bfe07a203cbabeba4f9ec to your computer and use it in GitHub Desktop.
CommandLineToArgvW rust wrapper
[dependencies]
winapi = { version = "0.3", features = ["shellapi","errhandlingapi"] }
widestring = "0.4.0"
use std::error::Error;
use std::ffi::OsString;
use widestring::U16CString;
use widestring::U16String;
use winapi::um::winbase::{FormatMessageW, LocalFree, FORMAT_MESSAGE_FROM_SYSTEM,
FORMAT_MESSAGE_ALLOCATE_BUFFER, FORMAT_MESSAGE_IGNORE_INSERTS};
use winapi::shared::ntdef::LPWSTR;
use winapi::shared::minwindef::HLOCAL;
use winapi::ctypes::c_int;
use winapi::um::shellapi;
use winapi::um::errhandlingapi;
use std::ptr;
pub fn win_parse_cmd(cmd: &str) -> std::result::Result<Vec<OsString>, String> {
use std::slice;
trace!("parse commandline args {}", cmd);
let cmd = U16CString::from_str(cmd).map_err(|err| err.description().to_string())?;
let mut num_args: c_int = 0;
unsafe {
let raw_args = shellapi::CommandLineToArgvW(cmd.as_ptr(), &mut num_args as *mut c_int);
if raw_args.is_null() {
let error_code = errhandlingapi::GetLastError();
let mut buffer: LPWSTR = ptr::null_mut();
let strlen = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null(),
error_code,
0,
(&mut buffer as *mut LPWSTR) as LPWSTR,
0,
ptr::null_mut());
let message = U16String::from_ptr(buffer, strlen as usize);
LocalFree(buffer as HLOCAL);
return Err(format!("CommandLineToArgvW failed code: ({})", message.to_string_lossy()));
}
let mut args: Vec<OsString> = Vec::with_capacity(num_args as usize);
for s_ptr in slice::from_raw_parts_mut(raw_args, num_args as usize) {
let s = U16CString::from_ptr_str(*s_ptr);
let os = s.to_os_string();
args.push(os);
}
LocalFree(raw_args as HLOCAL);
Ok(args)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_command_into_arguments() {
use std::ffi::OsStr;
let cmd = r#"test.exe --arg1 "PosArg1" "Pos Arg 2" PosArg3"#;
let arguments = win_parse_cmd(cmd).unwrap();
let expected_result: Vec<OsString> = [
"test.exe",
"--arg1",
"PosArg1",
"Pos Arg 2",
"PosArg3",
]
.iter()
.map(|s| OsStr::new(s).to_os_string())
.collect();
assert_eq!(arguments, expected_result);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment