feat: replace unmaintained winapi with windows-sys (#18)

* feat: replace unmaintained winapi with windows-sys

winapi has been unmaintained for a long time
this commit replaces winapi with windows-sys which is generated from
windows API headers and maintained by Microsoft

* address feedback

* fix bugs

---------

Co-authored-by: João Moreno <joao.moreno@microsoft.com>
This commit is contained in:
Jakub Panek 2023-07-04 21:32:15 +02:00 коммит произвёл GitHub
Родитель fe029f3e56
Коммит 0748f6b08d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 586 добавлений и 511 удалений

4
.rustfmt.toml Normal file
Просмотреть файл

@ -0,0 +1,4 @@
hard_tabs = true
# Requires nightly
# imports_granularity = "Module"

59
Cargo.lock сгенерированный
Просмотреть файл

@ -116,7 +116,7 @@ dependencies = [
"slog",
"slog-async",
"slog-term",
"winapi",
"windows-sys",
]
[[package]]
@ -329,3 +329,60 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

Просмотреть файл

@ -1,19 +1,31 @@
[package]
name = "inno_updater"
version = "0.9.0"
authors = ["Microsoft <monacotools@microsoft.com>"]
build = "build.rs"
[dependencies]
byteorder = "1"
crc = "^1.0.0"
slog = "2.1.1"
slog-async = "2.2.0"
slog-term = "2.3.0"
[target.'cfg(windows)'.dependencies]
winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] }
[profile.release]
lto = true
panic = 'abort'
[package]
name = "inno_updater"
version = "0.9.0"
authors = ["Microsoft <monacotools@microsoft.com>"]
build = "build.rs"
[dependencies]
byteorder = "1"
crc = "^1.0.0"
slog = "2.1.1"
slog-async = "2.2.0"
slog-term = "2.3.0"
[target.'cfg(windows)'.dependencies.windows-sys]
version = "0.42"
features = [
"Win32_Foundation",
"Win32_System_Shutdown",
"Win32_UI_WindowsAndMessaging",
"Win32_System_Threading",
"Win32_System_LibraryLoader",
"Win32_System_Diagnostics_Debug",
"Win32_Storage_FileSystem",
"Win32_Security",
"Win32_System_ProcessStatus",
"Win32_System_Diagnostics_ToolHelp"
]
[profile.release]
lto = true
panic = 'abort'

Просмотреть файл

@ -18,6 +18,12 @@ fn main() {
assert!(ecode.success(), "Resource compiler failed");
println!("cargo:rustc-link-search=native={}", resources.parent().unwrap().to_str().unwrap());
println!("cargo:rustc-link-lib={}", resources.file_stem().unwrap().to_str().unwrap());
println!(
"cargo:rustc-link-search=native={}",
resources.parent().unwrap().to_str().unwrap()
);
println!(
"cargo:rustc-link-lib={}",
resources.file_stem().unwrap().to_str().unwrap()
);
}

Просмотреть файл

@ -5,9 +5,8 @@
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crc::{crc32, Hasher32};
use std::cmp;
use std::io;
use std::io::prelude::*;
use std::{cmp, io};
const BLOCK_MAX_SIZE: usize = 4096;
@ -48,8 +47,8 @@ impl<'a> BlockRead<'a> {
}
let size = size as usize;
let mut buffer = &mut self.buffer[..size];
self.reader.read_exact(&mut buffer)?;
let buffer = &mut self.buffer[..size];
self.reader.read_exact(buffer)?;
let mut digest = crc32::Digest::new(crc32::IEEE);
digest.write(buffer);

Просмотреть файл

@ -6,13 +6,11 @@
use std::sync::mpsc::Sender;
use std::{mem, ptr};
use strings::to_utf16;
use winapi::shared::basetsd::INT_PTR;
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, UINT, WPARAM};
use winapi::shared::ntdef::LPCWSTR;
use winapi::shared::windef::HWND;
use windows_sys::core::PCWSTR;
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, WPARAM};
extern "system" {
pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: LPCWSTR) -> BOOL;
pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: PCWSTR) -> BOOL;
pub fn ShutdownBlockReasonDestroy(hWnd: HWND) -> BOOL;
}
@ -21,23 +19,27 @@ struct DialogData {
tx: Sender<ProgressWindow>,
}
unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) -> INT_PTR {
unsafe extern "system" fn dlgproc(hwnd: HWND, msg: u32, _: WPARAM, l: LPARAM) -> isize {
use resources;
use winapi::shared::windef::RECT;
use winapi::um::commctrl::PBM_SETMARQUEE;
use winapi::um::processthreadsapi::GetCurrentThreadId;
use winapi::um::winuser::{
GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow, HWND_TOPMOST,
SW_HIDE, WM_DESTROY, WM_INITDIALOG,
use windows_sys::Win32::Foundation::RECT;
use windows_sys::Win32::System::Threading::GetCurrentThreadId;
use windows_sys::Win32::UI::WindowsAndMessaging::{
GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow,
HWND_TOPMOST, SW_HIDE, WM_DESTROY, WM_INITDIALOG, WM_USER,
};
match msg {
WM_INITDIALOG => {
let data = &*(l as *const DialogData);
if !data.silent {
SendDlgItemMessageW(hwnd, resources::PROGRESS_SLIDER, PBM_SETMARQUEE, 1, 0);
SendDlgItemMessageW(hwnd, resources::PROGRESS_SLIDER, WM_USER + 10, 1, 0);
let mut rect = mem::MaybeUninit::<RECT>::uninit().assume_init();
let mut rect = RECT {
top: 0,
left: 0,
bottom: 0,
right: 0,
};
GetWindowRect(hwnd, &mut rect);
let width = rect.right - rect.left;
@ -58,8 +60,7 @@ unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) -
ShowWindow(hwnd, SW_HIDE);
}
data
.tx
data.tx
.send(ProgressWindow {
ui_thread_id: GetCurrentThreadId(),
})
@ -77,14 +78,14 @@ unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) -
}
pub struct ProgressWindow {
ui_thread_id: DWORD,
ui_thread_id: u32,
}
unsafe impl Send for ProgressWindow {}
impl ProgressWindow {
pub fn exit(&self) {
use winapi::um::winuser::{PostThreadMessageW, WM_QUIT};
use windows_sys::Win32::UI::WindowsAndMessaging::{PostThreadMessageW, WM_QUIT};
unsafe {
PostThreadMessageW(self.ui_thread_id, WM_QUIT, 0, 0);
@ -94,16 +95,16 @@ impl ProgressWindow {
pub fn run_progress_window(silent: bool, tx: Sender<ProgressWindow>) {
use resources;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{DialogBoxParamW, MAKEINTRESOURCEW};
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
use windows_sys::Win32::UI::WindowsAndMessaging::DialogBoxParamW;
let data = DialogData { silent, tx };
unsafe {
DialogBoxParamW(
GetModuleHandleW(ptr::null_mut()),
MAKEINTRESOURCEW(resources::PROGRESS_DIALOG),
ptr::null_mut(),
resources::PROGRESS_DIALOG as PCWSTR,
mem::zeroed(),
Some(dlgproc),
(&data as *const DialogData) as LPARAM,
);
@ -130,16 +131,16 @@ pub enum MessageBoxResult {
}
pub fn message_box(text: &str, caption: &str, mbtype: MessageBoxType) -> MessageBoxResult {
use winapi::um::winuser::{
MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, IDYES,
MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL,
use windows_sys::Win32::UI::WindowsAndMessaging::{
MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN,
IDYES, MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL,
};
let result: i32;
unsafe {
result = MessageBoxW(
ptr::null_mut(),
mem::zeroed(),
to_utf16(text).as_ptr(),
to_utf16(caption).as_ptr(),
match mbtype {

Просмотреть файл

@ -3,96 +3,97 @@
* Licensed under the MIT License. See LICENSE in the project root for license information.
*----------------------------------------------------------------------------------------*/
use std::ffi::c_void;
use std::path::Path;
use std::{error, io, ptr};
use strings::to_u16s;
use util;
use winapi::um::winnt::HANDLE;
use windows_sys::Win32::Foundation::HANDLE;
pub struct FileHandle(HANDLE);
impl FileHandle {
pub fn new(path: &Path) -> Result<FileHandle, Box<dyn error::Error>> {
use winapi::um::fileapi::CreateFileW;
use winapi::um::fileapi::OPEN_EXISTING;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::winnt::DELETE;
use winapi::um::winnt::FILE_ATTRIBUTE_NORMAL;
pub fn new(path: &Path) -> Result<FileHandle, Box<dyn error::Error>> {
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::Storage::FileSystem::{
CreateFileW, DELETE, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING,
};
unsafe {
let handle = CreateFileW(
to_u16s(path.as_os_str()).as_ptr(),
DELETE,
0,
ptr::null_mut(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
ptr::null_mut(),
);
unsafe {
let handle = CreateFileW(
to_u16s(path.as_os_str()).as_ptr(),
DELETE,
0,
ptr::null_mut(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
std::mem::zeroed(),
);
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to create file handle: {}",
util::get_last_error_message()?
),
)
.into());
}
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to create file handle: {}",
util::get_last_error_message()?
),
)
.into());
}
Ok(FileHandle(handle))
}
}
Ok(FileHandle(handle))
}
}
pub fn mark_for_deletion(&self) -> Result<(), Box<dyn error::Error>> {
use std::mem;
use winapi::shared::minwindef::{DWORD, FALSE, LPVOID, TRUE};
use winapi::um::fileapi::SetFileInformationByHandle;
use winapi::um::fileapi::FILE_DISPOSITION_INFO;
use winapi::um::minwinbase::FileDispositionInfo;
pub fn mark_for_deletion(&self) -> Result<(), Box<dyn error::Error>> {
use std::mem;
use windows_sys::Win32::Foundation::BOOLEAN;
use windows_sys::Win32::Storage::FileSystem::{
FileDispositionInfo, SetFileInformationByHandle, FILE_DISPOSITION_INFO,
};
unsafe {
let mut info = FILE_DISPOSITION_INFO { DeleteFile: TRUE as u8 };
let result = SetFileInformationByHandle(
self.0,
FileDispositionInfo,
&mut info as *mut _ as LPVOID,
mem::size_of::<FILE_DISPOSITION_INFO>() as DWORD,
);
unsafe {
let mut info = FILE_DISPOSITION_INFO {
DeleteFile: 1 as BOOLEAN,
};
let result = SetFileInformationByHandle(
self.0,
FileDispositionInfo,
&mut info as *mut _ as *mut c_void,
mem::size_of::<FILE_DISPOSITION_INFO>() as u32,
);
if result == FALSE {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to mark file for deletion: {}",
util::get_last_error_message()?
),
)
.into());
}
}
if result.is_negative() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to mark file for deletion: {}",
util::get_last_error_message()?
),
)
.into());
}
}
Ok(())
}
Ok(())
}
pub fn close(&self) -> Result<(), Box<dyn error::Error>> {
use winapi::shared::minwindef::FALSE;
use winapi::um::handleapi::CloseHandle;
pub fn close(&self) -> Result<(), Box<dyn error::Error>> {
use windows_sys::Win32::Foundation::CloseHandle;
unsafe {
if CloseHandle(self.0) == FALSE {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to close file handle: {}",
util::get_last_error_message()?
),
)
.into());
}
}
unsafe {
if CloseHandle(self.0).is_negative() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to close file handle: {}",
util::get_last_error_message()?
),
)
.into());
}
}
Ok(())
}
Ok(())
}
}

Просмотреть файл

@ -11,7 +11,7 @@ extern crate crc;
extern crate slog;
extern crate slog_async;
extern crate slog_term;
extern crate winapi;
extern crate windows_sys;
mod blockio;
mod gui;
@ -33,7 +33,7 @@ use std::time::SystemTime;
use std::vec::Vec;
use std::{env, error, fmt, fs, io, thread};
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn read_file(path: &Path) -> Result<(Header, Vec<FileRec>), Box<dyn error::Error>> {
let input_file = fs::File::open(path)?;
@ -100,17 +100,15 @@ fn delete_existing_version(
let root = PathBuf::from(root_path);
directories.push_back(root);
while directories.len() > 0 {
let dir = directories.pop_front().unwrap();
while let Some(dir) = directories.pop_front() {
info!(log, "Reading directory: {:?}", dir);
for entry in fs::read_dir(&dir)? {
let entry = entry?;
let entry_name = entry.file_name();
let entry_name = entry_name.to_str().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get entry name",
))?;
let entry_name = entry_name
.to_str()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?;
if dir == root_path {
// don't delete the update folder
@ -122,7 +120,7 @@ fn delete_existing_version(
if entry_name == "tools" {
continue;
}
// don't delete any of the unins* files
if entry_name.starts_with("unins") {
continue;
@ -220,10 +218,12 @@ fn move_update(
"move_update: {:?}, {}", uninstdat_path, update_folder_name
);
let root_path = uninstdat_path.parent().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
))?;
let root_path = uninstdat_path.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
)
})?;
let mut update_path = PathBuf::from(root_path);
update_path.push(update_folder_name);
@ -231,7 +231,9 @@ fn move_update(
let stat = fs::metadata(&update_path)?;
if !stat.is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Update folder is not a directory").into());
return Err(
io::Error::new(io::ErrorKind::Other, "Update folder is not a directory").into(),
);
}
// safely delete all current files
@ -241,10 +243,9 @@ fn move_update(
for entry in fs::read_dir(&update_path)? {
let entry = entry?;
let entry_name = entry.file_name();
let entry_name = entry_name.to_str().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get entry name",
))?;
let entry_name = entry_name
.to_str()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?;
let mut target = PathBuf::from(root_path);
target.push(entry_name);
@ -272,15 +273,17 @@ fn patch_uninstdat(
uninstdat_path: &PathBuf,
update_folder_name: &str,
) -> Result<(), Box<dyn error::Error>> {
let (header, recs) = read_file(&uninstdat_path)?;
let (header, recs) = read_file(uninstdat_path)?;
info!(log, "header: {:?}", header);
info!(log, "num_recs: {:?}", recs.len());
let root_path = uninstdat_path.parent().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
))?;
let root_path = uninstdat_path.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
)
})?;
let mut update_path = PathBuf::from(root_path);
update_path.push(&update_folder_name);
@ -296,7 +299,7 @@ fn patch_uninstdat(
.collect();
info!(log, "Updating uninstall file {:?}", uninstdat_path);
write_file(&uninstdat_path, &header, recs?)?;
write_file(uninstdat_path, &header, recs?)?;
Ok(())
}
@ -308,10 +311,12 @@ fn do_update(
) -> Result<(), Box<dyn error::Error>> {
info!(log, "do_update: {:?}, {}", code_path, update_folder_name);
let root_path = code_path.parent().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
))?;
let root_path = code_path.parent().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Could not get parent path of uninstdat",
)
})?;
let mut uninstdat_path = PathBuf::from(root_path);
uninstdat_path.push("unins000.dat");
@ -349,7 +354,7 @@ fn update(
.recv()
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Could not receive GUI window handle"))?;
do_update(&log, code_path, update_folder_name)?;
do_update(log, code_path, update_folder_name)?;
window.exit();
Ok(())
@ -374,19 +379,17 @@ impl error::Error for ArgumentError {
}
}
fn _main(log: &slog::Logger, args: &Vec<String>) -> Result<(), Box<dyn error::Error>> {
fn _main(log: &slog::Logger, args: &[String]) -> Result<(), Box<dyn error::Error>> {
info!(log, "Starting: {}, {}", args[1], args[2]);
let code_path = PathBuf::from(&args[1]);
if !code_path.is_absolute() {
return Err(
ArgumentError(format!(
"Code path needs to be absolute. Instead got: {}",
args[1]
))
.into(),
);
return Err(ArgumentError(format!(
"Code path needs to be absolute. Instead got: {}",
args[1]
))
.into());
}
if !code_path.exists() {
@ -396,32 +399,28 @@ fn _main(log: &slog::Logger, args: &Vec<String>) -> Result<(), Box<dyn error::Er
let silent = args[2].clone();
if silent != "true" && silent != "false" {
return Err(
ArgumentError(format!(
"Silent needs to be true or false. Instead got: {}",
silent
))
.into(),
);
return Err(ArgumentError(format!(
"Silent needs to be true or false. Instead got: {}",
silent
))
.into());
}
update(log, &code_path, "_", silent == "true")
}
fn handle_error(log_path: &str) {
let mut msgs = Vec::new();
msgs.push("Failed to install Visual Studio Code update.");
msgs.push("Updates may fail due to anti-virus software and/or runaway processes. Please try restarting your machine before attempting to update again.");
msgs.push("Please read the log file for more information:");
msgs.push(log_path);
let msg = msgs.join("\n\n");
let msg = format!(
"Failed to install Visual Studio Code update.\n\n\
Updates may fail due to anti-virus software and/or runaway processes. Please try restarting your machine before attempting to update again.\n\n\
Please read the log file for more information:\n\n\
{log_path}"
);
gui::message_box(&msg, "Visual Studio Code", gui::MessageBoxType::Error);
}
fn __main(args: &Vec<String>) -> i32 {
fn __main(args: &[String]) -> i32 {
let mut log_path = env::temp_dir();
log_path.push(format!(
"vscode-inno-updater-{:?}.log",
@ -522,7 +521,7 @@ fn main() {
Some(5),
);
if let Err(_) = result {
if result.is_err() {
handle_error(&log_path);
}

Просмотреть файл

@ -62,7 +62,7 @@ pub struct FileRec {
data: Vec<u8>,
}
impl<'a> fmt::Debug for FileRec {
impl fmt::Debug for FileRec {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
@ -95,10 +95,10 @@ impl<'a> error::Error for StringDecodeError<'a> {
fn decode_strings<'a>(data: &[u8]) -> Result<Vec<String>, StringDecodeError<'a>> {
let mut result: Vec<String> = Vec::with_capacity(10);
let mut slice = data.clone();
let mut slice = data;
loop {
let reader: &mut dyn Read = &mut slice.clone();
let reader: &mut dyn Read = &mut slice;
let byte_result = reader
.read_u8()
.map_err(|_| StringDecodeError("Failed to parse file rec string header"))?;
@ -241,7 +241,7 @@ impl error::Error for RebaseError {
}
}
impl<'a> FileRec {
impl FileRec {
pub fn from_reader<'b>(reader: &mut dyn Read) -> Result<FileRec, FileRecParseError<'b>> {
let typ = reader
.read_u16::<LittleEndian>()
@ -251,7 +251,8 @@ impl<'a> FileRec {
.map_err(|_| FileRecParseError("Failed to parse file rec extra data"))?;
let data_size = reader
.read_u32::<LittleEndian>()
.map_err(|_| FileRecParseError("Failed to parse file rec data size"))? as usize;
.map_err(|_| FileRecParseError("Failed to parse file rec data size"))?
as usize;
if data_size > 0x8000000 {
return Err(FileRecParseError("File rec data size too large"));
@ -303,8 +304,8 @@ impl<'a> FileRec {
let rebased_paths: Vec<String> = paths
.iter()
.map(|p| {
if p.starts_with(from) {
[to, &p[from.len()..]].join("")
if let Some(p) = p.strip_prefix(from) {
format!("{to}{p}")
} else {
p.clone()
}

Просмотреть файл

@ -112,8 +112,7 @@ impl Header {
.map_err(|_| HeaderParseError("Failed to parse header flags"))?;
let mut reserved = [0; 108];
read
.read_exact(&mut reserved)
read.read_exact(&mut reserved)
.map_err(|_| HeaderParseError("Failed to parse header reserved"))?;
let crc = read

Просмотреть файл

@ -1,235 +1,229 @@
/*-----------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*----------------------------------------------------------------------------------------*/
use slog;
use std::path::{Path, PathBuf};
use std::{error, io, mem, ptr, thread, time};
use strings::from_utf16;
use util;
use winapi::shared::minwindef::{DWORD, TRUE};
pub struct RunningProcess {
pub name: String,
pub id: DWORD,
}
pub fn get_running_processes() -> Result<Vec<RunningProcess>, io::Error> {
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::tlhelp32::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS,
};
unsafe {
let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not create process snapshot",
));
}
let mut pe32 = PROCESSENTRY32W {
dwSize: 0,
cntUsage: 0,
th32ProcessID: 0,
th32DefaultHeapID: 0,
th32ModuleID: 0,
cntThreads: 0,
th32ParentProcessID: 0,
pcPriClassBase: 0,
dwFlags: 0,
szExeFile: [0u16; 260],
};
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
if Process32FirstW(handle, &mut pe32) != TRUE {
CloseHandle(handle);
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get first process data",
));
}
let mut result: Vec<RunningProcess> = vec![];
loop {
result.push(RunningProcess {
name: from_utf16(&pe32.szExeFile).map_err(|e| {
CloseHandle(handle);
e
})?,
id: pe32.th32ProcessID,
});
if Process32NextW(handle, &mut pe32) != TRUE {
CloseHandle(handle);
break;
}
}
return Ok(result);
}
}
/**
* Kills a running process, if its path is the same as the provided one.
*/
fn kill_process_if(
log: &slog::Logger,
process: &RunningProcess,
path: &Path,
) -> Result<(), Box<dyn error::Error>> {
use winapi::shared::minwindef::MAX_PATH;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
use winapi::um::psapi::GetModuleFileNameExW;
use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ};
info!(
log,
"Kill process if found: {}, {}", process.id, process.name
);
unsafe {
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
let handle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
0,
process.id,
);
if handle.is_null() {
return Err(
io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to open process: {}",
util::get_last_error_message()?
),
)
.into(),
);
}
let mut raw_path = [0u16; MAX_PATH];
let len = GetModuleFileNameExW(
handle,
ptr::null_mut(),
raw_path.as_mut_ptr(),
MAX_PATH as DWORD,
) as usize;
if len == 0 {
CloseHandle(handle);
return Err(
io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to get process file name: {}",
util::get_last_error_message()?
),
)
.into(),
);
}
let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?);
if process_path != path {
CloseHandle(handle);
return Ok(());
}
info!(
log,
"Found {} running, pid {}, attempting to kill...", process.name, process.id
);
if TerminateProcess(handle, 0) != TRUE {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into());
}
info!(
log,
"Successfully killed {}, pid {}", process.name, process.id
);
CloseHandle(handle);
Ok(())
}
}
pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box<dyn error::Error>> {
let file_name = path.file_name().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get process file name",
))?;
let file_name = file_name.to_str().ok_or(io::Error::new(
io::ErrorKind::Other,
"Could not get convert file name to str",
))?;
let mut attempt: u32 = 0;
// wait for 10 seconds until all processes are dead
loop {
attempt += 1;
info!(
log,
"Checking for running {} processes... (attempt {})", file_name, attempt
);
let process_found = get_running_processes()?
.into_iter()
.any(|p| p.name == file_name);
if !process_found {
info!(log, "{} is not running", file_name);
break;
}
// give up after 60 * 500ms = 30 seconds
if attempt == 60 {
info!(log, "Gave up waiting for {} to exit", file_name);
break;
}
info!(log, "{} is running, wait a bit", file_name);
thread::sleep(time::Duration::from_millis(500));
}
// try to kill any running processes
util::retry(
"attempting to kill any running Code.exe processes",
|attempt| {
info!(
log,
"Checking for possible conflicting running processes... (attempt {})", attempt
);
let kill_errors: Vec<_> = get_running_processes()?
.into_iter()
.filter(|p| p.name == file_name)
.filter_map(|p| kill_process_if(log, &p, path).err())
.collect();
for err in &kill_errors {
warn!(log, "Kill error {}", err);
}
match kill_errors.len() {
0 => Ok(()),
_ => Err(kill_errors.into_iter().nth(1).unwrap()),
}
},
None,
)
}
/*-----------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*----------------------------------------------------------------------------------------*/
use std::ffi::c_void;
use std::path::{Path, PathBuf};
use std::{error, io, mem, ptr, thread, time};
use strings::from_utf16;
use {slog, util};
pub struct RunningProcess {
pub name: String,
pub id: u32,
}
pub fn get_running_processes() -> Result<Vec<RunningProcess>, io::Error> {
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
TH32CS_SNAPPROCESS,
};
unsafe {
let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not create process snapshot",
));
}
let mut pe32 = PROCESSENTRY32W {
dwSize: 0,
cntUsage: 0,
th32ProcessID: 0,
th32DefaultHeapID: 0,
th32ModuleID: 0,
cntThreads: 0,
th32ParentProcessID: 0,
pcPriClassBase: 0,
dwFlags: 0,
szExeFile: [0u16; 260],
};
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
if Process32FirstW(handle, &mut pe32) == 0 {
CloseHandle(handle);
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get first process data",
));
}
let mut result: Vec<RunningProcess> = vec![];
loop {
result.push(RunningProcess {
name: from_utf16(&pe32.szExeFile).map_err(|e| {
CloseHandle(handle);
e
})?,
id: pe32.th32ProcessID,
});
if Process32NextW(handle, &mut pe32) == 0 {
CloseHandle(handle);
break;
}
}
Ok(result)
}
}
/**
* Kills a running process, if its path is the same as the provided one.
*/
fn kill_process_if(
log: &slog::Logger,
process: &RunningProcess,
path: &Path,
) -> Result<(), Box<dyn error::Error>> {
use windows_sys::Win32::Foundation::{CloseHandle, MAX_PATH};
use windows_sys::Win32::System::ProcessStatus::K32GetModuleFileNameExW;
use windows_sys::Win32::System::Threading::{
OpenProcess, TerminateProcess, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE,
PROCESS_VM_READ,
};
info!(
log,
"Kill process if found: {}, {}", process.id, process.name
);
unsafe {
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
let handle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
0,
process.id,
);
if ptr::eq(handle as *mut c_void, ptr::null()) {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to open process: {}",
util::get_last_error_message()?
),
)
.into());
}
let mut raw_path = [0u16; MAX_PATH as usize];
let len = K32GetModuleFileNameExW(handle, mem::zeroed(), raw_path.as_mut_ptr(), MAX_PATH)
as usize;
if len == 0 {
CloseHandle(handle);
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to get process file name: {}",
util::get_last_error_message()?
),
)
.into());
}
let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?);
if process_path != path {
CloseHandle(handle);
return Ok(());
}
info!(
log,
"Found {} running, pid {}, attempting to kill...", process.name, process.id
);
if TerminateProcess(handle, 0).is_negative() {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into());
}
info!(
log,
"Successfully killed {}, pid {}", process.name, process.id
);
CloseHandle(handle);
Ok(())
}
}
pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box<dyn error::Error>> {
let file_name = path
.file_name()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get process file name"))?;
let file_name = file_name.to_str().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Could not get convert file name to str",
)
})?;
let mut attempt: u32 = 0;
// wait for 10 seconds until all processes are dead
loop {
attempt += 1;
info!(
log,
"Checking for running {} processes... (attempt {})", file_name, attempt
);
let process_found = get_running_processes()?
.into_iter()
.any(|p| p.name == file_name);
if !process_found {
info!(log, "{} is not running", file_name);
break;
}
// give up after 60 * 500ms = 30 seconds
if attempt == 60 {
info!(log, "Gave up waiting for {} to exit", file_name);
break;
}
info!(log, "{} is running, wait a bit", file_name);
thread::sleep(time::Duration::from_millis(500));
}
// try to kill any running processes
util::retry(
"attempting to kill any running Code.exe processes",
|attempt| {
info!(
log,
"Checking for possible conflicting running processes... (attempt {})", attempt
);
let kill_errors: Vec<_> = get_running_processes()?
.into_iter()
.filter(|p| p.name == file_name)
.filter_map(|p| kill_process_if(log, &p, path).err())
.collect();
for err in &kill_errors {
warn!(log, "Kill error {}", err);
}
match kill_errors.len() {
0 => Ok(()),
_ => Err(kill_errors.into_iter().nth(1).unwrap()),
}
},
None,
)
}

Просмотреть файл

@ -4,10 +4,9 @@
*----------------------------------------------------------------------------------------*/
use std::ffi::OsStr;
use std::io;
use std::io::prelude::*;
use std::os::windows::ffi::OsStrExt;
use std::string;
use std::{io, string};
#[derive(Debug)]
pub enum ReadUtf8StringError {
@ -23,11 +22,11 @@ pub fn read_utf8_string(
reader
.read_exact(&mut vec)
.map_err(|err| ReadUtf8StringError::IOError(err))
.map_err(ReadUtf8StringError::IOError)
.and_then(|_| {
let pos = vec.iter().position(|&x| x == 0).unwrap_or(64);
let bar = &vec[0..pos];
String::from_utf8(Vec::from(bar)).map_err(|err| ReadUtf8StringError::UTF8Error(err))
String::from_utf8(Vec::from(bar)).map_err(ReadUtf8StringError::UTF8Error)
})
}
@ -37,7 +36,7 @@ pub fn write_utf8_string(
capacity: usize,
) -> Result<(), io::Error> {
let bytes = string.as_bytes();
writer.write_all(&bytes)?;
writer.write_all(bytes)?;
let rest = vec![0; capacity - bytes.len()];
writer.write_all(&rest)?;

Просмотреть файл

@ -1,82 +1,85 @@
/*-----------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*----------------------------------------------------------------------------------------*/
use gui;
use std::{error, ptr, thread, time};
use strings::from_utf16;
/**
* Quadratic backoff retry mechanism.
*
* Use `max_attempts` to control how long it should retry for:
* - 11 (default): 19s
* - 16: ~1 minute
* - 20: ~2 minutes
* - 23: ~3 minutes
* - 25: ~4 minutes
* - 27: ~5 minutes
*/
pub fn retry<F, R, T>(task: &str, closure: F, max_attempts: T) -> Result<R, Box<dyn error::Error>>
where
F: Fn(u32) -> Result<R, Box<dyn error::Error>>,
T: Into<Option<u32>>,
{
let mut attempt: u32 = 0;
let max_attempts = max_attempts.into().unwrap_or(11);
loop {
attempt += 1;
let result = closure(attempt);
match result {
Ok(_) => return result,
Err(err) => {
if attempt >= max_attempts {
let msg = format!("There was an error while {}:\n\n{}\n\nPlease verify there are no Visual Studio Code processes still executing.", task, err);
let mb_result =
gui::message_box(&msg, "Visual Studio Code", gui::MessageBoxType::RetryCancel);
match mb_result {
gui::MessageBoxResult::Retry => {
attempt = 0;
}
_ => {
return Err(err);
}
}
}
thread::sleep(time::Duration::from_millis((attempt.pow(2) * 50) as u64));
}
}
}
}
pub fn get_last_error_message() -> Result<String, Box<dyn error::Error>> {
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::winbase::{
FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
};
let mut error_message = [0u16; 32000];
let error_message_len: usize;
unsafe {
error_message_len = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null_mut(),
GetLastError(),
0,
error_message.as_mut_ptr(),
32000,
ptr::null_mut(),
) as usize;
}
Ok(match error_message_len {
0 => String::from("unknown error"),
_ => from_utf16(&error_message[0..error_message_len])?,
})
}
/*-----------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*----------------------------------------------------------------------------------------*/
use gui;
use std::{error, ptr, thread, time};
use strings::from_utf16;
/**
* Quadratic backoff retry mechanism.
*
* Use `max_attempts` to control how long it should retry for:
* - 11 (default): 19s
* - 16: ~1 minute
* - 20: ~2 minutes
* - 23: ~3 minutes
* - 25: ~4 minutes
* - 27: ~5 minutes
*/
pub fn retry<F, R, T>(task: &str, closure: F, max_attempts: T) -> Result<R, Box<dyn error::Error>>
where
F: Fn(u32) -> Result<R, Box<dyn error::Error>>,
T: Into<Option<u32>>,
{
let mut attempt: u32 = 0;
let max_attempts = max_attempts.into().unwrap_or(11);
loop {
attempt += 1;
let result = closure(attempt);
match result {
Ok(_) => return result,
Err(err) => {
if attempt >= max_attempts {
let msg = format!("There was an error while {}:\n\n{}\n\nPlease verify there are no Visual Studio Code processes still executing.", task, err);
let mb_result = gui::message_box(
&msg,
"Visual Studio Code",
gui::MessageBoxType::RetryCancel,
);
match mb_result {
gui::MessageBoxResult::Retry => {
attempt = 0;
}
_ => {
return Err(err);
}
}
}
thread::sleep(time::Duration::from_millis((attempt.pow(2) * 50) as u64));
}
}
}
}
pub fn get_last_error_message() -> Result<String, Box<dyn error::Error>> {
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Diagnostics::Debug::{
FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
};
let mut error_message = [0u16; 32000];
let error_message_len: usize;
unsafe {
error_message_len = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null_mut(),
GetLastError(),
0,
error_message.as_mut_ptr(),
32000,
ptr::null_mut(),
) as usize;
}
Ok(match error_message_len {
0 => String::from("unknown error"),
_ => from_utf16(&error_message[0..error_message_len])?,
})
}