From a1d8a5130be933534a5ae0fbfb102e0aa964e001 Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 00:55:23 +0800 Subject: [PATCH 1/8] feat: implement MaaControllerPostStopApp for Win32 --- docs/en_us/2.2-IntegratedInterfaceOverview.md | 2 +- ...45\345\217\243\344\270\200\350\247\210.md" | 2 +- .../Manager/Win32ControlUnitMgr.cpp | 30 +++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/en_us/2.2-IntegratedInterfaceOverview.md b/docs/en_us/2.2-IntegratedInterfaceOverview.md index ca691844c4..fd724c5f26 100644 --- a/docs/en_us/2.2-IntegratedInterfaceOverview.md +++ b/docs/en_us/2.2-IntegratedInterfaceOverview.md @@ -449,7 +449,7 @@ Asynchronously start app. This is an asynchronous operation that immediately ret - `intent`: Target app - Adb Controller: package name - - Win32 Controller: not supported yet + - Win32 Controller: ignored (closes the currently associated target window directly) Asynchronously stop app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. diff --git "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" index a954fb314a..544dc26977 100644 --- "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" +++ "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" @@ -449,7 +449,7 @@ - `intent`: 目标应用 - Adb 控制器:package name - - Win32 控制器:暂不支持 + - Win32 控制器:无需参数(直接关联当前目标窗口关闭) 异步关闭应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 0ffd53a429..68b0c03d53 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -255,10 +255,36 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) bool Win32ControlUnitMgr::stop_app(const std::string& intent) { - // TODO + LogFunc << VAR(intent); std::ignore = intent; - return false; + if (!hwnd_ || !IsWindow(hwnd_)) { + LogError << "hwnd_ is invalid"; + return false; + } + + DWORD processId = 0; + GetWindowThreadProcessId(hwnd_, &processId); + if (processId == 0) { + LogError << "GetWindowThreadProcessId failed, error:" << GetLastError(); + return false; + } + + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, processId); + if (!hProcess) { + LogError << "OpenProcess failed, error:" << GetLastError(); + return false; + } + + if (!TerminateProcess(hProcess, 0)) { + LogError << "TerminateProcess failed, error:" << GetLastError(); + CloseHandle(hProcess); + return false; + } + + CloseHandle(hProcess); + LogInfo << "Process" << processId << "terminated successfully"; + return true; } bool Win32ControlUnitMgr::screencap(cv::Mat& image) From 97473118a1b4e1e55d37b43f824e233b89fd2566 Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 01:21:50 +0800 Subject: [PATCH 2/8] refactor: adjust PostStopApp for Win32 to support optional intent matching --- docs/en_us/2.2-IntegratedInterfaceOverview.md | 2 +- ...45\345\217\243\344\270\200\350\247\210.md" | 2 +- .../Manager/Win32ControlUnitMgr.cpp | 79 ++++++++++++++----- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/docs/en_us/2.2-IntegratedInterfaceOverview.md b/docs/en_us/2.2-IntegratedInterfaceOverview.md index fd724c5f26..a389adc7b8 100644 --- a/docs/en_us/2.2-IntegratedInterfaceOverview.md +++ b/docs/en_us/2.2-IntegratedInterfaceOverview.md @@ -449,7 +449,7 @@ Asynchronously start app. This is an asynchronous operation that immediately ret - `intent`: Target app - Adb Controller: package name - - Win32 Controller: ignored (closes the currently associated target window directly) + - Win32 Controller: optional, if provided closes the process by name (e.g., `Endfield.exe`); if empty, directly closes the associated window process Asynchronously stop app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. diff --git "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" index 544dc26977..7ea5b333ab 100644 --- "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" +++ "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" @@ -449,7 +449,7 @@ - `intent`: 目标应用 - Adb 控制器:package name - - Win32 控制器:无需参数(直接关联当前目标窗口关闭) + - Win32 控制器:可选参数,若提供则依据进程名称关闭(如 `Endfield.exe`);若为空则直接关闭关联的视窗进程 异步关闭应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 68b0c03d53..e2f8665c2c 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -5,6 +5,9 @@ #include "MaaFramework/MaaMsg.h" #include "MaaUtils/Logger.h" #include "MaaUtils/Time.hpp" +#include "MaaUtils/Encoding.h" + +#include #include "Input/BackgroundManagedKeyInput.h" #include "Input/LegacyEventInput.h" @@ -256,35 +259,69 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) bool Win32ControlUnitMgr::stop_app(const std::string& intent) { LogFunc << VAR(intent); - std::ignore = intent; - if (!hwnd_ || !IsWindow(hwnd_)) { - LogError << "hwnd_ is invalid"; - return false; - } + if (intent.empty()) { + if (!hwnd_ || !IsWindow(hwnd_)) { + LogError << "hwnd_ is invalid and intent is empty"; + return false; + } - DWORD processId = 0; - GetWindowThreadProcessId(hwnd_, &processId); - if (processId == 0) { - LogError << "GetWindowThreadProcessId failed, error:" << GetLastError(); - return false; - } + DWORD processId = 0; + GetWindowThreadProcessId(hwnd_, &processId); + if (processId == 0) { + LogError << "GetWindowThreadProcessId failed, error:" << GetLastError(); + return false; + } - HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, processId); - if (!hProcess) { - LogError << "OpenProcess failed, error:" << GetLastError(); - return false; - } + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, processId); + if (!hProcess) { + LogError << "OpenProcess failed, error:" << GetLastError(); + return false; + } + + if (!TerminateProcess(hProcess, 0)) { + LogError << "TerminateProcess failed, error:" << GetLastError(); + CloseHandle(hProcess); + return false; + } - if (!TerminateProcess(hProcess, 0)) { - LogError << "TerminateProcess failed, error:" << GetLastError(); CloseHandle(hProcess); + LogInfo << "Process" << processId << "terminated successfully by hwnd"; + return true; + } + + std::wstring target_exe = MAA_NS::to_u16(intent); + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap == INVALID_HANDLE_VALUE) { + LogError << "CreateToolhelp32Snapshot failed, error:" << GetLastError(); return false; } - CloseHandle(hProcess); - LogInfo << "Process" << processId << "terminated successfully"; - return true; + PROCESSENTRY32W pe; + pe.dwSize = sizeof(pe); + bool terminated_any = false; + + if (Process32FirstW(hSnap, &pe)) { + do { + if (target_exe == pe.szExeFile) { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID); + if (hProcess) { + if (TerminateProcess(hProcess, 0)) { + LogInfo << "Process" << pe.th32ProcessID << "terminated successfully by intent matching"; + terminated_any = true; + } else { + LogError << "TerminateProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); + } + CloseHandle(hProcess); + } else { + LogError << "OpenProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); + } + } + } while (Process32NextW(hSnap, &pe)); + } + CloseHandle(hSnap); + + return terminated_any; } bool Win32ControlUnitMgr::screencap(cv::Mat& image) From e83c61ef343b56c0d493f25f40ad48d7432877ff Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 01:26:07 +0800 Subject: [PATCH 3/8] feat: implement PostStartApp for Win32 controller --- docs/en_us/2.2-IntegratedInterfaceOverview.md | 2 +- ...45\345\217\243\344\270\200\350\247\210.md" | 2 +- .../Manager/Win32ControlUnitMgr.cpp | 26 ++++++++++++++++--- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/en_us/2.2-IntegratedInterfaceOverview.md b/docs/en_us/2.2-IntegratedInterfaceOverview.md index a389adc7b8..f9bdb0f8b6 100644 --- a/docs/en_us/2.2-IntegratedInterfaceOverview.md +++ b/docs/en_us/2.2-IntegratedInterfaceOverview.md @@ -441,7 +441,7 @@ Asynchronously input text. This is an asynchronous operation that immediately re - `intent`: Target app - Adb Controller: package name or activity - - Win32 Controller: not supported yet + - Win32 Controller: path or command of the executable (e.g., `D:\Endfield\Endfield.exe` or `notepad.exe`) Asynchronously start app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. diff --git "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" index 7ea5b333ab..d67f3a6280 100644 --- "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" +++ "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" @@ -441,7 +441,7 @@ - `intent`: 目标应用 - Adb 控制器:package name 或 activity - - Win32 控制器:暂不支持 + - Win32 控制器:执行文件的路径或命令(如 `D:\Endfield\Endfield.exe` 或 `notepad.exe`) 异步启动应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index e2f8665c2c..1824ec8574 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -250,10 +250,30 @@ MaaControllerFeature Win32ControlUnitMgr::get_features() const bool Win32ControlUnitMgr::start_app(const std::string& intent) { - // TODO - std::ignore = intent; + LogFunc << VAR(intent); + + if (intent.empty()) { + LogError << "intent is empty"; + return false; + } + + std::wstring cmd = MAA_NS::to_u16(intent); - return false; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcessW(nullptr, cmd.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + LogError << "CreateProcessW failed, error:" << GetLastError(); + return false; + } + + LogInfo << "CreateProcessW succeeded, PID:" << pi.dwProcessId; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return true; } bool Win32ControlUnitMgr::stop_app(const std::string& intent) From 92547d93e6a472b4bf4be81e3cf9c98304c9670e Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 01:37:39 +0800 Subject: [PATCH 4/8] feat: enhance start_app with UAC fallback and update docs --- docs/en_us/2.2-IntegratedInterfaceOverview.md | 4 ++-- ...45\345\217\243\344\270\200\350\247\210.md" | 4 ++-- .../Manager/Win32ControlUnitMgr.cpp | 21 ++++++++++++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/docs/en_us/2.2-IntegratedInterfaceOverview.md b/docs/en_us/2.2-IntegratedInterfaceOverview.md index f9bdb0f8b6..674f8f6edb 100644 --- a/docs/en_us/2.2-IntegratedInterfaceOverview.md +++ b/docs/en_us/2.2-IntegratedInterfaceOverview.md @@ -441,7 +441,7 @@ Asynchronously input text. This is an asynchronous operation that immediately re - `intent`: Target app - Adb Controller: package name or activity - - Win32 Controller: path or command of the executable (e.g., `D:\Endfield\Endfield.exe` or `notepad.exe`) + - Win32 Controller: path or command of the executable Asynchronously start app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. @@ -449,7 +449,7 @@ Asynchronously start app. This is an asynchronous operation that immediately ret - `intent`: Target app - Adb Controller: package name - - Win32 Controller: optional, if provided closes the process by name (e.g., `Endfield.exe`); if empty, directly closes the associated window process + - Win32 Controller: optional, if provided closes the process by name; if empty, directly closes the associated window process Asynchronously stop app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. diff --git "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" index d67f3a6280..54dec6a139 100644 --- "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" +++ "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" @@ -441,7 +441,7 @@ - `intent`: 目标应用 - Adb 控制器:package name 或 activity - - Win32 控制器:执行文件的路径或命令(如 `D:\Endfield\Endfield.exe` 或 `notepad.exe`) + - Win32 控制器:执行文件的路径或命令 异步启动应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 @@ -449,7 +449,7 @@ - `intent`: 目标应用 - Adb 控制器:package name - - Win32 控制器:可选参数,若提供则依据进程名称关闭(如 `Endfield.exe`);若为空则直接关闭关联的视窗进程 + - Win32 控制器:可选参数,若提供则依据进程名称关闭;若为空则直接关闭关联的视窗进程 异步关闭应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 1824ec8574..476cb65eb1 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -266,7 +266,26 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) ZeroMemory(&pi, sizeof(pi)); if (!CreateProcessW(nullptr, cmd.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - LogError << "CreateProcessW failed, error:" << GetLastError(); + DWORD err = GetLastError(); + if (err == ERROR_ELEVATION_REQUIRED) { + LogInfo << "Elevation required. Falling back to ShellExecuteExW..."; + SHELLEXECUTEINFOW sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_NOCLOSEPROCESS; + sei.lpVerb = L"runas"; + sei.lpFile = cmd.c_str(); + sei.nShow = SW_SHOWNORMAL; + if (ShellExecuteExW(&sei)) { + LogInfo << "ShellExecuteExW succeeded."; + if (sei.hProcess) { + CloseHandle(sei.hProcess); + } + return true; + } else { + LogError << "ShellExecuteExW failed, error:" << GetLastError(); + return false; + } + } + LogError << "CreateProcessW failed, error:" << err; return false; } From ada31f4cec03ea9957cd4969e1bdde045e291c93 Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 02:00:24 +0800 Subject: [PATCH 5/8] style: remove unnecessary else block in start_app to align with guard clauses pattern --- source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 476cb65eb1..9ef4b58656 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -280,10 +280,9 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) CloseHandle(sei.hProcess); } return true; - } else { - LogError << "ShellExecuteExW failed, error:" << GetLastError(); - return false; } + LogError << "ShellExecuteExW failed, error:" << GetLastError(); + return false; } LogError << "CreateProcessW failed, error:" << err; return false; From 2e6545311cc7bd3c520042348f6fa2a9b863ebef Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 02:37:03 +0800 Subject: [PATCH 6/8] fix: correctly pass lpFile and lpParameters to ShellExecuteExW --- .../Manager/Win32ControlUnitMgr.cpp | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 9ef4b58656..336a87415f 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -269,10 +269,33 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) DWORD err = GetLastError(); if (err == ERROR_ELEVATION_REQUIRED) { LogInfo << "Elevation required. Falling back to ShellExecuteExW..."; + std::wstring file = cmd; + std::wstring args; + if (!cmd.empty()) { + if (cmd[0] == L'\"') { + auto pos = cmd.find(L'\"', 1); + if (pos != std::wstring::npos) { + file = cmd.substr(1, pos - 1); + if (pos + 1 < cmd.length()) { + args = cmd.substr(pos + 1); + args.erase(0, args.find_first_not_of(L" \t")); + } + } + } else { + auto pos = cmd.find(L' '); + if (pos != std::wstring::npos) { + file = cmd.substr(0, pos); + args = cmd.substr(pos + 1); + args.erase(0, args.find_first_not_of(L" \t")); + } + } + } + SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.fMask = SEE_MASK_NOCLOSEPROCESS; sei.lpVerb = L"runas"; - sei.lpFile = cmd.c_str(); + sei.lpFile = file.c_str(); + sei.lpParameters = args.empty() ? nullptr : args.c_str(); sei.nShow = SW_SHOWNORMAL; if (ShellExecuteExW(&sei)) { LogInfo << "ShellExecuteExW succeeded."; From 2243abe6149cec3f44de8b41aad93f072936ab66 Mon Sep 17 00:00:00 2001 From: kaihuang1122 Date: Sat, 20 Jun 2026 03:05:08 +0800 Subject: [PATCH 7/8] docs: specify the usage of PostStopApp --- docs/en_us/2.2-IntegratedInterfaceOverview.md | 2 +- ...\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en_us/2.2-IntegratedInterfaceOverview.md b/docs/en_us/2.2-IntegratedInterfaceOverview.md index 674f8f6edb..b3f342a187 100644 --- a/docs/en_us/2.2-IntegratedInterfaceOverview.md +++ b/docs/en_us/2.2-IntegratedInterfaceOverview.md @@ -449,7 +449,7 @@ Asynchronously start app. This is an asynchronous operation that immediately ret - `intent`: Target app - Adb Controller: package name - - Win32 Controller: optional, if provided closes the process by name; if empty, directly closes the associated window process + - Win32 Controller: process name (optional), default to the associated window process Asynchronously stop app. This is an asynchronous operation that immediately returns an operation id. You can query the status via `MaaControllerStatus` and `MaaControllerWait`. diff --git "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" index 54dec6a139..2ae8a2c2c5 100644 --- "a/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" +++ "b/docs/zh_cn/2.2-\351\233\206\346\210\220\346\216\245\345\217\243\344\270\200\350\247\210.md" @@ -449,7 +449,7 @@ - `intent`: 目标应用 - Adb 控制器:package name - - Win32 控制器:可选参数,若提供则依据进程名称关闭;若为空则直接关闭关联的视窗进程 + - Win32 控制器:进程名称(可选),预设为关联的视窗进程 异步关闭应用。这是一个异步操作,会立即返回一个操作 id,可通过 `MaaControllerStatus` 和 `MaaControllerWait` 查询状态。 From 07be5c61e1f7e26f3dd87084468eccd0c7890a6a Mon Sep 17 00:00:00 2001 From: Kai Huang Date: Sat, 20 Jun 2026 21:07:21 +0800 Subject: [PATCH 8/8] refactor: improve flows in start_app and stop_app methods --- .../Manager/Win32ControlUnitMgr.cpp | 131 +++++++++--------- 1 file changed, 69 insertions(+), 62 deletions(-) diff --git a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp index 336a87415f..ed1a7ee693 100644 --- a/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp +++ b/source/MaaWin32ControlUnit/Manager/Win32ControlUnitMgr.cpp @@ -265,55 +265,53 @@ bool Win32ControlUnitMgr::start_app(const std::string& intent) si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); - if (!CreateProcessW(nullptr, cmd.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - DWORD err = GetLastError(); - if (err == ERROR_ELEVATION_REQUIRED) { - LogInfo << "Elevation required. Falling back to ShellExecuteExW..."; - std::wstring file = cmd; - std::wstring args; - if (!cmd.empty()) { - if (cmd[0] == L'\"') { - auto pos = cmd.find(L'\"', 1); - if (pos != std::wstring::npos) { - file = cmd.substr(1, pos - 1); - if (pos + 1 < cmd.length()) { - args = cmd.substr(pos + 1); - args.erase(0, args.find_first_not_of(L" \t")); - } - } - } else { - auto pos = cmd.find(L' '); - if (pos != std::wstring::npos) { - file = cmd.substr(0, pos); - args = cmd.substr(pos + 1); - args.erase(0, args.find_first_not_of(L" \t")); - } - } - } - - SHELLEXECUTEINFOW sei = { sizeof(sei) }; - sei.fMask = SEE_MASK_NOCLOSEPROCESS; - sei.lpVerb = L"runas"; - sei.lpFile = file.c_str(); - sei.lpParameters = args.empty() ? nullptr : args.c_str(); - sei.nShow = SW_SHOWNORMAL; - if (ShellExecuteExW(&sei)) { - LogInfo << "ShellExecuteExW succeeded."; - if (sei.hProcess) { - CloseHandle(sei.hProcess); - } - return true; - } - LogError << "ShellExecuteExW failed, error:" << GetLastError(); + if (CreateProcessW(nullptr, cmd.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + LogInfo << "CreateProcessW succeeded, PID:" << pi.dwProcessId; + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return true; + } + + DWORD err = GetLastError(); + if (err != ERROR_ELEVATION_REQUIRED) { + LogError << "CreateProcessW failed, error:" << err; + return false; + } + LogInfo << "Elevation required. Falling back to ShellExecuteExW..."; + std::wstring file = cmd; + std::wstring args; + // Parse + if (cmd[0] == L'\"') { + auto pos = cmd.find(L'\"', 1); + if (pos == std::wstring::npos) { + LogError << "Mismatched quotes in command, cannot parse file and arguments"; return false; } - LogError << "CreateProcessW failed, error:" << err; + file = cmd.substr(1, pos - 1); + if (pos + 1 < cmd.length()) { + args = cmd.substr(pos + 1); + args.erase(0, args.find_first_not_of(L" \t")); + } + } else if (auto pos = cmd.find(L' '); pos != std::wstring::npos) { + file = cmd.substr(0, pos); + args = cmd.substr(pos + 1); + args.erase(0, args.find_first_not_of(L" \t")); + } + // execute + SHELLEXECUTEINFOW sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_NOCLOSEPROCESS; + sei.lpVerb = L"runas"; + sei.lpFile = file.c_str(); + sei.lpParameters = args.empty() ? nullptr : args.c_str(); + sei.nShow = SW_SHOWNORMAL; + if (!ShellExecuteExW(&sei)) { + LogError << "ShellExecuteExW failed, error:" << GetLastError(); return false; } - - LogInfo << "CreateProcessW succeeded, PID:" << pi.dwProcessId; - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); + LogInfo << "ShellExecuteExW succeeded."; + if (sei.hProcess) { + CloseHandle(sei.hProcess); + } return true; } @@ -362,24 +360,33 @@ bool Win32ControlUnitMgr::stop_app(const std::string& intent) pe.dwSize = sizeof(pe); bool terminated_any = false; - if (Process32FirstW(hSnap, &pe)) { - do { - if (target_exe == pe.szExeFile) { - HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID); - if (hProcess) { - if (TerminateProcess(hProcess, 0)) { - LogInfo << "Process" << pe.th32ProcessID << "terminated successfully by intent matching"; - terminated_any = true; - } else { - LogError << "TerminateProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); - } - CloseHandle(hProcess); - } else { - LogError << "OpenProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); - } - } - } while (Process32NextW(hSnap, &pe)); + if (!Process32FirstW(hSnap, &pe)) { + LogError << "Process32FirstW failed, error:" << GetLastError(); + CloseHandle(hSnap); + return false; } + + do { + if (target_exe != pe.szExeFile) { + continue; + } + + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID); + if (!hProcess) { + LogError << "OpenProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); + continue; + } + + if (TerminateProcess(hProcess, 0)) { + LogInfo << "Process" << pe.th32ProcessID << "terminated successfully by intent matching"; + terminated_any = true; + } else { + LogError << "TerminateProcess failed for PID" << pe.th32ProcessID << "error:" << GetLastError(); + } + + CloseHandle(hProcess); + + } while (Process32NextW(hSnap, &pe)); CloseHandle(hSnap); return terminated_any;