From f88989bd4bcd0eea614b7b1cb8bfab51e2c450b4 Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Wed, 27 Apr 2022 15:46:44 +0800
Subject: [PATCH] fix: change service installer and uninstaller

---
 scripts/check.mjs             | 22 +++++++----
 src-tauri/src/cmds.rs         | 12 +++---
 src-tauri/src/core/service.rs | 71 ++++++++++++++++++-----------------
 3 files changed, 57 insertions(+), 48 deletions(-)

diff --git a/scripts/check.mjs b/scripts/check.mjs
index 86ac7a0..b662cdd 100644
--- a/scripts/check.mjs
+++ b/scripts/check.mjs
@@ -143,20 +143,26 @@ async function resolveService() {
 
   if (platform !== "win32") return;
 
-  const url =
-    "https://github.com/zzzgydi/clash-verge-service/releases/download/latest/clash-verge-service.exe";
+  const resDir = path.join(cwd, "src-tauri/resources");
 
-  const binName = "clash-verge-service.exe";
+  const repo =
+    "https://github.com/zzzgydi/clash-verge-service/releases/download/latest";
 
-  const resDir = path.join(cwd, "src-tauri/resources");
-  const targetPath = path.join(resDir, binName);
+  async function help(bin) {
+    const targetPath = path.join(resDir, bin);
 
-  if (!FORCE && (await fs.pathExists(targetPath))) return;
+    if (!FORCE && (await fs.pathExists(targetPath))) return;
+
+    const url = `${repo}/${bin}`;
+    await downloadFile(url, targetPath);
+  }
 
   await fs.mkdirp(resDir);
-  await downloadFile(url, targetPath);
+  await help("clash-verge-service.exe");
+  await help("install-service.exe");
+  await help("uninstall-service.exe");
 
-  console.log(`[INFO]: resolve ${binName} finished`);
+  console.log(`[INFO]: resolve Service finished`);
 }
 
 /**
diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
index 2a42c6b..bb65a19 100644
--- a/src-tauri/src/cmds.rs
+++ b/src-tauri/src/cmds.rs
@@ -258,20 +258,20 @@ pub mod service {
 
   #[tauri::command]
   pub async fn check_service() -> Result<JsonResponse, String> {
-    wrap_err!(crate::core::Service::check_service().await)
+    // no log
+    match crate::core::Service::check_service().await {
+      Ok(res) => Ok(res),
+      Err(err) => Err(err.to_string()),
+    }
   }
 
   #[tauri::command]
   pub async fn install_service() -> Result<(), String> {
-    log_if_err!(crate::core::Service::install_service().await);
-    let ret = wrap_err!(crate::core::Service::start_service().await);
-    log::info!("clash verge service started successfully");
-    ret
+    wrap_err!(crate::core::Service::install_service().await)
   }
 
   #[tauri::command]
   pub async fn uninstall_service() -> Result<(), String> {
-    log_if_err!(crate::core::Service::stop_service().await);
     wrap_err!(crate::core::Service::uninstall_service().await)
   }
 }
diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs
index 62b10ef..7f31390 100644
--- a/src-tauri/src/core/service.rs
+++ b/src-tauri/src/core/service.rs
@@ -191,8 +191,9 @@ pub mod win_service {
   use super::*;
   use anyhow::Context;
   use deelevate::{PrivilegeLevel, Token};
-  use runas::Command as RunasCommond;
-  use std::{env::current_exe, process::Command as StdCommond};
+  use runas::Command as RunasCommand;
+  use std::os::windows::process::CommandExt;
+  use std::{env::current_exe, process::Command as StdCommand};
 
   const SERVICE_NAME: &str = "clash_verge_service";
 
@@ -217,61 +218,63 @@ pub mod win_service {
     /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
     pub async fn install_service() -> Result<()> {
       let binary_path = dirs::service_path();
-      let arg = format!("binpath={}", binary_path.as_os_str().to_string_lossy());
+      let install_path = binary_path.with_file_name("install-service.exe");
+
+      if !install_path.exists() {
+        bail!("installer exe not found");
+      }
 
       let token = Token::with_current_process()?;
       let level = token.privilege_level()?;
 
-      let args = [
-        "create",
-        SERVICE_NAME,
-        arg.as_str(),
-        "type=own",
-        "start=AUTO",
-        "displayname=Clash Verge Service",
-      ];
-
       let status = match level {
-        PrivilegeLevel::NotPrivileged => RunasCommond::new("sc").args(&args).status()?,
-        _ => StdCommond::new("sc").args(&args).status()?,
+        PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?,
+        _ => StdCommand::new(install_path)
+          .creation_flags(0x08000000)
+          .status()?,
       };
 
-      if status.success() {
-        return Ok(());
-      }
-
-      if status.code() == Some(1073i32) {
-        bail!("clash verge service is installed");
+      if !status.success() {
+        bail!(
+          "failed to install service with status {}",
+          status.code().unwrap()
+        );
       }
 
-      bail!(
-        "failed to install service with status {}",
-        status.code().unwrap()
-      )
+      Ok(())
     }
 
     /// Uninstall the Clash Verge Service
     /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
     pub async fn uninstall_service() -> Result<()> {
+      let binary_path = dirs::service_path();
+      let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
+
+      if !uninstall_path.exists() {
+        bail!("uninstaller exe not found");
+      }
+
       let token = Token::with_current_process()?;
       let level = token.privilege_level()?;
 
-      let args = ["delete", SERVICE_NAME];
-
       let status = match level {
-        PrivilegeLevel::NotPrivileged => RunasCommond::new("sc").args(&args).status()?,
-        _ => StdCommond::new("sc").args(&args).status()?,
+        PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?,
+        _ => StdCommand::new(uninstall_path)
+          .creation_flags(0x08000000)
+          .status()?,
       };
 
-      match status.success() {
-        true => Ok(()),
-        false => bail!(
+      if !status.success() {
+        bail!(
           "failed to uninstall service with status {}",
           status.code().unwrap()
-        ),
+        );
       }
+
+      Ok(())
     }
 
+    /// [deprecated]
     /// start service
     /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
     pub async fn start_service() -> Result<()> {
@@ -281,8 +284,8 @@ pub mod win_service {
       let args = ["start", SERVICE_NAME];
 
       let status = match level {
-        PrivilegeLevel::NotPrivileged => RunasCommond::new("sc").args(&args).status()?,
-        _ => StdCommond::new("sc").args(&args).status()?,
+        PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?,
+        _ => StdCommand::new("sc").args(&args).status()?,
       };
 
       match status.success() {
-- 
GitLab