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",
|
||||||
"slog-async",
|
"slog-async",
|
||||||
"slog-term",
|
"slog-term",
|
||||||
"winapi",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -329,3 +329,60 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
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]
|
[package]
|
||||||
name = "inno_updater"
|
name = "inno_updater"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
authors = ["Microsoft <monacotools@microsoft.com>"]
|
authors = ["Microsoft <monacotools@microsoft.com>"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
crc = "^1.0.0"
|
crc = "^1.0.0"
|
||||||
slog = "2.1.1"
|
slog = "2.1.1"
|
||||||
slog-async = "2.2.0"
|
slog-async = "2.2.0"
|
||||||
slog-term = "2.3.0"
|
slog-term = "2.3.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies.windows-sys]
|
||||||
winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] }
|
version = "0.42"
|
||||||
|
features = [
|
||||||
[profile.release]
|
"Win32_Foundation",
|
||||||
lto = true
|
"Win32_System_Shutdown",
|
||||||
panic = 'abort'
|
"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");
|
assert!(ecode.success(), "Resource compiler failed");
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", resources.parent().unwrap().to_str().unwrap());
|
println!(
|
||||||
println!("cargo:rustc-link-lib={}", resources.file_stem().unwrap().to_str().unwrap());
|
"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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use crc::{crc32, Hasher32};
|
use crc::{crc32, Hasher32};
|
||||||
use std::cmp;
|
|
||||||
use std::io;
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::{cmp, io};
|
||||||
|
|
||||||
const BLOCK_MAX_SIZE: usize = 4096;
|
const BLOCK_MAX_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
@ -48,8 +47,8 @@ impl<'a> BlockRead<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = size as usize;
|
let size = size as usize;
|
||||||
let mut buffer = &mut self.buffer[..size];
|
let buffer = &mut self.buffer[..size];
|
||||||
self.reader.read_exact(&mut buffer)?;
|
self.reader.read_exact(buffer)?;
|
||||||
|
|
||||||
let mut digest = crc32::Digest::new(crc32::IEEE);
|
let mut digest = crc32::Digest::new(crc32::IEEE);
|
||||||
digest.write(buffer);
|
digest.write(buffer);
|
||||||
|
|
53
src/gui.rs
53
src/gui.rs
|
@ -6,13 +6,11 @@
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
use strings::to_utf16;
|
use strings::to_utf16;
|
||||||
use winapi::shared::basetsd::INT_PTR;
|
use windows_sys::core::PCWSTR;
|
||||||
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, UINT, WPARAM};
|
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, WPARAM};
|
||||||
use winapi::shared::ntdef::LPCWSTR;
|
|
||||||
use winapi::shared::windef::HWND;
|
|
||||||
|
|
||||||
extern "system" {
|
extern "system" {
|
||||||
pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: LPCWSTR) -> BOOL;
|
pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: PCWSTR) -> BOOL;
|
||||||
pub fn ShutdownBlockReasonDestroy(hWnd: HWND) -> BOOL;
|
pub fn ShutdownBlockReasonDestroy(hWnd: HWND) -> BOOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,23 +19,27 @@ struct DialogData {
|
||||||
tx: Sender<ProgressWindow>,
|
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 resources;
|
||||||
use winapi::shared::windef::RECT;
|
use windows_sys::Win32::Foundation::RECT;
|
||||||
use winapi::um::commctrl::PBM_SETMARQUEE;
|
use windows_sys::Win32::System::Threading::GetCurrentThreadId;
|
||||||
use winapi::um::processthreadsapi::GetCurrentThreadId;
|
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||||
use winapi::um::winuser::{
|
GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow,
|
||||||
GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow, HWND_TOPMOST,
|
HWND_TOPMOST, SW_HIDE, WM_DESTROY, WM_INITDIALOG, WM_USER,
|
||||||
SW_HIDE, WM_DESTROY, WM_INITDIALOG,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
WM_INITDIALOG => {
|
WM_INITDIALOG => {
|
||||||
let data = &*(l as *const DialogData);
|
let data = &*(l as *const DialogData);
|
||||||
if !data.silent {
|
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);
|
GetWindowRect(hwnd, &mut rect);
|
||||||
|
|
||||||
let width = rect.right - rect.left;
|
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);
|
ShowWindow(hwnd, SW_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
data.tx
|
||||||
.tx
|
|
||||||
.send(ProgressWindow {
|
.send(ProgressWindow {
|
||||||
ui_thread_id: GetCurrentThreadId(),
|
ui_thread_id: GetCurrentThreadId(),
|
||||||
})
|
})
|
||||||
|
@ -77,14 +78,14 @@ unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) -
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProgressWindow {
|
pub struct ProgressWindow {
|
||||||
ui_thread_id: DWORD,
|
ui_thread_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ProgressWindow {}
|
unsafe impl Send for ProgressWindow {}
|
||||||
|
|
||||||
impl ProgressWindow {
|
impl ProgressWindow {
|
||||||
pub fn exit(&self) {
|
pub fn exit(&self) {
|
||||||
use winapi::um::winuser::{PostThreadMessageW, WM_QUIT};
|
use windows_sys::Win32::UI::WindowsAndMessaging::{PostThreadMessageW, WM_QUIT};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
PostThreadMessageW(self.ui_thread_id, WM_QUIT, 0, 0);
|
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>) {
|
pub fn run_progress_window(silent: bool, tx: Sender<ProgressWindow>) {
|
||||||
use resources;
|
use resources;
|
||||||
use winapi::um::libloaderapi::GetModuleHandleW;
|
use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||||
use winapi::um::winuser::{DialogBoxParamW, MAKEINTRESOURCEW};
|
use windows_sys::Win32::UI::WindowsAndMessaging::DialogBoxParamW;
|
||||||
|
|
||||||
let data = DialogData { silent, tx };
|
let data = DialogData { silent, tx };
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
DialogBoxParamW(
|
DialogBoxParamW(
|
||||||
GetModuleHandleW(ptr::null_mut()),
|
GetModuleHandleW(ptr::null_mut()),
|
||||||
MAKEINTRESOURCEW(resources::PROGRESS_DIALOG),
|
resources::PROGRESS_DIALOG as PCWSTR,
|
||||||
ptr::null_mut(),
|
mem::zeroed(),
|
||||||
Some(dlgproc),
|
Some(dlgproc),
|
||||||
(&data as *const DialogData) as LPARAM,
|
(&data as *const DialogData) as LPARAM,
|
||||||
);
|
);
|
||||||
|
@ -130,16 +131,16 @@ pub enum MessageBoxResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_box(text: &str, caption: &str, mbtype: MessageBoxType) -> MessageBoxResult {
|
pub fn message_box(text: &str, caption: &str, mbtype: MessageBoxType) -> MessageBoxResult {
|
||||||
use winapi::um::winuser::{
|
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||||
MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, IDYES,
|
MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN,
|
||||||
MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL,
|
IDYES, MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result: i32;
|
let result: i32;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
result = MessageBoxW(
|
result = MessageBoxW(
|
||||||
ptr::null_mut(),
|
mem::zeroed(),
|
||||||
to_utf16(text).as_ptr(),
|
to_utf16(text).as_ptr(),
|
||||||
to_utf16(caption).as_ptr(),
|
to_utf16(caption).as_ptr(),
|
||||||
match mbtype {
|
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.
|
* 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::path::Path;
|
||||||
use std::{error, io, ptr};
|
use std::{error, io, ptr};
|
||||||
use strings::to_u16s;
|
use strings::to_u16s;
|
||||||
use util;
|
use util;
|
||||||
use winapi::um::winnt::HANDLE;
|
use windows_sys::Win32::Foundation::HANDLE;
|
||||||
|
|
||||||
pub struct FileHandle(HANDLE);
|
pub struct FileHandle(HANDLE);
|
||||||
|
|
||||||
impl FileHandle {
|
impl FileHandle {
|
||||||
pub fn new(path: &Path) -> Result<FileHandle, Box<dyn error::Error>> {
|
pub fn new(path: &Path) -> Result<FileHandle, Box<dyn error::Error>> {
|
||||||
use winapi::um::fileapi::CreateFileW;
|
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
|
||||||
use winapi::um::fileapi::OPEN_EXISTING;
|
use windows_sys::Win32::Storage::FileSystem::{
|
||||||
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
|
CreateFileW, DELETE, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING,
|
||||||
use winapi::um::winnt::DELETE;
|
};
|
||||||
use winapi::um::winnt::FILE_ATTRIBUTE_NORMAL;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let handle = CreateFileW(
|
let handle = CreateFileW(
|
||||||
to_u16s(path.as_os_str()).as_ptr(),
|
to_u16s(path.as_os_str()).as_ptr(),
|
||||||
DELETE,
|
DELETE,
|
||||||
0,
|
0,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
ptr::null_mut(),
|
std::mem::zeroed(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if handle == INVALID_HANDLE_VALUE {
|
if handle == INVALID_HANDLE_VALUE {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"Failed to create file handle: {}",
|
"Failed to create file handle: {}",
|
||||||
util::get_last_error_message()?
|
util::get_last_error_message()?
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FileHandle(handle))
|
Ok(FileHandle(handle))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_for_deletion(&self) -> Result<(), Box<dyn error::Error>> {
|
pub fn mark_for_deletion(&self) -> Result<(), Box<dyn error::Error>> {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use winapi::shared::minwindef::{DWORD, FALSE, LPVOID, TRUE};
|
use windows_sys::Win32::Foundation::BOOLEAN;
|
||||||
use winapi::um::fileapi::SetFileInformationByHandle;
|
use windows_sys::Win32::Storage::FileSystem::{
|
||||||
use winapi::um::fileapi::FILE_DISPOSITION_INFO;
|
FileDispositionInfo, SetFileInformationByHandle, FILE_DISPOSITION_INFO,
|
||||||
use winapi::um::minwinbase::FileDispositionInfo;
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut info = FILE_DISPOSITION_INFO { DeleteFile: TRUE as u8 };
|
let mut info = FILE_DISPOSITION_INFO {
|
||||||
let result = SetFileInformationByHandle(
|
DeleteFile: 1 as BOOLEAN,
|
||||||
self.0,
|
};
|
||||||
FileDispositionInfo,
|
let result = SetFileInformationByHandle(
|
||||||
&mut info as *mut _ as LPVOID,
|
self.0,
|
||||||
mem::size_of::<FILE_DISPOSITION_INFO>() as DWORD,
|
FileDispositionInfo,
|
||||||
);
|
&mut info as *mut _ as *mut c_void,
|
||||||
|
mem::size_of::<FILE_DISPOSITION_INFO>() as u32,
|
||||||
|
);
|
||||||
|
|
||||||
if result == FALSE {
|
if result.is_negative() {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"Failed to mark file for deletion: {}",
|
"Failed to mark file for deletion: {}",
|
||||||
util::get_last_error_message()?
|
util::get_last_error_message()?
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) -> Result<(), Box<dyn error::Error>> {
|
pub fn close(&self) -> Result<(), Box<dyn error::Error>> {
|
||||||
use winapi::shared::minwindef::FALSE;
|
use windows_sys::Win32::Foundation::CloseHandle;
|
||||||
use winapi::um::handleapi::CloseHandle;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if CloseHandle(self.0) == FALSE {
|
if CloseHandle(self.0).is_negative() {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"Failed to close file handle: {}",
|
"Failed to close file handle: {}",
|
||||||
util::get_last_error_message()?
|
util::get_last_error_message()?
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
107
src/main.rs
107
src/main.rs
|
@ -11,7 +11,7 @@ extern crate crc;
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
extern crate slog_async;
|
extern crate slog_async;
|
||||||
extern crate slog_term;
|
extern crate slog_term;
|
||||||
extern crate winapi;
|
extern crate windows_sys;
|
||||||
|
|
||||||
mod blockio;
|
mod blockio;
|
||||||
mod gui;
|
mod gui;
|
||||||
|
@ -33,7 +33,7 @@ use std::time::SystemTime;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::{env, error, fmt, fs, io, thread};
|
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>> {
|
fn read_file(path: &Path) -> Result<(Header, Vec<FileRec>), Box<dyn error::Error>> {
|
||||||
let input_file = fs::File::open(path)?;
|
let input_file = fs::File::open(path)?;
|
||||||
|
@ -100,17 +100,15 @@ fn delete_existing_version(
|
||||||
let root = PathBuf::from(root_path);
|
let root = PathBuf::from(root_path);
|
||||||
directories.push_back(root);
|
directories.push_back(root);
|
||||||
|
|
||||||
while directories.len() > 0 {
|
while let Some(dir) = directories.pop_front() {
|
||||||
let dir = directories.pop_front().unwrap();
|
|
||||||
info!(log, "Reading directory: {:?}", dir);
|
info!(log, "Reading directory: {:?}", dir);
|
||||||
|
|
||||||
for entry in fs::read_dir(&dir)? {
|
for entry in fs::read_dir(&dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let entry_name = entry.file_name();
|
let entry_name = entry.file_name();
|
||||||
let entry_name = entry_name.to_str().ok_or(io::Error::new(
|
let entry_name = entry_name
|
||||||
io::ErrorKind::Other,
|
.to_str()
|
||||||
"Could not get entry name",
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?;
|
||||||
))?;
|
|
||||||
|
|
||||||
if dir == root_path {
|
if dir == root_path {
|
||||||
// don't delete the update folder
|
// don't delete the update folder
|
||||||
|
@ -122,7 +120,7 @@ fn delete_existing_version(
|
||||||
if entry_name == "tools" {
|
if entry_name == "tools" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't delete any of the unins* files
|
// don't delete any of the unins* files
|
||||||
if entry_name.starts_with("unins") {
|
if entry_name.starts_with("unins") {
|
||||||
continue;
|
continue;
|
||||||
|
@ -220,10 +218,12 @@ fn move_update(
|
||||||
"move_update: {:?}, {}", uninstdat_path, update_folder_name
|
"move_update: {:?}, {}", uninstdat_path, update_folder_name
|
||||||
);
|
);
|
||||||
|
|
||||||
let root_path = uninstdat_path.parent().ok_or(io::Error::new(
|
let root_path = uninstdat_path.parent().ok_or_else(|| {
|
||||||
io::ErrorKind::Other,
|
io::Error::new(
|
||||||
"Could not get parent path of uninstdat",
|
io::ErrorKind::Other,
|
||||||
))?;
|
"Could not get parent path of uninstdat",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut update_path = PathBuf::from(root_path);
|
let mut update_path = PathBuf::from(root_path);
|
||||||
update_path.push(update_folder_name);
|
update_path.push(update_folder_name);
|
||||||
|
@ -231,7 +231,9 @@ fn move_update(
|
||||||
let stat = fs::metadata(&update_path)?;
|
let stat = fs::metadata(&update_path)?;
|
||||||
|
|
||||||
if !stat.is_dir() {
|
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
|
// safely delete all current files
|
||||||
|
@ -241,10 +243,9 @@ fn move_update(
|
||||||
for entry in fs::read_dir(&update_path)? {
|
for entry in fs::read_dir(&update_path)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let entry_name = entry.file_name();
|
let entry_name = entry.file_name();
|
||||||
let entry_name = entry_name.to_str().ok_or(io::Error::new(
|
let entry_name = entry_name
|
||||||
io::ErrorKind::Other,
|
.to_str()
|
||||||
"Could not get entry name",
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?;
|
||||||
))?;
|
|
||||||
|
|
||||||
let mut target = PathBuf::from(root_path);
|
let mut target = PathBuf::from(root_path);
|
||||||
target.push(entry_name);
|
target.push(entry_name);
|
||||||
|
@ -272,15 +273,17 @@ fn patch_uninstdat(
|
||||||
uninstdat_path: &PathBuf,
|
uninstdat_path: &PathBuf,
|
||||||
update_folder_name: &str,
|
update_folder_name: &str,
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> 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, "header: {:?}", header);
|
||||||
info!(log, "num_recs: {:?}", recs.len());
|
info!(log, "num_recs: {:?}", recs.len());
|
||||||
|
|
||||||
let root_path = uninstdat_path.parent().ok_or(io::Error::new(
|
let root_path = uninstdat_path.parent().ok_or_else(|| {
|
||||||
io::ErrorKind::Other,
|
io::Error::new(
|
||||||
"Could not get parent path of uninstdat",
|
io::ErrorKind::Other,
|
||||||
))?;
|
"Could not get parent path of uninstdat",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut update_path = PathBuf::from(root_path);
|
let mut update_path = PathBuf::from(root_path);
|
||||||
update_path.push(&update_folder_name);
|
update_path.push(&update_folder_name);
|
||||||
|
@ -296,7 +299,7 @@ fn patch_uninstdat(
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
info!(log, "Updating uninstall file {:?}", uninstdat_path);
|
info!(log, "Updating uninstall file {:?}", uninstdat_path);
|
||||||
write_file(&uninstdat_path, &header, recs?)?;
|
write_file(uninstdat_path, &header, recs?)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -308,10 +311,12 @@ fn do_update(
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
info!(log, "do_update: {:?}, {}", code_path, update_folder_name);
|
info!(log, "do_update: {:?}, {}", code_path, update_folder_name);
|
||||||
|
|
||||||
let root_path = code_path.parent().ok_or(io::Error::new(
|
let root_path = code_path.parent().ok_or_else(|| {
|
||||||
io::ErrorKind::Other,
|
io::Error::new(
|
||||||
"Could not get parent path of uninstdat",
|
io::ErrorKind::Other,
|
||||||
))?;
|
"Could not get parent path of uninstdat",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut uninstdat_path = PathBuf::from(root_path);
|
let mut uninstdat_path = PathBuf::from(root_path);
|
||||||
uninstdat_path.push("unins000.dat");
|
uninstdat_path.push("unins000.dat");
|
||||||
|
@ -349,7 +354,7 @@ fn update(
|
||||||
.recv()
|
.recv()
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Could not receive GUI window handle"))?;
|
.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();
|
window.exit();
|
||||||
|
|
||||||
Ok(())
|
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]);
|
info!(log, "Starting: {}, {}", args[1], args[2]);
|
||||||
|
|
||||||
let code_path = PathBuf::from(&args[1]);
|
let code_path = PathBuf::from(&args[1]);
|
||||||
|
|
||||||
if !code_path.is_absolute() {
|
if !code_path.is_absolute() {
|
||||||
return Err(
|
return Err(ArgumentError(format!(
|
||||||
ArgumentError(format!(
|
"Code path needs to be absolute. Instead got: {}",
|
||||||
"Code path needs to be absolute. Instead got: {}",
|
args[1]
|
||||||
args[1]
|
))
|
||||||
))
|
.into());
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !code_path.exists() {
|
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();
|
let silent = args[2].clone();
|
||||||
|
|
||||||
if silent != "true" && silent != "false" {
|
if silent != "true" && silent != "false" {
|
||||||
return Err(
|
return Err(ArgumentError(format!(
|
||||||
ArgumentError(format!(
|
"Silent needs to be true or false. Instead got: {}",
|
||||||
"Silent needs to be true or false. Instead got: {}",
|
silent
|
||||||
silent
|
))
|
||||||
))
|
.into());
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(log, &code_path, "_", silent == "true")
|
update(log, &code_path, "_", silent == "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(log_path: &str) {
|
fn handle_error(log_path: &str) {
|
||||||
let mut msgs = Vec::new();
|
let msg = format!(
|
||||||
|
"Failed to install Visual Studio Code update.\n\n\
|
||||||
msgs.push("Failed to install Visual Studio Code update.");
|
Updates may fail due to anti-virus software and/or runaway processes. Please try restarting your machine before attempting to update again.\n\n\
|
||||||
msgs.push("Updates may fail due to anti-virus software and/or runaway processes. Please try restarting your machine before attempting to update again.");
|
Please read the log file for more information:\n\n\
|
||||||
msgs.push("Please read the log file for more information:");
|
{log_path}"
|
||||||
msgs.push(log_path);
|
);
|
||||||
|
|
||||||
let msg = msgs.join("\n\n");
|
|
||||||
|
|
||||||
gui::message_box(&msg, "Visual Studio Code", gui::MessageBoxType::Error);
|
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();
|
let mut log_path = env::temp_dir();
|
||||||
log_path.push(format!(
|
log_path.push(format!(
|
||||||
"vscode-inno-updater-{:?}.log",
|
"vscode-inno-updater-{:?}.log",
|
||||||
|
@ -522,7 +521,7 @@ fn main() {
|
||||||
Some(5),
|
Some(5),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Err(_) = result {
|
if result.is_err() {
|
||||||
handle_error(&log_path);
|
handle_error(&log_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub struct FileRec {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Debug for FileRec {
|
impl fmt::Debug for FileRec {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
|
@ -95,10 +95,10 @@ impl<'a> error::Error for StringDecodeError<'a> {
|
||||||
|
|
||||||
fn decode_strings<'a>(data: &[u8]) -> Result<Vec<String>, StringDecodeError<'a>> {
|
fn decode_strings<'a>(data: &[u8]) -> Result<Vec<String>, StringDecodeError<'a>> {
|
||||||
let mut result: Vec<String> = Vec::with_capacity(10);
|
let mut result: Vec<String> = Vec::with_capacity(10);
|
||||||
let mut slice = data.clone();
|
let mut slice = data;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let reader: &mut dyn Read = &mut slice.clone();
|
let reader: &mut dyn Read = &mut slice;
|
||||||
let byte_result = reader
|
let byte_result = reader
|
||||||
.read_u8()
|
.read_u8()
|
||||||
.map_err(|_| StringDecodeError("Failed to parse file rec string header"))?;
|
.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>> {
|
pub fn from_reader<'b>(reader: &mut dyn Read) -> Result<FileRec, FileRecParseError<'b>> {
|
||||||
let typ = reader
|
let typ = reader
|
||||||
.read_u16::<LittleEndian>()
|
.read_u16::<LittleEndian>()
|
||||||
|
@ -251,7 +251,8 @@ impl<'a> FileRec {
|
||||||
.map_err(|_| FileRecParseError("Failed to parse file rec extra data"))?;
|
.map_err(|_| FileRecParseError("Failed to parse file rec extra data"))?;
|
||||||
let data_size = reader
|
let data_size = reader
|
||||||
.read_u32::<LittleEndian>()
|
.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 {
|
if data_size > 0x8000000 {
|
||||||
return Err(FileRecParseError("File rec data size too large"));
|
return Err(FileRecParseError("File rec data size too large"));
|
||||||
|
@ -303,8 +304,8 @@ impl<'a> FileRec {
|
||||||
let rebased_paths: Vec<String> = paths
|
let rebased_paths: Vec<String> = paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
if p.starts_with(from) {
|
if let Some(p) = p.strip_prefix(from) {
|
||||||
[to, &p[from.len()..]].join("")
|
format!("{to}{p}")
|
||||||
} else {
|
} else {
|
||||||
p.clone()
|
p.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,7 @@ impl Header {
|
||||||
.map_err(|_| HeaderParseError("Failed to parse header flags"))?;
|
.map_err(|_| HeaderParseError("Failed to parse header flags"))?;
|
||||||
|
|
||||||
let mut reserved = [0; 108];
|
let mut reserved = [0; 108];
|
||||||
read
|
read.read_exact(&mut reserved)
|
||||||
.read_exact(&mut reserved)
|
|
||||||
.map_err(|_| HeaderParseError("Failed to parse header reserved"))?;
|
.map_err(|_| HeaderParseError("Failed to parse header reserved"))?;
|
||||||
|
|
||||||
let crc = read
|
let crc = read
|
||||||
|
|
464
src/process.rs
464
src/process.rs
|
@ -1,235 +1,229 @@
|
||||||
/*-----------------------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||||
*----------------------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
use slog;
|
use std::ffi::c_void;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{error, io, mem, ptr, thread, time};
|
use std::{error, io, mem, ptr, thread, time};
|
||||||
use strings::from_utf16;
|
use strings::from_utf16;
|
||||||
use util;
|
use {slog, util};
|
||||||
use winapi::shared::minwindef::{DWORD, TRUE};
|
|
||||||
|
pub struct RunningProcess {
|
||||||
pub struct RunningProcess {
|
pub name: String,
|
||||||
pub name: String,
|
pub id: u32,
|
||||||
pub id: DWORD,
|
}
|
||||||
}
|
|
||||||
|
pub fn get_running_processes() -> Result<Vec<RunningProcess>, io::Error> {
|
||||||
pub fn get_running_processes() -> Result<Vec<RunningProcess>, io::Error> {
|
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
|
||||||
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
|
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
|
||||||
use winapi::um::tlhelp32::{
|
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W,
|
||||||
CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS,
|
TH32CS_SNAPPROCESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
|
||||||
if handle == INVALID_HANDLE_VALUE {
|
if handle == INVALID_HANDLE_VALUE {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"Could not create process snapshot",
|
"Could not create process snapshot",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pe32 = PROCESSENTRY32W {
|
let mut pe32 = PROCESSENTRY32W {
|
||||||
dwSize: 0,
|
dwSize: 0,
|
||||||
cntUsage: 0,
|
cntUsage: 0,
|
||||||
th32ProcessID: 0,
|
th32ProcessID: 0,
|
||||||
th32DefaultHeapID: 0,
|
th32DefaultHeapID: 0,
|
||||||
th32ModuleID: 0,
|
th32ModuleID: 0,
|
||||||
cntThreads: 0,
|
cntThreads: 0,
|
||||||
th32ParentProcessID: 0,
|
th32ParentProcessID: 0,
|
||||||
pcPriClassBase: 0,
|
pcPriClassBase: 0,
|
||||||
dwFlags: 0,
|
dwFlags: 0,
|
||||||
szExeFile: [0u16; 260],
|
szExeFile: [0u16; 260],
|
||||||
};
|
};
|
||||||
|
|
||||||
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
|
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
|
||||||
|
|
||||||
if Process32FirstW(handle, &mut pe32) != TRUE {
|
if Process32FirstW(handle, &mut pe32) == 0 {
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
|
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"Could not get first process data",
|
"Could not get first process data",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result: Vec<RunningProcess> = vec![];
|
let mut result: Vec<RunningProcess> = vec![];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
result.push(RunningProcess {
|
result.push(RunningProcess {
|
||||||
name: from_utf16(&pe32.szExeFile).map_err(|e| {
|
name: from_utf16(&pe32.szExeFile).map_err(|e| {
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
e
|
e
|
||||||
})?,
|
})?,
|
||||||
id: pe32.th32ProcessID,
|
id: pe32.th32ProcessID,
|
||||||
});
|
});
|
||||||
|
|
||||||
if Process32NextW(handle, &mut pe32) != TRUE {
|
if Process32NextW(handle, &mut pe32) == 0 {
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(result);
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kills a running process, if its path is the same as the provided one.
|
* Kills a running process, if its path is the same as the provided one.
|
||||||
*/
|
*/
|
||||||
fn kill_process_if(
|
fn kill_process_if(
|
||||||
log: &slog::Logger,
|
log: &slog::Logger,
|
||||||
process: &RunningProcess,
|
process: &RunningProcess,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
use winapi::shared::minwindef::MAX_PATH;
|
use windows_sys::Win32::Foundation::{CloseHandle, MAX_PATH};
|
||||||
use winapi::um::handleapi::CloseHandle;
|
use windows_sys::Win32::System::ProcessStatus::K32GetModuleFileNameExW;
|
||||||
use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
|
use windows_sys::Win32::System::Threading::{
|
||||||
use winapi::um::psapi::GetModuleFileNameExW;
|
OpenProcess, TerminateProcess, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE,
|
||||||
use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ};
|
PROCESS_VM_READ,
|
||||||
|
};
|
||||||
info!(
|
|
||||||
log,
|
info!(
|
||||||
"Kill process if found: {}, {}", process.id, process.name
|
log,
|
||||||
);
|
"Kill process if found: {}, {}", process.id, process.name
|
||||||
|
);
|
||||||
unsafe {
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
|
unsafe {
|
||||||
let handle = OpenProcess(
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
|
||||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
|
let handle = OpenProcess(
|
||||||
0,
|
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
|
||||||
process.id,
|
0,
|
||||||
);
|
process.id,
|
||||||
|
);
|
||||||
if handle.is_null() {
|
|
||||||
return Err(
|
if ptr::eq(handle as *mut c_void, ptr::null()) {
|
||||||
io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"Failed to open process: {}",
|
"Failed to open process: {}",
|
||||||
util::get_last_error_message()?
|
util::get_last_error_message()?
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.into(),
|
.into());
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
let mut raw_path = [0u16; MAX_PATH as usize];
|
||||||
let mut raw_path = [0u16; MAX_PATH];
|
let len = K32GetModuleFileNameExW(handle, mem::zeroed(), raw_path.as_mut_ptr(), MAX_PATH)
|
||||||
let len = GetModuleFileNameExW(
|
as usize;
|
||||||
handle,
|
|
||||||
ptr::null_mut(),
|
if len == 0 {
|
||||||
raw_path.as_mut_ptr(),
|
CloseHandle(handle);
|
||||||
MAX_PATH as DWORD,
|
|
||||||
) as usize;
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
if len == 0 {
|
format!(
|
||||||
CloseHandle(handle);
|
"Failed to get process file name: {}",
|
||||||
|
util::get_last_error_message()?
|
||||||
return Err(
|
),
|
||||||
io::Error::new(
|
)
|
||||||
io::ErrorKind::Other,
|
.into());
|
||||||
format!(
|
}
|
||||||
"Failed to get process file name: {}",
|
|
||||||
util::get_last_error_message()?
|
let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?);
|
||||||
),
|
|
||||||
)
|
if process_path != path {
|
||||||
.into(),
|
CloseHandle(handle);
|
||||||
);
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?);
|
info!(
|
||||||
|
log,
|
||||||
if process_path != path {
|
"Found {} running, pid {}, attempting to kill...", process.name, process.id
|
||||||
CloseHandle(handle);
|
);
|
||||||
return Ok(());
|
|
||||||
}
|
if TerminateProcess(handle, 0).is_negative() {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into());
|
||||||
info!(
|
}
|
||||||
log,
|
|
||||||
"Found {} running, pid {}, attempting to kill...", process.name, process.id
|
info!(
|
||||||
);
|
log,
|
||||||
|
"Successfully killed {}, pid {}", process.name, process.id
|
||||||
if TerminateProcess(handle, 0) != TRUE {
|
);
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into());
|
|
||||||
}
|
CloseHandle(handle);
|
||||||
|
Ok(())
|
||||||
info!(
|
}
|
||||||
log,
|
}
|
||||||
"Successfully killed {}, pid {}", process.name, process.id
|
|
||||||
);
|
pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box<dyn error::Error>> {
|
||||||
|
let file_name = path
|
||||||
CloseHandle(handle);
|
.file_name()
|
||||||
Ok(())
|
.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(
|
||||||
pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box<dyn error::Error>> {
|
io::ErrorKind::Other,
|
||||||
let file_name = path.file_name().ok_or(io::Error::new(
|
"Could not get convert file name to str",
|
||||||
io::ErrorKind::Other,
|
)
|
||||||
"Could not get process file name",
|
})?;
|
||||||
))?;
|
|
||||||
|
let mut attempt: u32 = 0;
|
||||||
let file_name = file_name.to_str().ok_or(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
// wait for 10 seconds until all processes are dead
|
||||||
"Could not get convert file name to str",
|
loop {
|
||||||
))?;
|
attempt += 1;
|
||||||
|
|
||||||
let mut attempt: u32 = 0;
|
info!(
|
||||||
|
log,
|
||||||
// wait for 10 seconds until all processes are dead
|
"Checking for running {} processes... (attempt {})", file_name, attempt
|
||||||
loop {
|
);
|
||||||
attempt += 1;
|
|
||||||
|
let process_found = get_running_processes()?
|
||||||
info!(
|
.into_iter()
|
||||||
log,
|
.any(|p| p.name == file_name);
|
||||||
"Checking for running {} processes... (attempt {})", file_name, attempt
|
|
||||||
);
|
if !process_found {
|
||||||
|
info!(log, "{} is not running", file_name);
|
||||||
let process_found = get_running_processes()?
|
break;
|
||||||
.into_iter()
|
}
|
||||||
.any(|p| p.name == file_name);
|
|
||||||
|
// give up after 60 * 500ms = 30 seconds
|
||||||
if !process_found {
|
if attempt == 60 {
|
||||||
info!(log, "{} is not running", file_name);
|
info!(log, "Gave up waiting for {} to exit", file_name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// give up after 60 * 500ms = 30 seconds
|
info!(log, "{} is running, wait a bit", file_name);
|
||||||
if attempt == 60 {
|
thread::sleep(time::Duration::from_millis(500));
|
||||||
info!(log, "Gave up waiting for {} to exit", file_name);
|
}
|
||||||
break;
|
|
||||||
}
|
// try to kill any running processes
|
||||||
|
util::retry(
|
||||||
info!(log, "{} is running, wait a bit", file_name);
|
"attempting to kill any running Code.exe processes",
|
||||||
thread::sleep(time::Duration::from_millis(500));
|
|attempt| {
|
||||||
}
|
info!(
|
||||||
|
log,
|
||||||
// try to kill any running processes
|
"Checking for possible conflicting running processes... (attempt {})", attempt
|
||||||
util::retry(
|
);
|
||||||
"attempting to kill any running Code.exe processes",
|
|
||||||
|attempt| {
|
let kill_errors: Vec<_> = get_running_processes()?
|
||||||
info!(
|
.into_iter()
|
||||||
log,
|
.filter(|p| p.name == file_name)
|
||||||
"Checking for possible conflicting running processes... (attempt {})", attempt
|
.filter_map(|p| kill_process_if(log, &p, path).err())
|
||||||
);
|
.collect();
|
||||||
|
|
||||||
let kill_errors: Vec<_> = get_running_processes()?
|
for err in &kill_errors {
|
||||||
.into_iter()
|
warn!(log, "Kill error {}", err);
|
||||||
.filter(|p| p.name == file_name)
|
}
|
||||||
.filter_map(|p| kill_process_if(log, &p, path).err())
|
|
||||||
.collect();
|
match kill_errors.len() {
|
||||||
|
0 => Ok(()),
|
||||||
for err in &kill_errors {
|
_ => Err(kill_errors.into_iter().nth(1).unwrap()),
|
||||||
warn!(log, "Kill error {}", err);
|
}
|
||||||
}
|
},
|
||||||
|
None,
|
||||||
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::ffi::OsStr;
|
||||||
use std::io;
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::string;
|
use std::{io, string};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadUtf8StringError {
|
pub enum ReadUtf8StringError {
|
||||||
|
@ -23,11 +22,11 @@ pub fn read_utf8_string(
|
||||||
|
|
||||||
reader
|
reader
|
||||||
.read_exact(&mut vec)
|
.read_exact(&mut vec)
|
||||||
.map_err(|err| ReadUtf8StringError::IOError(err))
|
.map_err(ReadUtf8StringError::IOError)
|
||||||
.and_then(|_| {
|
.and_then(|_| {
|
||||||
let pos = vec.iter().position(|&x| x == 0).unwrap_or(64);
|
let pos = vec.iter().position(|&x| x == 0).unwrap_or(64);
|
||||||
let bar = &vec[0..pos];
|
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,
|
capacity: usize,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
let bytes = string.as_bytes();
|
let bytes = string.as_bytes();
|
||||||
writer.write_all(&bytes)?;
|
writer.write_all(bytes)?;
|
||||||
|
|
||||||
let rest = vec![0; capacity - bytes.len()];
|
let rest = vec![0; capacity - bytes.len()];
|
||||||
writer.write_all(&rest)?;
|
writer.write_all(&rest)?;
|
||||||
|
|
167
src/util.rs
167
src/util.rs
|
@ -1,82 +1,85 @@
|
||||||
/*-----------------------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------------------
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
* Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||||
*----------------------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
use gui;
|
use gui;
|
||||||
use std::{error, ptr, thread, time};
|
use std::{error, ptr, thread, time};
|
||||||
use strings::from_utf16;
|
use strings::from_utf16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quadratic backoff retry mechanism.
|
* Quadratic backoff retry mechanism.
|
||||||
*
|
*
|
||||||
* Use `max_attempts` to control how long it should retry for:
|
* Use `max_attempts` to control how long it should retry for:
|
||||||
* - 11 (default): 19s
|
* - 11 (default): 19s
|
||||||
* - 16: ~1 minute
|
* - 16: ~1 minute
|
||||||
* - 20: ~2 minutes
|
* - 20: ~2 minutes
|
||||||
* - 23: ~3 minutes
|
* - 23: ~3 minutes
|
||||||
* - 25: ~4 minutes
|
* - 25: ~4 minutes
|
||||||
* - 27: ~5 minutes
|
* - 27: ~5 minutes
|
||||||
*/
|
*/
|
||||||
pub fn retry<F, R, T>(task: &str, closure: F, max_attempts: T) -> Result<R, Box<dyn error::Error>>
|
pub fn retry<F, R, T>(task: &str, closure: F, max_attempts: T) -> Result<R, Box<dyn error::Error>>
|
||||||
where
|
where
|
||||||
F: Fn(u32) -> Result<R, Box<dyn error::Error>>,
|
F: Fn(u32) -> Result<R, Box<dyn error::Error>>,
|
||||||
T: Into<Option<u32>>,
|
T: Into<Option<u32>>,
|
||||||
{
|
{
|
||||||
let mut attempt: u32 = 0;
|
let mut attempt: u32 = 0;
|
||||||
let max_attempts = max_attempts.into().unwrap_or(11);
|
let max_attempts = max_attempts.into().unwrap_or(11);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
attempt += 1;
|
attempt += 1;
|
||||||
|
|
||||||
let result = closure(attempt);
|
let result = closure(attempt);
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => return result,
|
Ok(_) => return result,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if attempt >= max_attempts {
|
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 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 =
|
let mb_result = gui::message_box(
|
||||||
gui::message_box(&msg, "Visual Studio Code", gui::MessageBoxType::RetryCancel);
|
&msg,
|
||||||
|
"Visual Studio Code",
|
||||||
match mb_result {
|
gui::MessageBoxType::RetryCancel,
|
||||||
gui::MessageBoxResult::Retry => {
|
);
|
||||||
attempt = 0;
|
|
||||||
}
|
match mb_result {
|
||||||
_ => {
|
gui::MessageBoxResult::Retry => {
|
||||||
return Err(err);
|
attempt = 0;
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
}
|
return Err(err);
|
||||||
|
}
|
||||||
thread::sleep(time::Duration::from_millis((attempt.pow(2) * 50) as u64));
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
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,
|
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::{
|
||||||
let mut error_message = [0u16; 32000];
|
FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
let error_message_len: usize;
|
};
|
||||||
|
|
||||||
unsafe {
|
let mut error_message = [0u16; 32000];
|
||||||
error_message_len = FormatMessageW(
|
let error_message_len: usize;
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
ptr::null_mut(),
|
unsafe {
|
||||||
GetLastError(),
|
error_message_len = FormatMessageW(
|
||||||
0,
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
error_message.as_mut_ptr(),
|
ptr::null_mut(),
|
||||||
32000,
|
GetLastError(),
|
||||||
ptr::null_mut(),
|
0,
|
||||||
) as usize;
|
error_message.as_mut_ptr(),
|
||||||
}
|
32000,
|
||||||
|
ptr::null_mut(),
|
||||||
Ok(match error_message_len {
|
) as usize;
|
||||||
0 => String::from("unknown error"),
|
}
|
||||||
_ => from_utf16(&error_message[0..error_message_len])?,
|
|
||||||
})
|
Ok(match error_message_len {
|
||||||
}
|
0 => String::from("unknown error"),
|
||||||
|
_ => from_utf16(&error_message[0..error_message_len])?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче