Skip to content

Commit 8677025

Browse files
committed
完善UIA置顶_进行打包
1 parent aaa5937 commit 8677025

7 files changed

Lines changed: 156 additions & 20 deletions

File tree

CHANGELOG/v2.3.0-beta.1/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ v2.3 - Shirako (砂狼白子) beta 1
1010
## 💡 功能优化
1111

1212
- 优化 **UIA置顶**,统一封装DLL调用
13-
- 优化 **UIA置顶**,切换后询问重启
13+
- 优化 **UIA置顶**,切换后支持提权重启
14+
- 优化 **UIA置顶**,提权后主界面显示再重启
1415

1516
## 🐛 修复问题
1617

17-
-
18+
- 修复 **UIA置顶**,提权后连重导致退出
1819

1920
## 🔧 其它变更
2021

app/Language/modules/sidebar_tray_management.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "UIA置顶切换后重启按钮文本",
3636
},
3737
"uia_topmost_restart_dialog_cancel_btn": {
38-
"name": "稍后",
38+
"name": "取消",
3939
"description": "UIA置顶切换后取消按钮文本",
4040
},
4141
"reset_floating_window_position_button": {
@@ -147,7 +147,7 @@
147147
"description": "Restart button text after switching UIA topmost",
148148
},
149149
"uia_topmost_restart_dialog_cancel_btn": {
150-
"name": "Later",
150+
"name": "Cancel",
151151
"description": "Cancel button text after switching UIA topmost",
152152
},
153153
"reset_floating_window_position_button": {

app/common/windows/uiaccess.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class _DummyWinTypes:
2121
HWND = int
2222
LPCWSTR = str
2323
UINT = int
24-
24+
2525
wintypes = _DummyWinTypes()
2626

2727
from app.tools.path_utils import get_data_path
@@ -31,6 +31,8 @@ class _DummyWinTypes:
3131
_kernel32 = None
3232

3333
UIACCESS_RESTART_ENV = "SECRANDOM_RESTART_UIACCESS"
34+
ELEVATE_RESTART_ENV = "SECRANDOM_RESTART_ELEVATED"
35+
UIACCESS_RESTART_ARG = "--secrandom-uiaccess"
3436

3537

3638
def _is_windows() -> bool:
@@ -198,3 +200,35 @@ def set_window_band_uiaccess(hwnd: int) -> bool:
198200
return bool(user32.SetWindowPos(h, hwnd_topmost, 0, 0, 0, 0, flags))
199201
except Exception:
200202
return False
203+
204+
205+
def start_elevated_process(cmd_list: list[str], cwd: str | None = None) -> bool:
206+
if not _is_windows():
207+
return False
208+
if not cmd_list:
209+
return False
210+
executable = str(cmd_list[0] or "").strip()
211+
if not executable:
212+
return False
213+
214+
params = list2cmdline(list(cmd_list[1:])) if len(cmd_list) > 1 else ""
215+
directory = str(cwd) if cwd else None
216+
try:
217+
shell32 = ctypes.windll.shell32
218+
shell32.ShellExecuteW.argtypes = [
219+
wintypes.HWND,
220+
wintypes.LPCWSTR,
221+
wintypes.LPCWSTR,
222+
wintypes.LPCWSTR,
223+
wintypes.LPCWSTR,
224+
ctypes.c_int,
225+
]
226+
shell32.ShellExecuteW.restype = wintypes.HINSTANCE
227+
rc = int(shell32.ShellExecuteW(None, "runas", executable, params, directory, 1))
228+
if rc <= 32:
229+
logger.debug("请求管理员启动失败: rc={}", rc)
230+
return False
231+
return True
232+
except Exception as e:
233+
logger.debug("请求管理员启动异常: {}", e)
234+
return False

app/core/window_manager.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from typing import Optional, Callable, TYPE_CHECKING
23
from loguru import logger
34
from PySide6.QtCore import QTimer
@@ -9,6 +10,8 @@
910

1011

1112
app_start_time: float = 0.0
13+
pending_uiaccess_restart_after_show: bool = False
14+
_pending_uiaccess_restart_consumed: bool = False
1215

1316

1417
class WindowManager:
@@ -160,16 +163,62 @@ def _configure_main_window_display(self) -> None:
160163
if is_maximized:
161164
from app.tools.variable import APP_INIT_DELAY
162165

163-
QTimer.singleShot(APP_INIT_DELAY, self.main_window.showMaximized)
166+
def show_main_window():
167+
try:
168+
self.main_window.showMaximized()
169+
finally:
170+
self._schedule_main_window_shown_tasks()
171+
172+
QTimer.singleShot(APP_INIT_DELAY, show_main_window)
164173
else:
165174
self.main_window.show()
175+
self._schedule_main_window_shown_tasks()
166176

167177
startup_display_float = readme_settings_async(
168178
"floating_window_management", "startup_display_floating_window"
169179
)
170180
if startup_display_float:
171181
self.show_float_window()
172182

183+
def _schedule_main_window_shown_tasks(self) -> None:
184+
try:
185+
QTimer.singleShot(0, self._handle_main_window_shown)
186+
except Exception:
187+
pass
188+
189+
def _handle_main_window_shown(self) -> None:
190+
global pending_uiaccess_restart_after_show
191+
global _pending_uiaccess_restart_consumed
192+
193+
if not bool(pending_uiaccess_restart_after_show):
194+
return
195+
if bool(_pending_uiaccess_restart_consumed):
196+
return
197+
_pending_uiaccess_restart_consumed = True
198+
199+
try:
200+
import platform
201+
202+
if platform.system() != "Windows":
203+
return
204+
except Exception:
205+
return
206+
207+
try:
208+
from PySide6.QtWidgets import QApplication
209+
from app.tools.variable import EXIT_CODE_RESTART
210+
from app.common.windows.uiaccess import (
211+
UIACCESS_RESTART_ENV,
212+
is_uiaccess_process,
213+
)
214+
215+
if bool(is_uiaccess_process()):
216+
return
217+
os.environ[str(UIACCESS_RESTART_ENV)] = "1"
218+
QApplication.exit(EXIT_CODE_RESTART)
219+
except Exception as e:
220+
logger.debug("主窗口显示后触发 UIAccess 重启失败(已忽略): {}", e)
221+
173222
def _connect_url_handler_signals(self) -> None:
174223
"""连接URL处理器信号"""
175224
if not self.url_handler:

app/view/floating_window/levitation.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ def _request_uiaccess_restart(self):
187187
os.environ[str(env_key)] = "1"
188188
except Exception:
189189
pass
190+
try:
191+
if not bool(self._is_admin()):
192+
from app.common.windows.uiaccess import ELEVATE_RESTART_ENV
193+
194+
os.environ[str(ELEVATE_RESTART_ENV)] = "1"
195+
except Exception:
196+
pass
190197

191198
app = QApplication.instance()
192199
if app is not None:
@@ -471,11 +478,6 @@ def _init_settings(self):
471478
self._topmost_mode = self._get_int_setting(
472479
"floating_window_management", "floating_window_topmost_mode", 1
473480
)
474-
if self._topmost_mode == 2 and not self._is_admin():
475-
self._topmost_mode = 1
476-
update_settings(
477-
"floating_window_management", "floating_window_topmost_mode", 1
478-
)
479481
self._refresh_window_flags()
480482

481483
# 贴边隐藏功能配置
@@ -1791,14 +1793,6 @@ def _on_setting_changed(self, first, second, value):
17911793
self.setWindowOpacity(self._opacity)
17921794
elif second == "floating_window_topmost_mode":
17931795
mode = int(value or 0)
1794-
if mode == 2 and not self._is_admin():
1795-
self._topmost_mode = 1
1796-
update_settings(
1797-
"floating_window_management", "floating_window_topmost_mode", 1
1798-
)
1799-
self._refresh_window_flags()
1800-
self._apply_topmost_runtime()
1801-
return
18021796
self._topmost_mode = mode
18031797
self._refresh_window_flags()
18041798
self._apply_topmost_runtime()

app/view/settings/floating_window_management.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
# 导入库
33
# ==================================================
44

5+
import os
6+
import ctypes
7+
58
from PySide6.QtWidgets import *
69
from PySide6.QtGui import *
710
from PySide6.QtCore import *
@@ -315,11 +318,25 @@ def floating_window_topmost_mode_combo_box_changed(self, index):
315318
"uia_topmost_restart_dialog_cancel_btn",
316319
)
317320
)
318-
319321
if dialog.exec():
320322
update_settings(
321323
"floating_window_management", "floating_window_topmost_mode", index
322324
)
325+
try:
326+
from app.common.windows.uiaccess import (
327+
ELEVATE_RESTART_ENV,
328+
UIACCESS_RESTART_ENV,
329+
)
330+
331+
os.environ[str(UIACCESS_RESTART_ENV)] = "1"
332+
try:
333+
is_admin = bool(ctypes.windll.shell32.IsUserAnAdmin())
334+
except Exception:
335+
is_admin = False
336+
if not is_admin:
337+
os.environ[str(ELEVATE_RESTART_ENV)] = "1"
338+
except Exception:
339+
pass
323340
QApplication.exit(EXIT_CODE_RESTART)
324341
else:
325342
blocker = QSignalBlocker(self.floating_window_topmost_mode_combo_box)

main.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,33 @@ def restart_application(program_dir):
360360
if platform.system() == "Windows":
361361
try:
362362
from app.common.windows.uiaccess import (
363+
ELEVATE_RESTART_ENV,
363364
UIACCESS_RESTART_ENV,
365+
UIACCESS_RESTART_ARG,
366+
start_elevated_process,
364367
start_uiaccess_process,
365368
)
366369

367370
need_uiaccess = bool(os.environ.pop(UIACCESS_RESTART_ENV, "") == "1")
371+
need_elevated = bool(os.environ.pop(ELEVATE_RESTART_ENV, "") == "1")
368372
except Exception:
369373
need_uiaccess = False
374+
need_elevated = False
370375
start_uiaccess_process = None
376+
start_elevated_process = None
377+
UIACCESS_RESTART_ARG = None
378+
379+
if need_elevated and start_elevated_process is not None:
380+
cmd = [executable] + filtered_args
381+
if need_uiaccess and UIACCESS_RESTART_ARG:
382+
cmd.append(str(UIACCESS_RESTART_ARG))
383+
try:
384+
time.sleep(max(0.8, float(PROCESS_EXIT_WAIT_SECONDS or 0)))
385+
except Exception:
386+
time.sleep(0.8)
387+
if bool(start_elevated_process(cmd, cwd=program_dir)):
388+
logger.info("Windows 平台:已请求管理员启动新进程")
389+
os._exit(0)
371390

372391
if need_uiaccess and start_uiaccess_process is not None:
373392
cmd = [executable] + filtered_args
@@ -457,6 +476,28 @@ def handle_exit(
457476

458477
def main():
459478
"""主程序入口"""
479+
try:
480+
if platform.system() == "Windows":
481+
from app.common.windows.uiaccess import (
482+
UIACCESS_RESTART_ARG,
483+
is_uiaccess_process,
484+
)
485+
486+
if UIACCESS_RESTART_ARG in sys.argv:
487+
try:
488+
while UIACCESS_RESTART_ARG in sys.argv:
489+
sys.argv.remove(UIACCESS_RESTART_ARG)
490+
except Exception:
491+
pass
492+
493+
if not bool(is_uiaccess_process()):
494+
try:
495+
wm.pending_uiaccess_restart_after_show = True
496+
except Exception:
497+
pass
498+
except Exception:
499+
pass
500+
460501
program_dir, shared_memory, is_first_instance = initialize_application()
461502

462503
if not is_first_instance:

0 commit comments

Comments
 (0)