diff --git a/ant/windows/windows-launcher.nsi.in b/ant/windows/windows-launcher.nsi.in index 2d94297e7..20987601d 100644 --- a/ant/windows/windows-launcher.nsi.in +++ b/ant/windows/windows-launcher.nsi.in @@ -66,7 +66,14 @@ Section StrCpy $command '"$R2" /s /k "$command"' ${EndIf} - Exec $command + ; Check for "--wait" parameter + ${StrLoc} $R3 "$params" "--wait" "<" + ${If} $R3 != "" + ExecWait $command + ${Else} + Exec $command + ${EndIf} + ${If} ${RunningX64} ${EnableX64FSRedirection} ${EndIf} diff --git a/src/qz/installer/Installer.java b/src/qz/installer/Installer.java index a51350d50..d3abe3533 100644 --- a/src/qz/installer/Installer.java +++ b/src/qz/installer/Installer.java @@ -95,11 +95,11 @@ public static boolean preinstall() { public static void install() throws Exception { getInstance(); log.info("Installing to {}", instance.getDestination()); - instance.removeLibs() + instance.removeServiceRegistration() + .removeLibs() .deployApp() .removeLegacyStartup() .removeLegacyFiles() - .removeServiceRegistration() .addSharedDirectory() .addAppLauncher() .addStartupEntry() diff --git a/src/qz/installer/WindowsInstaller.java b/src/qz/installer/WindowsInstaller.java index b93775fc4..88a28d8f2 100644 --- a/src/qz/installer/WindowsInstaller.java +++ b/src/qz/installer/WindowsInstaller.java @@ -206,22 +206,28 @@ public void spawn(List args) throws Exception { @Override public Installer addServiceRegistration(String user) { + log.warn("Registering system service: {}", PROPS_FILE); if(!SystemUtilities.isAdmin()) { throw new UnsupportedOperationException("Installing a service requires elevation"); } + if(WindowsUtilities.serviceExists(PROPS_FILE)) { + log.warn("System service is already registered, removing."); + removeServiceRegistration(); + } + Path nssm = SystemUtilities.getJarParentPath().resolve("utils/nssm.exe"); Path qz = SystemUtilities.getJarParentPath().resolve(PROPS_FILE + ".exe"); - String servicePath = String.format("\"" + qz.toString() + "\" %s %s %s", - ArgValue.WAIT.getMatches()[0], - ArgValue.STEAL.getMatches()[0], - ArgValue.HEADLESS.getMatches()[0]); // Install the service - if(ShellUtilities.execute(nssm.toString(), "install", PROPS_FILE, servicePath)) { - ShellUtilities.execute(nssm.toString(), "set", "DisplayName", ABOUT_TITLE); - ShellUtilities.execute(nssm.toString(), "set", "Description", ABOUT_DESCRIPTION); - ShellUtilities.execute(nssm.toString(), "set", "DependOnService", "Spooler"); + if(ShellUtilities.execute(nssm.toString(), "install", PROPS_FILE, + qz.toString(), + ArgValue.WAIT.getMatches()[0], + ArgValue.STEAL.getMatches()[0], + ArgValue.HEADLESS.getMatches()[0])) { + ShellUtilities.execute(nssm.toString(), "set", PROPS_FILE, "DisplayName", ABOUT_TITLE); + ShellUtilities.execute(nssm.toString(), "set", PROPS_FILE, "Description", ABOUT_DESCRIPTION); + ShellUtilities.execute(nssm.toString(), "set", PROPS_FILE, "DependOnService", "Spooler"); log.info("Successfully registered system service: {}", PROPS_FILE); if(user != null && !user.trim().isEmpty()) { log.info("Setting service to run as {}", user); @@ -233,7 +239,9 @@ public Installer addServiceRegistration(String user) { TaskKiller.killAll(); // Instruct autostart to be ignored FileUtilities.disableGlobalAutoStart(); + log.info("Starting system service: {}", PROPS_FILE); if(WindowsUtilities.startService(PROPS_FILE)) { + log.info("System system service started successfully.", PROPS_FILE); return this; } } @@ -242,20 +250,25 @@ public Installer addServiceRegistration(String user) { @Override public Installer removeServiceRegistration() { + log.info("Removing system service: {}", PROPS_FILE); if(!SystemUtilities.isAdmin()) { throw new UnsupportedOperationException("Removing a service requires elevation"); } - WindowsUtilities.stopService(PROPS_FILE); - Path nssm = SystemUtilities.getJarParentPath().resolve("utils/nssm.exe"); - if(ShellUtilities.execute(nssm.toString(), "remove", PROPS_FILE, "confirm")) { - // Old tutorials used "QZ Tray" as the service name - ShellUtilities.execute(nssm.toString(), "remove", ABOUT_TITLE, "confirm"); - // Restore default autostart settings by deleting the preference file - FileUtils.deleteQuietly(FileUtilities.SHARED_DIR.resolve(AUTOSTART_FILE).toFile()); - log.info("System service successfully removed: {}", PROPS_FILE); + if(WindowsUtilities.serviceExists(PROPS_FILE)) { + WindowsUtilities.stopService(PROPS_FILE); + Path nssm = SystemUtilities.getJarParentPath().resolve("utils/nssm.exe"); + if(ShellUtilities.execute(nssm.toString(), "remove", PROPS_FILE, "confirm")) { + // Old tutorials used "QZ Tray" as the service name + ShellUtilities.execute(nssm.toString(), "remove", ABOUT_TITLE, "confirm"); + // Restore default autostart settings by deleting the preference file + FileUtils.deleteQuietly(FileUtilities.SHARED_DIR.resolve(AUTOSTART_FILE).toFile()); + log.info("System service successfully removed: {}", PROPS_FILE); + } else { + log.error("An error occurred removing system service: {}", PROPS_FILE); + } } else { - log.error("An error occurred removing system service: {}, please try to remove manually using 'sc ", PROPS_FILE); + log.info("System service was not found, skipping."); } return this; diff --git a/src/qz/utils/ArgParser.java b/src/qz/utils/ArgParser.java index e4f5e8d44..217e03097 100644 --- a/src/qz/utils/ArgParser.java +++ b/src/qz/utils/ArgParser.java @@ -183,7 +183,11 @@ public ExitStatus processInstallerArgs(ArgValue argValue, List args) { Installer.uninstall(); return SUCCESS; case SERVICE: - Installer.getInstance().addServiceRegistration(valueOf(RUNAS)); + if(hasFlag(REMOVE)) { + Installer.getInstance().removeServiceRegistration(); + } else { + Installer.getInstance().addServiceRegistration(valueOf(RUNAS)); + } return SUCCESS; case SPAWN: args.remove(0); // first argument is "spawn", remove it diff --git a/src/qz/utils/ArgValue.java b/src/qz/utils/ArgValue.java index d0b334e91..8f6e6ee7e 100644 --- a/src/qz/utils/ArgValue.java +++ b/src/qz/utils/ArgValue.java @@ -130,7 +130,9 @@ public enum ArgValueOption { // service RUNAS(ArgValue.SERVICE, "Username to run the system service as (Windows only)", - "--runas", "--user", "-u"); + "--runas", "--user", "-u"), + REMOVE(ArgValue.SERVICE, "Remove the system service as (Windows only)", + "--remove", "-r"); ArgValue parent; String description; diff --git a/src/qz/utils/FileUtilities.java b/src/qz/utils/FileUtilities.java index fda921e10..de55b9042 100644 --- a/src/qz/utils/FileUtilities.java +++ b/src/qz/utils/FileUtilities.java @@ -164,7 +164,7 @@ public static Path inheritParentPermissions(Path filePath) { public static boolean disableGlobalAutoStart() { Path autostart = SHARED_DIR.resolve(AUTOSTART_FILE); try { - Files.write(autostart, "0".getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING); + Files.write(autostart, "0".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE_NEW, StandardOpenOption.TRUNCATE_EXISTING); return true; } catch(IOException e) { diff --git a/src/qz/utils/WindowsUtilities.java b/src/qz/utils/WindowsUtilities.java index 83bb3358b..b1474f0a5 100644 --- a/src/qz/utils/WindowsUtilities.java +++ b/src/qz/utils/WindowsUtilities.java @@ -345,6 +345,18 @@ public static boolean isWow64() { return isWow64; } + public static boolean serviceExists(String serviceName) { + try { + W32ServiceManager serviceManager = new W32ServiceManager(); + serviceManager.open(Winsvc.SC_MANAGER_ALL_ACCESS); + W32Service service = serviceManager.openService(serviceName, Winsvc.SC_MANAGER_ALL_ACCESS); + return true; + } catch(Win32Exception e) { + return false; + } catch(Throwable t) {} + return ShellUtilities.execute("sc", "query", serviceName); + } + public static boolean stopService(String serviceName) { try { W32ServiceManager serviceManager = new W32ServiceManager();