diff --git a/package.json b/package.json index 7c89a3f..1c4b794 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fieldlens/print", - "version": "0.1.26", + "version": "0.1.27", "private": true, "type": "module", "scripts": { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6ba90f3..f059ffd 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fieldlens-print" -version = "0.1.26" +version = "0.1.27" description = "FieldLens print-stream barcode capture companion" authors = ["Arrowstack"] edition = "2021" diff --git a/src-tauri/src/printers.rs b/src-tauri/src/printers.rs index b271f6b..ea08358 100644 --- a/src-tauri/src/printers.rs +++ b/src-tauri/src/printers.rs @@ -407,21 +407,51 @@ if ($null -ne $targetConfig -and $targetConfig.PrintTicketXML) {{ fs::write(&script_path, script).context("write Windows printer setup script")?; + // First run the script directly. This is the reliable path when FieldLens + // itself was started as administrator and avoids a second hidden RunAs + // process plus fragile nested PowerShell argument quoting. + let direct = hidden_windows_command("powershell.exe") + .args([ + "-NoProfile", + "-NonInteractive", + "-ExecutionPolicy", + "Bypass", + "-File", + ]) + .arg(&script_path) + .args(["-TargetPrinter", target_printer]) + .output() + .context("run Windows printer setup")?; + + if direct.status.success() { + let _ = fs::remove_file(&script_path); + return Ok(()); + } + + // If the app is not elevated, retry through UAC. Keep this as a fallback + // rather than the primary path so admin-launched builds never depend on a + // nested hidden RunAs process. let path = ps_quote(&script_path.to_string_lossy()); let elevated = format!( - "$p = Start-Process -FilePath powershell.exe -Verb RunAs -WindowStyle Hidden -Wait -PassThru -ArgumentList @('-NoProfile','-NonInteractive','-WindowStyle','Hidden','-ExecutionPolicy','Bypass','-File','{path}','-TargetPrinter','{target}'); exit $p.ExitCode" + "$p = Start-Process -FilePath powershell.exe -Verb RunAs -Wait -PassThru -ArgumentList @('-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File','{path}','-TargetPrinter','{target}'); exit $p.ExitCode" ); - let status = hidden_windows_command("powershell.exe") - .args(["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", &elevated]) + let elevated_status = hidden_windows_command("powershell.exe") + .args(["-NoProfile", "-NonInteractive", "-Command", &elevated]) .status() .context("launch elevated Windows printer setup")?; let _ = fs::remove_file(&script_path); - if !status.success() { + if !elevated_status.success() { + let stderr = String::from_utf8_lossy(&direct.stderr).trim().to_string(); return Err(anyhow!( - "printer setup was cancelled or failed; Windows administrator approval may be required" + "Windows printer setup failed{}", + if stderr.is_empty() { + "; administrator approval may be required".to_string() + } else { + format!(": {stderr}") + } )); } @@ -450,17 +480,42 @@ if (Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue) {{ uuid::Uuid::new_v4() )); fs::write(&script_path, script)?; + let direct = hidden_windows_command("powershell.exe") + .args([ + "-NoProfile", + "-NonInteractive", + "-ExecutionPolicy", + "Bypass", + "-File", + ]) + .arg(&script_path) + .output() + .context("run Windows printer removal")?; + + if direct.status.success() { + let _ = fs::remove_file(&script_path); + return Ok(()); + } + let path = ps_quote(&script_path.to_string_lossy()); let elevated = format!( - "$p = Start-Process -FilePath powershell.exe -Verb RunAs -WindowStyle Hidden -Wait -PassThru -ArgumentList @('-NoProfile','-NonInteractive','-WindowStyle','Hidden','-ExecutionPolicy','Bypass','-File','{path}'); exit $p.ExitCode" + "$p = Start-Process -FilePath powershell.exe -Verb RunAs -Wait -PassThru -ArgumentList @('-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File','{path}'); exit $p.ExitCode" ); let status = hidden_windows_command("powershell.exe") - .args(["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", &elevated]) + .args(["-NoProfile", "-NonInteractive", "-Command", &elevated]) .status()?; let _ = fs::remove_file(&script_path); if !status.success() { - return Err(anyhow!("removing FieldLens Print was cancelled or failed")); + let stderr = String::from_utf8_lossy(&direct.stderr).trim(); + return Err(anyhow!( + "removing FieldLens Print failed{}", + if stderr.is_empty() { + String::new() + } else { + format!(": {stderr}") + } + )); } Ok(()) } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index e3e3a90..db11405 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "FieldLens Print", - "version": "0.1.26", + "version": "0.1.27", "identifier": "dev.arrowstack.fieldlens.print", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/main.ts b/src/main.ts index f2e0167..8c5d1d8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -142,7 +142,7 @@ let config: AppConfig let jobs: Job[] = [] let status: Status = { running: false, mode: 'raw-proxy', pending: 0, jobs: 0 } let loginTimer: number | undefined -let currentVersion = '0.1.25' +let currentVersion = '0.1.27' let bundleType: BundleType | null = null let availableUpdate: Update | null = null let availableManualUpdate: ManualUpdate | null = null