Browse Source

重启电脑以后,之前的采集程序就打不开了

master
han\hanst 3 weeks ago
parent
commit
8b06b62684
  1. 137
      client-file-collector/client_file_collector.py
  2. BIN
      client-file-collector/dist/QMSFileCollector.exe

137
client-file-collector/client_file_collector.py

@ -4,6 +4,7 @@ import json
import os import os
import queue import queue
import shutil import shutil
import socket
import sys import sys
import threading import threading
import time import time
@ -16,6 +17,39 @@ import requests
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry from urllib3.util.retry import Retry
CONTROL_HOST = "127.0.0.1"
CONTROL_PORT = 52347
SINGLE_INSTANCE_MUTEX_NAME = "Global\\QMSFileCollector_SingleInstance"
ERROR_ALREADY_EXISTS = 183
def acquire_single_instance_lock():
if os.name != "nt":
return None, True
try:
import ctypes
kernel32 = ctypes.windll.kernel32
handle = kernel32.CreateMutexW(None, False, SINGLE_INSTANCE_MUTEX_NAME)
if not handle:
return None, True
if kernel32.GetLastError() == ERROR_ALREADY_EXISTS:
kernel32.CloseHandle(handle)
return None, False
return handle, True
except Exception:
return None, True
def notify_existing_instance_show(retries=8, sleep_seconds=0.25):
for _ in range(max(1, retries)):
try:
with socket.create_connection((CONTROL_HOST, CONTROL_PORT), timeout=1.0) as conn:
conn.sendall(b"SHOW")
return True
except Exception:
time.sleep(sleep_seconds)
return False
def get_runtime_dir(): def get_runtime_dir():
if getattr(sys, "frozen", False): if getattr(sys, "frozen", False):
@ -51,16 +85,7 @@ def _ensure_windows_run_registry(entry_name, exe_path):
return False, "写入系统自启动项失败: %s" % e return False, "写入系统自启动项失败: %s" % e
def _cleanup_legacy_startup_items(legacy_entry_name, app_data):
if app_data:
startup_dir = Path(app_data) / "Microsoft" / "Windows" / "Start Menu" / "Programs" / "Startup"
legacy_script = startup_dir / (legacy_entry_name + ".vbs")
try:
if legacy_script.exists():
legacy_script.unlink()
except Exception:
pass
def _remove_windows_run_entry(entry_name):
try: try:
import winreg import winreg
run_key_path = r"Software\Microsoft\Windows\CurrentVersion\Run" run_key_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
@ -71,13 +96,26 @@ def _cleanup_legacy_startup_items(legacy_entry_name, app_data):
winreg.KEY_SET_VALUE winreg.KEY_SET_VALUE
) as key: ) as key:
try: try:
winreg.DeleteValue(key, legacy_entry_name)
winreg.DeleteValue(key, entry_name)
except FileNotFoundError: except FileNotFoundError:
pass pass
except Exception: except Exception:
pass pass
def _cleanup_legacy_startup_items(legacy_entry_name, app_data):
if app_data:
startup_dir = Path(app_data) / "Microsoft" / "Windows" / "Start Menu" / "Programs" / "Startup"
legacy_script = startup_dir / (legacy_entry_name + ".vbs")
try:
if legacy_script.exists():
legacy_script.unlink()
except Exception:
pass
_remove_windows_run_entry(legacy_entry_name)
def ensure_windows_startup(): def ensure_windows_startup():
if os.name != "nt": if os.name != "nt":
return False, "当前系统非Windows,跳过自启动注册。" return False, "当前系统非Windows,跳过自启动注册。"
@ -95,7 +133,7 @@ def ensure_windows_startup():
startup_script = startup_dir / (entry_name + ".vbs") startup_script = startup_dir / (entry_name + ".vbs")
script_content = ( script_content = (
"Set WshShell = CreateObject(\"WScript.Shell\")\n" "Set WshShell = CreateObject(\"WScript.Shell\")\n"
"WshShell.Run Chr(34) & \"%s\" & Chr(34), 0\n"
"WshShell.Run Chr(34) & \"%s\" & Chr(34), 7\n"
"Set WshShell = Nothing\n" "Set WshShell = Nothing\n"
) % exe_path.replace("\"", "\"\"") ) % exe_path.replace("\"", "\"\"")
@ -109,10 +147,16 @@ def ensure_windows_startup():
return False, "启动文件夹自启动项已存在,无需重复写入。" return False, "启动文件夹自启动项已存在,无需重复写入。"
startup_script.write_text(script_content, encoding="utf-8") startup_script.write_text(script_content, encoding="utf-8")
_remove_windows_run_entry(entry_name)
if old_content: if old_content:
return True, "检测到启动文件夹自启动路径变化,已自动更新。" return True, "检测到启动文件夹自启动路径变化,已自动更新。"
return True, "首次启动已写入启动文件夹自启动项,下次开机会自动运行。" return True, "首次启动已写入启动文件夹自启动项,下次开机会自动运行。"
except Exception as startup_error: except Exception as startup_error:
try:
if startup_script.exists():
startup_script.unlink()
except Exception:
pass
try: try:
changed, reg_msg = _ensure_windows_run_registry(entry_name, exe_path) changed, reg_msg = _ensure_windows_run_registry(entry_name, exe_path)
return changed, "写入启动文件夹失败,已回退注册表方式: %s" % reg_msg return changed, "写入启动文件夹失败,已回退注册表方式: %s" % reg_msg
@ -142,6 +186,7 @@ class CollectorApp:
self.notice_ts = {} self.notice_ts = {}
self.processing_lock = threading.Lock() self.processing_lock = threading.Lock()
self.processing_files = set() self.processing_files = set()
self.control_stop_event = threading.Event()
self.http_session = self._create_http_session() self.http_session = self._create_http_session()
self.base_url_var = StringVar() self.base_url_var = StringVar()
@ -159,11 +204,66 @@ class CollectorApp:
self._build_ui() self._build_ui()
self.root.protocol("WM_DELETE_WINDOW", self._on_window_close) self.root.protocol("WM_DELETE_WINDOW", self._on_window_close)
self._start_control_server()
self._init_windows_startup() self._init_windows_startup()
self._load_config() self._load_config()
self.root.after(200, self._flush_logs) self.root.after(200, self._flush_logs)
self.root.after(3000, self._watch_worker) self.root.after(3000, self._watch_worker)
def _start_control_server(self):
self.control_thread = threading.Thread(target=self._control_server_loop, daemon=True)
self.control_thread.start()
def _control_server_loop(self):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
server.bind((CONTROL_HOST, CONTROL_PORT))
server.listen(1)
server.settimeout(1.0)
except Exception:
try:
server.close()
except Exception:
pass
return
while not self.control_stop_event.is_set():
try:
conn, _ = server.accept()
except socket.timeout:
continue
except Exception:
break
try:
payload = conn.recv(16)
if b"SHOW" in payload:
self.root.after(0, self._show_main_window)
except Exception:
pass
finally:
try:
conn.close()
except Exception:
pass
try:
server.close()
except Exception:
pass
def _show_main_window(self):
try:
self.root.deiconify()
self.root.state("normal")
self.root.lift()
self.root.attributes("-topmost", True)
self.root.after(300, lambda: self.root.attributes("-topmost", False))
self.root.focus_force()
except Exception:
pass
def _create_http_session(self): def _create_http_session(self):
retry = Retry( retry = Retry(
total=3, total=3,
@ -449,6 +549,7 @@ class CollectorApp:
def exit_app(self): def exit_app(self):
self.running = False self.running = False
self.stop_event.set() self.stop_event.set()
self.control_stop_event.set()
try: try:
self.http_session.close() self.http_session.close()
except Exception: except Exception:
@ -689,6 +790,18 @@ class CollectorApp:
if __name__ == "__main__": if __name__ == "__main__":
instance_lock_handle, is_primary = acquire_single_instance_lock()
if not is_primary:
notify_existing_instance_show()
sys.exit(0)
app_root = Tk() app_root = Tk()
app = CollectorApp(app_root) app = CollectorApp(app_root)
app_root.mainloop() app_root.mainloop()
if instance_lock_handle and os.name == "nt":
try:
import ctypes
ctypes.windll.kernel32.CloseHandle(instance_lock_handle)
except Exception:
pass

BIN
client-file-collector/dist/QMSFileCollector.exe

Loading…
Cancel
Save