launch packaged apps using names when possible

This commit is contained in:
seraphima
2024-06-26 15:56:30 +02:00
parent c89b4bb9fd
commit 3c694e428c
5 changed files with 144 additions and 5 deletions

View File

@@ -4,7 +4,7 @@
#include <winrt/Windows.Management.Deployment.h>
#include <winrt/Windows.ApplicationModel.Core.h>
#include <iostream>
#include <shellapi.h>
#include <projects-common/AppUtils.h>
#include <projects-common/MonitorEnumerator.h>
@@ -15,6 +15,8 @@
#include <common/Display/dpi_aware.h>
#include <common/utils/winapi_error.h>
#include <RegistryUtils.h>
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Management::Deployment;
@@ -155,6 +157,30 @@ bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs)
return false;
}
bool LaunchUsingUriProtocolName(const std::wstring& uriProtocolName, const std::wstring& commandLineArgs)
{
std::wstring command = std::wstring(uriProtocolName + (commandLineArgs.starts_with(L":") ? L"" : L":") + commandLineArgs);
HINSTANCE result = ShellExecute(
nullptr,
L"open",
command.c_str(),
nullptr,
nullptr,
SW_SHOWNORMAL
);
// If the function succeeds, it returns a value greater than 32.
if (result > reinterpret_cast<HINSTANCE>(32))
{
return true;
}
else
{
Logger::error(L"Failed to launch process. {}", get_last_error_or_default(GetLastError()));
return false;
}
}
bool LaunchPackagedApp(const std::wstring& packageFullName)
{
try
@@ -190,16 +216,31 @@ bool LaunchPackagedApp(const std::wstring& packageFullName)
bool Launch(const Project::Application& app)
{
// TODO: verify app path is up to date.
// Packaged apps have version in the path, it will be outdated after update.
bool launched;
if (!app.packageFullName.empty() && app.commandLineArgs.empty())
{
Logger::trace(L"Launching packaged {}", app.name);
Logger::trace(L"Launching packaged without command line args {}", app.name);
launched = LaunchPackagedApp(app.packageFullName);
}
else
else if (!app.packageFullName.empty() && !app.commandLineArgs.empty())
{
auto names = RegistryUtils::GetUriProtocolNames(app.packageFullName);
if (!names.empty())
{
Logger::trace(L"Launching packaged by protocol with command line args {}", app.name);
launched = LaunchUsingUriProtocolName(names[0], app.commandLineArgs);
}
else
{
Logger::info(L"Uri protocol names not found for {}", app.packageFullName);
}
}
if (!launched)
{
// TODO: verify app path is up to date.
// Packaged apps have version in the path, it will be outdated after update.
Logger::trace(L"Launching {} at {}", app.name, app.path);
DWORD dwAttrib = GetFileAttributesW(app.path.c_str());

View File

@@ -128,10 +128,12 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="RegistryUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AppLauncher.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="RegistryUtils.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>

View File

@@ -24,6 +24,9 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RegistryUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -35,6 +38,9 @@
<ClCompile Include="AppLauncher.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RegistryUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,84 @@
#include "pch.h"
#include "RegistryUtils.h"
#include <strsafe.h>
#include <common/utils/winapi_error.h>
namespace RegistryUtils
{
namespace NonLocalizable
{
const wchar_t RegKeyPackageId[] = L"Extensions\\ContractId\\Windows.Protocol\\PackageId\\";
const wchar_t RegKeyPackageActivatableClassId[] = L"\\ActivatableClassId";
const wchar_t RegKeyPackageCustomProperties[] = L"\\CustomProperties";
const wchar_t RegValueName[] = L"Name";
}
HKEY OpenRootRegKey(const wchar_t* key)
{
HKEY hKey{ nullptr };
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
return hKey;
}
return nullptr;
}
std::vector<std::wstring> GetUriProtocolNames(const std::wstring& packageFullPath)
{
std::vector<std::wstring> names{};
std::wstring keyPath = std::wstring(NonLocalizable::RegKeyPackageId) + packageFullPath + std::wstring(NonLocalizable::RegKeyPackageActivatableClassId);
HKEY key = OpenRootRegKey(keyPath.c_str());
if (key != nullptr)
{
LSTATUS result;
// iterate over all the subkeys to get the protocol names
DWORD index = 0;
wchar_t keyName[256];
DWORD keyNameSize = sizeof(keyName) / sizeof(keyName[0]);
FILETIME lastWriteTime;
while ((result = RegEnumKeyEx(key, index, keyName, &keyNameSize, NULL, NULL, NULL, &lastWriteTime)) != ERROR_NO_MORE_ITEMS)
{
if (result == ERROR_SUCCESS)
{
std::wstring subkeyPath = std::wstring(keyPath) + L"\\" + std::wstring(keyName, keyNameSize) + std::wstring(NonLocalizable::RegKeyPackageCustomProperties);
HKEY subkey = OpenRootRegKey(subkeyPath.c_str());
if (subkey != nullptr)
{
DWORD dataSize;
wchar_t value[256];
result = RegGetValueW(subkey, nullptr, NonLocalizable::RegValueName, RRF_RT_REG_SZ, nullptr, value, &dataSize);
if (result == ERROR_SUCCESS)
{
names.emplace_back(std::wstring(value, dataSize / sizeof(wchar_t) - 1));
}
else
{
Logger::error(L"Failed to query registry value. Error: {}", get_last_error_or_default(result));
}
RegCloseKey(subkey);
}
}
else
{
Logger::error(L"Failed to enumerate subkey. Error: {}", get_last_error_or_default(result));
break;
}
keyNameSize = sizeof(keyName) / sizeof(keyName[0]); // Reset the buffer size
++index;
}
// Close the registry key
RegCloseKey(key);
}
return names;
}
}

View File

@@ -0,0 +1,6 @@
#pragma once
namespace RegistryUtils
{
std::vector<std::wstring> GetUriProtocolNames(const std::wstring& packageFullPath);
};