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:
Родитель
fe029f3e56
Коммит
0748f6b08d
|
@ -0,0 +1,4 @@
|
|||
hard_tabs = true
|
||||
|
||||
# Requires nightly
|
||||
# imports_granularity = "Module"
|
|
@ -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"
|
||||
|
|
50
Cargo.toml
50
Cargo.toml
|
@ -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'
|
||||
|
|
10
build.rs
10
build.rs
|
@ -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);
|
||||
|
|
53
src/gui.rs
53
src/gui.rs
|
@ -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 {
|
||||
|
|
149
src/handle.rs
149
src/handle.rs
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
107
src/main.rs
107
src/main.rs
|
@ -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
|
||||
|
|
464
src/process.rs
464
src/process.rs
|
@ -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)?;
|
||||
|
|
167
src/util.rs
167
src/util.rs
|
@ -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])?,
|
||||
})
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче