mirror of
				https://github.com/velopack/velopack.git
				synced 2025-10-25 15:19:22 +00:00 
			
		
		
		
	Stop redirecting stdout on hook execution
This commit is contained in:
		| @@ -10,7 +10,6 @@ use std::{ | |||||||
|     env, |     env, | ||||||
|     fs::{self, File}, |     fs::{self, File}, | ||||||
|     path::{Path, PathBuf}, |     path::{Path, PathBuf}, | ||||||
|     time::Duration, |  | ||||||
| }; | }; | ||||||
| use winsafe::{self as w, co}; | use winsafe::{self as w, co}; | ||||||
|  |  | ||||||
| @@ -184,22 +183,13 @@ fn install_impl(pkg: &bundle::BundleInfo, root_path: &PathBuf, tx: &std::sync::m | |||||||
|     info!("Creating new default shortcuts..."); |     info!("Creating new default shortcuts..."); | ||||||
|     let _ = windows::create_default_lnks(&root_path, &app); |     let _ = windows::create_default_lnks(&root_path, &app); | ||||||
|  |  | ||||||
|     let ver_string = app.version.to_string(); |     info!("Starting process install hook"); | ||||||
|     info!("Starting process install hook: \"{}\" --veloapp-install {}", &main_exe_path, &ver_string); |     if windows::run_hook(&app, &root_path, "--veloapp-install", 30) == false { | ||||||
|     let args = vec!["--veloapp-install", &ver_string]; |  | ||||||
|     if let Err(e) = windows::run_process_no_console_and_wait(&main_exe_path, args, ¤t_path, Some(Duration::from_secs(30))) { |  | ||||||
|         let setup_name = format!("{} Setup {}", app.title, app.version); |         let setup_name = format!("{} Setup {}", app.title, app.version); | ||||||
|         error!("Process install hook failed: {}", e); |         dialogs::show_warn(&setup_name, None, "Installation has completed, but the application install hook failed. It may not have installed correctly."); | ||||||
|         let _ = tx.send(windows::splash::MSG_CLOSE); |  | ||||||
|         dialogs::show_warn( |  | ||||||
|             &setup_name, |  | ||||||
|             None, |  | ||||||
|             format!("Installation has completed, but the application install hook failed ({}). It may not have installed correctly.", e).as_str(), |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let _ = tx.send(100); |     let _ = tx.send(100); | ||||||
|  |  | ||||||
|     app.write_uninstall_entry(root_path)?; |     app.write_uninstall_entry(root_path)?; | ||||||
|  |  | ||||||
|     if !dialogs::get_silent() { |     if !dialogs::get_silent() { | ||||||
|   | |||||||
| @@ -1,13 +1,16 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     dialogs, |     dialogs, | ||||||
|     shared::{self, bundle, OperationWait}, |     shared::{self, bundle, OperationWait}, | ||||||
|     windows, |     windows as win, | ||||||
| }; | }; | ||||||
| use anyhow::{anyhow, bail, Result}; | use anyhow::{anyhow, bail, Result}; | ||||||
|  | use std::os::windows::process::CommandExt; | ||||||
| use std::{ | use std::{ | ||||||
|     fs, |     fs, | ||||||
|     path::{Path, PathBuf}, |     path::{Path, PathBuf}, | ||||||
|  |     process::Command as Process, | ||||||
| }; | }; | ||||||
|  | use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow; | ||||||
|  |  | ||||||
| pub fn start(wait: OperationWait, exe_name: Option<&String>, exe_args: Option<Vec<&str>>, legacy_args: Option<&String>) -> Result<()> { | pub fn start(wait: OperationWait, exe_name: Option<&String>, exe_args: Option<Vec<&str>>, legacy_args: Option<&String>) -> Result<()> { | ||||||
|     if legacy_args.is_some() && exe_args.is_some() { |     if legacy_args.is_some() && exe_args.is_some() { | ||||||
| @@ -61,14 +64,17 @@ pub fn start(wait: OperationWait, exe_name: Option<&String>, exe_args: Option<Ve | |||||||
|  |  | ||||||
|     info!("About to launch: '{}' in dir '{}'", exe_to_execute.to_string_lossy(), current); |     info!("About to launch: '{}' in dir '{}'", exe_to_execute.to_string_lossy(), current); | ||||||
|  |  | ||||||
|     if let Some(args) = exe_args { |     let mut cmd = Process::new(&exe_to_execute); | ||||||
|         crate::windows::run_process(exe_to_execute, args, current)?; |     cmd.current_dir(¤t); | ||||||
|     } else if let Some(args) = legacy_args { |  | ||||||
|         crate::windows::run_process_raw_args(exe_to_execute, args, current)?; |  | ||||||
|     } else { |  | ||||||
|         crate::windows::run_process(exe_to_execute, vec![], current)?; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|  |     if let Some(args) = exe_args { | ||||||
|  |         cmd.args(args); | ||||||
|  |     } else if let Some(args) = legacy_args { | ||||||
|  |         cmd.raw_arg(args); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let cmd = cmd.spawn()?; | ||||||
|  |     let _ = unsafe { AllowSetForegroundWindow(cmd.id()) }; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -96,12 +102,12 @@ fn try_legacy_migration(root_dir: &PathBuf, app: &bundle::Manifest) -> Result<() | |||||||
|     let _ = remove_dir_all::remove_dir_all(root_dir.join("staging")); |     let _ = remove_dir_all::remove_dir_all(root_dir.join("staging")); | ||||||
|  |  | ||||||
|     info!("Removing old shortcuts..."); |     info!("Removing old shortcuts..."); | ||||||
|     if let Err(e) = windows::remove_all_shortcuts_for_root_dir(&root_dir) { |     if let Err(e) = win::remove_all_shortcuts_for_root_dir(&root_dir) { | ||||||
|         warn!("Failed to remove shortcuts ({}).", e); |         warn!("Failed to remove shortcuts ({}).", e); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     info!("Creating new default shortcuts..."); |     info!("Creating new default shortcuts..."); | ||||||
|     let _ = windows::create_default_lnks(&root_dir, &app); |     let _ = win::create_default_lnks(&root_dir, &app); | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,8 +2,6 @@ use crate::shared::{self, runtime_arch::RuntimeArch}; | |||||||
| use anyhow::{anyhow, Result}; | use anyhow::{anyhow, Result}; | ||||||
| use normpath::PathExt; | use normpath::PathExt; | ||||||
| use std::{ | use std::{ | ||||||
|     ffi::OsStr, |  | ||||||
|     io::Read, |  | ||||||
|     os::windows::process::CommandExt, |     os::windows::process::CommandExt, | ||||||
|     path::{Path, PathBuf}, |     path::{Path, PathBuf}, | ||||||
|     process::Command as Process, |     process::Command as Process, | ||||||
| @@ -20,20 +18,47 @@ use windows::{ | |||||||
| }; | }; | ||||||
| use winsafe::{self as w, co}; | use winsafe::{self as w, co}; | ||||||
|  |  | ||||||
| pub fn run_hook(app: &shared::bundle::Manifest, root_path: &PathBuf, hook_name: &str, timeout_secs: u64) { | pub fn run_hook(app: &shared::bundle::Manifest, root_path: &PathBuf, hook_name: &str, timeout_secs: u64) -> bool { | ||||||
|     let sw = simple_stopwatch::Stopwatch::start_new(); |     let sw = simple_stopwatch::Stopwatch::start_new(); | ||||||
|     let current_path = app.get_current_path(&root_path); |     let current_path = app.get_current_path(&root_path); | ||||||
|     let main_exe_path = app.get_main_exe_path(&root_path); |     let main_exe_path = app.get_main_exe_path(&root_path); | ||||||
|     info!("Running {} hook...", hook_name); |  | ||||||
|     let ver_string = app.version.to_string(); |     let ver_string = app.version.to_string(); | ||||||
|     let args = vec![hook_name, &ver_string]; |     let args = vec![hook_name, &ver_string]; | ||||||
|     if let Err(e) = run_process_no_console_and_wait(&main_exe_path, args, ¤t_path, Some(Duration::from_secs(timeout_secs))) { |     let mut success = false; | ||||||
|         warn!("Error running hook {}: {} (took {}ms)", hook_name, e, sw.ms()); |  | ||||||
|     } else { |     info!("Running {} hook...", hook_name); | ||||||
|         info!("Hook executed successfully (took {}ms)", sw.ms()); |     const CREATE_NO_WINDOW: u32 = 0x08000000; | ||||||
|  |     let cmd = Process::new(&main_exe_path).args(args).current_dir(¤t_path).creation_flags(CREATE_NO_WINDOW).spawn(); | ||||||
|  |  | ||||||
|  |     if let Err(e) = cmd { | ||||||
|  |         warn!("Failed to start hook {}: {}", hook_name, e); | ||||||
|  |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     let mut cmd = cmd.unwrap(); | ||||||
|  |     let _ = unsafe { AllowSetForegroundWindow(cmd.id()) }; | ||||||
|  |  | ||||||
|  |     match cmd.wait_timeout(Duration::from_secs(timeout_secs)) { | ||||||
|  |         Ok(Some(status)) => { | ||||||
|  |             if status.success() { | ||||||
|  |                 info!("Hook executed successfully (took {}ms)", sw.ms()); | ||||||
|  |                 success = true; | ||||||
|  |             } else { | ||||||
|  |                 warn!("Hook exited with non-zero exit code: {}", status.code().unwrap_or(0)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Ok(None) => { | ||||||
|  |             let _ = cmd.kill(); | ||||||
|  |             error!("Process timed out after {}s", timeout_secs); | ||||||
|  |         } | ||||||
|  |         Err(e) => { | ||||||
|  |             error!("Error waiting for process to finish: {}", e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // in case the hook left running processes |     // in case the hook left running processes | ||||||
|     let _ = shared::force_stop_package(&root_path); |     let _ = shared::force_stop_package(&root_path); | ||||||
|  |     success | ||||||
| } | } | ||||||
|  |  | ||||||
| pub struct MutexDropGuard { | pub struct MutexDropGuard { | ||||||
| @@ -86,7 +111,7 @@ pub fn is_sub_path<P1: AsRef<Path>, P2: AsRef<Path>>(path: P1, parent: P2) -> Re | |||||||
|     let parent = Path::new(&parent); |     let parent = Path::new(&parent); | ||||||
|  |  | ||||||
|     // we just bail if paths are not absolute. in the cases where we use this function, |     // we just bail if paths are not absolute. in the cases where we use this function, | ||||||
|     // we should have absolute paths from the file system (eg. iterating running processes, reading shortcuts) |     // we should have absolute paths from the file system (e.g. iterating running processes, reading shortcuts) | ||||||
|     // if we receive a relative path, it's likely coming from a shortcut target/working directory |     // if we receive a relative path, it's likely coming from a shortcut target/working directory | ||||||
|     // that we can't resolve with ExpandEnvironmentStrings |     // that we can't resolve with ExpandEnvironmentStrings | ||||||
|     if !path.is_absolute() || !parent.is_absolute() { |     if !path.is_absolute() || !parent.is_absolute() { | ||||||
| @@ -226,75 +251,6 @@ pub fn test_os_returns_true_for_everything_on_windows_11_and_below() { | |||||||
|     assert!(!is_os_version_or_greater("12").unwrap()); |     assert!(!is_os_version_or_greater("12").unwrap()); | ||||||
| } | } | ||||||
|  |  | ||||||
| const CREATE_NO_WINDOW: u32 = 0x08000000; |  | ||||||
| pub fn run_process_no_console_and_wait<S, P>(exe: S, args: Vec<&str>, work_dir: P, timeout: Option<Duration>) -> Result<String> |  | ||||||
| where |  | ||||||
|     S: AsRef<OsStr>, |  | ||||||
|     P: AsRef<Path>, |  | ||||||
| { |  | ||||||
|     let mut cmd = Process::new(exe) |  | ||||||
|         .args(args) |  | ||||||
|         .current_dir(work_dir) |  | ||||||
|         .stdout(std::process::Stdio::piped()) |  | ||||||
|         .stderr(std::process::Stdio::piped()) |  | ||||||
|         .creation_flags(CREATE_NO_WINDOW) |  | ||||||
|         .spawn()?; |  | ||||||
|  |  | ||||||
|     let _ = unsafe { AllowSetForegroundWindow(cmd.id()) }; |  | ||||||
|  |  | ||||||
|     fn check_process_status_and_output(status: std::process::ExitStatus, mut cmd: std::process::Child) -> Result<String> { |  | ||||||
|         let mut stdout = cmd.stdout.take().unwrap(); |  | ||||||
|         let mut stderr = cmd.stderr.take().unwrap(); |  | ||||||
|         let mut stdout_buf = Vec::new(); |  | ||||||
|         stdout.read_to_end(&mut stdout_buf)?; |  | ||||||
|         stderr.read_to_end(&mut stdout_buf)?; |  | ||||||
|  |  | ||||||
|         if !status.success() { |  | ||||||
|             warn!("Process exited with non-zero exit code: {}", status.code().unwrap_or(0)); |  | ||||||
|             if stdout_buf.len() > 0 { |  | ||||||
|                 warn!("    Output:\n{}", String::from_utf8_lossy(&stdout_buf)); |  | ||||||
|             } |  | ||||||
|             return Err(anyhow!("Process exited with non-zero exit code: {}", status.code().unwrap_or(0))); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Ok(String::from_utf8_lossy(&stdout_buf).to_string()) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if let Some(t) = timeout { |  | ||||||
|         match cmd.wait_timeout(t) { |  | ||||||
|             Ok(Some(status)) => check_process_status_and_output(status, cmd), |  | ||||||
|             Ok(None) => { |  | ||||||
|                 cmd.kill()?; |  | ||||||
|                 return Err(anyhow!("Process timed out after {:?}", t)); |  | ||||||
|             } |  | ||||||
|             Err(e) => return Err(e.into()), |  | ||||||
|         } |  | ||||||
|     } else { |  | ||||||
|         let status = cmd.wait()?; |  | ||||||
|         check_process_status_and_output(status, cmd) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn run_process<S, P>(exe: S, args: Vec<&str>, work_dir: P) -> Result<()> |  | ||||||
| where |  | ||||||
|     S: AsRef<OsStr>, |  | ||||||
|     P: AsRef<Path>, |  | ||||||
| { |  | ||||||
|     let cmd = Process::new(exe).args(args).current_dir(work_dir).spawn()?; |  | ||||||
|     let _ = unsafe { AllowSetForegroundWindow(cmd.id()) }; |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn run_process_raw_args<S, P>(exe: S, args: &str, work_dir: P) -> Result<()> |  | ||||||
| where |  | ||||||
|     S: AsRef<OsStr>, |  | ||||||
|     P: AsRef<Path>, |  | ||||||
| { |  | ||||||
|     let cmd = Process::new(exe).raw_arg(args).current_dir(work_dir).spawn()?; |  | ||||||
|     let _ = unsafe { AllowSetForegroundWindow(cmd.id()) }; |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> { | pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> { | ||||||
|     let machine = RuntimeArch::from_current_system(); |     let machine = RuntimeArch::from_current_system(); | ||||||
|     if machine.is_none() { |     if machine.is_none() { | ||||||
| @@ -319,7 +275,7 @@ pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> { | |||||||
|         // windows x64 only supports x86 and x64 |         // windows x64 only supports x86 and x64 | ||||||
|         Ok(architecture == RuntimeArch::X86 || architecture == RuntimeArch::X64) |         Ok(architecture == RuntimeArch::X86 || architecture == RuntimeArch::X64) | ||||||
|     } else if machine == RuntimeArch::Arm64 { |     } else if machine == RuntimeArch::Arm64 { | ||||||
|         // windows arm64 supports x86, and arm64, and only on windows 11 does it support x64 |         // windows arm64 supports x86, and arm64, and only on Windows 11 does it support x64 | ||||||
|         Ok(architecture == RuntimeArch::X86 || (architecture == RuntimeArch::X64 && is_win_11) || architecture == RuntimeArch::Arm64) |         Ok(architecture == RuntimeArch::X86 || (architecture == RuntimeArch::X64 && is_win_11) || architecture == RuntimeArch::Arm64) | ||||||
|     } else { |     } else { | ||||||
|         // we don't know what this is, so try installing anyway |         // we don't know what this is, so try installing anyway | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user