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

* feat: replace unmaintained winapi with windows-sys

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

* address feedback

* fix bugs

---------

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

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

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

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

@ -116,7 +116,7 @@ dependencies = [
"slog", "slog",
"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"

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

@ -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'

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

@ -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);

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

@ -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 {

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

@ -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(())
} }
} }

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

@ -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

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

@ -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)?;

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

@ -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])?,
})
}