MCPcopy Create free account
hub / github.com/ClassicOldSong/Apollo / resolve_command_string

Function resolve_command_string

src/platform/windows/misc.cpp:744–938  ·  view source on GitHub ↗

* @brief Resolve the given raw command into a proper command string for CreateProcess(). * @details This converts URLs and non-executable file paths into a runnable command like ShellExecute(). * @param raw_cmd The raw command provided by the user. * @param working_dir The working directory for the new process. * @param token The user token currently being impersonated or `nullptr` if

Source from the content-addressed store, hash-verified

742 * @return A command string suitable for use by CreateProcess().
743 */
744 std::wstring resolve_command_string(const std::string &raw_cmd, const std::wstring &working_dir, HANDLE token, DWORD &creation_flags) {
745 std::wstring raw_cmd_w = from_utf8(raw_cmd);
746
747 // First, convert the given command into parts so we can get the executable/file/URL without parameters
748 auto raw_cmd_parts = boost::program_options::split_winmain(raw_cmd_w);
749 if (raw_cmd_parts.empty()) {
750 // This is highly unexpected, but we'll just return the raw string and hope for the best.
751 BOOST_LOG(warning) << "Failed to split command string: "sv << raw_cmd;
752 return from_utf8(raw_cmd);
753 }
754
755 auto raw_target = raw_cmd_parts.at(0);
756 if (PathIsURLW(raw_target.c_str())) {
757 // If the target is a URL, handle it directly with rundll32.exe
758 std::wstring cmd = L"rundll32.exe url.dll,FileProtocolHandler " + raw_target;
759 return cmd;
760 }
761
762 // If the target is not a URL, assume it's a regular file path
763 auto extension = PathFindExtensionW(raw_target.c_str());
764 if (extension == nullptr || *extension == 0) {
765 // If the file has no extension, assume it's a command and allow CreateProcess()
766 // to try to find it via PATH
767 return from_utf8(raw_cmd);
768 }
769 else if (boost::iequals(extension, L".exe")) {
770 // If the file has an .exe extension, we will bypass the resolution here and
771 // directly pass the unmodified command string to CreateProcess(). The argument
772 // escaping rules are subtly different between CreateProcess() and ShellExecute(),
773 // and we want to preserve backwards compatibility with older configs.
774 return from_utf8(raw_cmd);
775 }
776
777 // For regular files, the class is found using the file extension (including the dot)
778 std::wstring lookup_string = extension;
779 HRESULT res;
780
781 std::array<WCHAR, MAX_PATH> shell_command_string;
782 bool needs_cmd_escaping = false;
783 {
784 // Overriding these predefined keys affects process-wide state, so serialize all calls
785 // to ensure the handle state is consistent while we perform the command query.
786 static std::mutex per_user_key_mutex;
787 auto lg = std::lock_guard(per_user_key_mutex);
788
789 // Override HKEY_CLASSES_ROOT and HKEY_CURRENT_USER to ensure we query the correct class info
790 if (!override_per_user_predefined_keys(token)) {
791 return from_utf8(raw_cmd);
792 }
793
794 // Find the command string for the specified class
795 DWORD out_len = shell_command_string.size();
796 res = AssocQueryStringW(ASSOCF_NOTRUNCATE, ASSOCSTR_COMMAND, lookup_string.c_str(), L"open", shell_command_string.data(), &out_len);
797
798 // In some cases (UWP apps), we might not have a command for this target. If that happens,
799 // we'll have to launch via cmd.exe. This prevents proper job tracking, but that was already
800 // broken for UWP apps anyway due to how they are started by Windows. Even 'start /wait'
801 // doesn't work properly for UWP, so really no termination tracking seems to work at all.

Callers 1

run_commandFunction · 0.85

Calls 8

hexFunction · 0.85
escape_argumentFunction · 0.85
escape_argument_for_cmdFunction · 0.85
to_string_viewMethod · 0.80
from_utf8Function · 0.70
sizeMethod · 0.45
dataMethod · 0.45

Tested by

no test coverage detected