add version
windows-exe-build / build (push) Successful in 38s

This commit is contained in:
2026-03-18 11:08:20 +09:00
parent 3e29bc197b
commit b4065f6ce2
+113 -10
View File
@@ -5,6 +5,8 @@ import json
import time
import traceback
import subprocess
import ctypes
from ctypes import wintypes
from pathlib import Path
from logging.handlers import RotatingFileHandler
import logging
@@ -14,6 +16,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets
APP_NAME = "Watchdog GUI"
APP_VERSION = "1.1.0"
BASE_DIR = Path(__file__).resolve().parent
CONFIG_PATH = BASE_DIR / "watchdog_config.json"
LOG_DIR = BASE_DIR / "watchdog_logs"
@@ -113,6 +117,68 @@ def save_config(cfg: dict):
LOGGER.exception("설정 파일 저장 실패")
# =========================
# 버전 유틸
# =========================
def get_file_version(file_path: str) -> str:
"""
Windows EXE/DLL 파일 버전 읽기
실패 시 '알 수 없음' 반환
"""
try:
if not file_path or not os.path.exists(file_path):
return "알 수 없음"
size = ctypes.windll.version.GetFileVersionInfoSizeW(file_path, None)
if not size:
return "알 수 없음"
res = ctypes.create_string_buffer(size)
ctypes.windll.version.GetFileVersionInfoW(file_path, 0, size, res)
u_len = wintypes.UINT()
u_ptr = ctypes.c_void_p()
ok = ctypes.windll.version.VerQueryValueW(
res,
"\\",
ctypes.byref(u_ptr),
ctypes.byref(u_len)
)
if not ok:
return "알 수 없음"
class VS_FIXEDFILEINFO(ctypes.Structure):
_fields_ = [
("dwSignature", wintypes.DWORD),
("dwStrucVersion", wintypes.DWORD),
("dwFileVersionMS", wintypes.DWORD),
("dwFileVersionLS", wintypes.DWORD),
("dwProductVersionMS", wintypes.DWORD),
("dwProductVersionLS", wintypes.DWORD),
("dwFileFlagsMask", wintypes.DWORD),
("dwFileFlags", wintypes.DWORD),
("dwFileOS", wintypes.DWORD),
("dwFileType", wintypes.DWORD),
("dwFileSubtype", wintypes.DWORD),
("dwFileDateMS", wintypes.DWORD),
("dwFileDateLS", wintypes.DWORD),
]
ffi = ctypes.cast(u_ptr, ctypes.POINTER(VS_FIXEDFILEINFO)).contents
major = (ffi.dwFileVersionMS >> 16) & 0xFFFF
minor = ffi.dwFileVersionMS & 0xFFFF
build = (ffi.dwFileVersionLS >> 16) & 0xFFFF
revision = ffi.dwFileVersionLS & 0xFFFF
return f"{major}.{minor}.{build}.{revision}"
except Exception:
LOGGER.exception("파일 버전 읽기 실패")
return "알 수 없음"
# =========================
# 감시 유틸
# =========================
@@ -184,9 +250,9 @@ def start_target_program(target_exe_path: str) -> bool:
class WatchdogWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle(APP_NAME)
self.resize(520, 250)
self.setMinimumSize(500, 230)
self.setWindowTitle(f"{APP_NAME} v{APP_VERSION}")
self.resize(560, 300)
self.setMinimumSize(520, 270)
self.cfg = load_config()
self.monitoring = False
@@ -204,7 +270,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.timer.timeout.connect(self.check_target_once)
LOGGER.info("=" * 60)
LOGGER.info("Watchdog GUI 시작")
LOGGER.info(f"{APP_NAME} 시작 - 버전 {APP_VERSION}")
LOGGER.info(f"로그 파일: {LOG_FILE}")
LOGGER.info("=" * 60)
@@ -212,6 +278,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.start_monitoring()
self.update_target_status_label()
self.update_version_labels()
if self.cfg.get("start_minimized_to_tray", False):
QtCore.QTimer.singleShot(300, self.hide_to_tray)
@@ -227,6 +294,20 @@ class WatchdogWindow(QtWidgets.QMainWindow):
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(8)
# 버전 표시
version_layout = QtWidgets.QGridLayout()
version_layout.setHorizontalSpacing(8)
version_layout.setVerticalSpacing(4)
self.lbl_watchdog_version = QtWidgets.QLabel(f"Watchdog 버전: {APP_VERSION}")
self.lbl_watchdog_version.setStyleSheet("font-weight: bold; color: #1f1f1f;")
self.lbl_target_version = QtWidgets.QLabel("대상 버전: 알 수 없음")
self.lbl_target_version.setStyleSheet("font-weight: bold; color: #1f1f1f;")
version_layout.addWidget(self.lbl_watchdog_version, 0, 0)
version_layout.addWidget(self.lbl_target_version, 0, 1)
# 경로 행
path_layout = QtWidgets.QHBoxLayout()
path_layout.setSpacing(6)
@@ -320,6 +401,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
log_layout.addWidget(self.text_log)
log_layout.addWidget(self.btn_clear_log)
main_layout.addLayout(version_layout)
main_layout.addLayout(path_layout)
main_layout.addLayout(option_layout)
main_layout.addLayout(top_btn_layout)
@@ -334,7 +416,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.btn_open_log_file.clicked.connect(self.open_log_file)
self.btn_clear_log.clicked.connect(self.text_log.clear)
self.edit_exe_path.editingFinished.connect(self.save_ui_to_config)
self.edit_exe_path.editingFinished.connect(self.on_path_edited)
self.spin_interval.valueChanged.connect(self.save_ui_to_config)
self.spin_cooldown.valueChanged.connect(self.save_ui_to_config)
self.chk_auto_start.stateChanged.connect(self.save_ui_to_config)
@@ -361,9 +443,9 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.btn_toggle_log.setText("로그 숨기기" if show_log else "로그 보기")
if show_log:
self.resize(560, 500)
self.resize(600, 540)
else:
self.resize(520, 250)
self.resize(560, 300)
def save_ui_to_config(self):
self.cfg["target_exe_path"] = self.edit_exe_path.text().strip()
@@ -375,6 +457,17 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.cfg["show_log_panel"] = bool(self.log_group.isVisible())
save_config(self.cfg)
def on_path_edited(self):
self.save_ui_to_config()
self.update_target_status_label()
self.update_version_labels()
def update_version_labels(self):
exe_path = self.edit_exe_path.text().strip()
target_ver = get_file_version(exe_path)
self.lbl_watchdog_version.setText(f"Watchdog 버전: {APP_VERSION}")
self.lbl_target_version.setText(f"대상 버전: {target_ver}")
# ---------------------
# 트레이
# ---------------------
@@ -444,6 +537,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
if path:
self.edit_exe_path.setText(path)
self.save_ui_to_config()
self.update_version_labels()
LOGGER.info(f"감시 대상 선택: {path}")
self.update_target_status_label()
@@ -453,9 +547,9 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.btn_toggle_log.setText("로그 숨기기" if visible else "로그 보기")
if visible:
self.resize(max(self.width(), 560), 500)
self.resize(max(self.width(), 600), 540)
else:
self.resize(max(520, self.width()), 250)
self.resize(max(560, self.width()), 300)
self.save_ui_to_config()
@@ -493,6 +587,8 @@ class WatchdogWindow(QtWidgets.QMainWindow):
self.lbl_monitor.setText("감시 상태: 실행 중")
self.lbl_monitor.setStyleSheet("font-weight: bold; color: #007a1f;")
self.update_version_labels()
LOGGER.info(f"감시 시작: {exe_path}")
LOGGER.info(f"체크 주기: {self.spin_interval.value()}초 / 쿨다운: {self.spin_cooldown.value()}")
@@ -532,16 +628,19 @@ class WatchdogWindow(QtWidgets.QMainWindow):
if not exe_path:
self.update_target_status_label(False)
self.update_version_labels()
LOGGER.warning("감시 대상 경로가 비어 있습니다.")
return
if not os.path.exists(exe_path):
self.update_target_status_label(False)
self.update_version_labels()
LOGGER.error(f"감시 대상 파일이 존재하지 않습니다: {exe_path}")
return
running = is_process_running_by_path(exe_path)
self.update_target_status_label(running)
self.update_version_labels()
if running:
return
@@ -566,7 +665,7 @@ class WatchdogWindow(QtWidgets.QMainWindow):
QtWidgets.QSystemTrayIcon.Warning,
2500
)
QtCore.QTimer.singleShot(1500, self.update_target_status_label)
QtCore.QTimer.singleShot(1500, self._refresh_after_restart)
else:
self.update_target_status_label(False)
@@ -574,6 +673,10 @@ class WatchdogWindow(QtWidgets.QMainWindow):
LOGGER.error("감시 루프 오류 발생")
LOGGER.error(traceback.format_exc())
def _refresh_after_restart(self):
self.update_target_status_label()
self.update_version_labels()
# ---------------------
# 종료 / 최소화
# ---------------------