#include "pch.h" #include "AppLauncher.h" #include #include #include #include #include #include using namespace winrt; using namespace Windows::Foundation; using namespace Windows::Management::Deployment; namespace AppLauncher { namespace NonLocalizable { const std::wstring EdgeFilename = L"msedge.exe"; const std::wstring EdgePwaFilename = L"msedge_proxy.exe"; const std::wstring ChromeFilename = L"chrome.exe"; const std::wstring ChromePwaFilename = L"chrome_proxy.exe"; const std::wstring PwaCommandLineAddition = L"--profile-directory=Default --app-id="; const std::wstring SteamProtocolPrefix = L"steam:"; } Result LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated) { std::wstring dir = std::filesystem::path(appPath).parent_path(); SHELLEXECUTEINFO sei = { 0 }; sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.hwnd = nullptr; sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; sei.lpVerb = elevated ? L"runas" : L"open"; sei.lpFile = appPath.c_str(); sei.lpParameters = commandLineArgs.c_str(); sei.lpDirectory = dir.c_str(); sei.nShow = SW_SHOWNORMAL; if (!ShellExecuteEx(&sei)) { std::wstring error = get_last_error_or_default(GetLastError()); Logger::error(L"Failed to launch process. {}", error); return Error(error); } return Ok(sei); } bool LaunchPackagedApp(const std::wstring& packageFullName, ErrorList& launchErrors) { try { PackageManager packageManager; for (const auto& package : packageManager.FindPackagesForUser({})) { if (package.Id().FullName() == packageFullName) { auto getAppListEntriesOperation = package.GetAppListEntriesAsync(); auto appEntries = getAppListEntriesOperation.get(); if (appEntries.Size() > 0) { IAsyncOperation launchOperation = appEntries.GetAt(0).LaunchAsync(); bool launchResult = launchOperation.get(); return launchResult; } else { Logger::error(L"No app entries found for the package."); launchErrors.push_back({ packageFullName, L"No app entries found for the package." }); } } } } catch (const hresult_error& ex) { Logger::error(L"Packaged app launching error: {}", ex.message()); launchErrors.push_back({ packageFullName, ex.message().c_str() }); } return false; } bool Launch(const WorkspacesData::WorkspacesProject::Application& app, ErrorList& launchErrors) { bool launched{ false }; // packaged apps: check protocol in registry // usage example: Settings with cmd args if (!app.packageFullName.empty()) { auto names = RegistryUtils::GetUriProtocolNames(app.packageFullName); if (!names.empty()) { Logger::trace(L"Launching packaged by protocol with command line args {}", app.name); std::wstring uriProtocolName = names[0]; std::wstring command = std::wstring(uriProtocolName + (app.commandLineArgs.starts_with(L":") ? L"" : L":") + app.commandLineArgs); auto res = LaunchApp(command, L"", app.isElevated); if (res.isOk()) { launched = true; } else { launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); } } else { Logger::info(L"Uri protocol names not found for {}", app.packageFullName); } } // packaged apps: try launching first by AppUserModel.ID // usage example: elevated Terminal if (!launched && !app.appUserModelId.empty() && !app.packageFullName.empty()) { Logger::trace(L"Launching {} as {} - {app.packageFullName}", app.name, app.appUserModelId, app.packageFullName); auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); if (res.isOk()) { launched = true; } else { launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); } } // protocol launch for steam if (!launched && !app.appUserModelId.empty() && app.appUserModelId.contains(NonLocalizable::SteamProtocolPrefix)) { Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId); auto res = LaunchApp(app.appUserModelId, app.commandLineArgs, app.isElevated); if (res.isOk()) { launched = true; } else { launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); } } // packaged apps: try launching by package full name // doesn't work for elevated apps or apps with command line args if (!launched && !app.packageFullName.empty() && app.commandLineArgs.empty() && !app.isElevated) { Logger::trace(L"Launching packaged app {}", app.name); launched = LaunchPackagedApp(app.packageFullName, launchErrors); } std::wstring appPathFinal; std::wstring commandLineArgsFinal; appPathFinal = app.path; commandLineArgsFinal = app.commandLineArgs; if (!launched && !app.pwaAppId.empty()) { int version = 0; if (app.version != L"") { try { version = std::stoi(app.version); } catch (const std::invalid_argument&) { Logger::error(L"Invalid version format: {}", app.version); version = 0; } catch (const std::out_of_range&) { Logger::error(L"Version out of range: {}", app.version); version = 0; } } if (version >= 1) { auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); if (res.isOk()) { launched = true; } else { launchErrors.push_back({ app.appUserModelId, res.error() }); } } if (!launched) { std::filesystem::path appPath(app.path); if (appPath.filename() == NonLocalizable::EdgeFilename) { appPathFinal = appPath.parent_path() / NonLocalizable::EdgePwaFilename; commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs; } if (appPath.filename() == NonLocalizable::ChromeFilename) { appPathFinal = appPath.parent_path() / NonLocalizable::ChromePwaFilename; commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs; } } } if (!launched) { Logger::trace(L"Launching {} at {}", app.name, appPathFinal); DWORD dwAttrib = GetFileAttributesW(appPathFinal.c_str()); if (dwAttrib == INVALID_FILE_ATTRIBUTES) { Logger::error(L"File not found at {}", appPathFinal); launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), L"File not found" }); return false; } auto res = LaunchApp(appPathFinal, commandLineArgsFinal, app.isElevated); if (res.isOk()) { launched = true; } else { launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), res.error() }); } } Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), appPathFinal); return launched; } }