Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions src/common/utils/modulesRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

namespace fs = std::filesystem;

// Import path normalization function from registry namespace
using registry::shellex::normalizePathForComRegistration;

namespace NonLocalizable
{
const static wchar_t* MONACO_LANGUAGES_FILE_NAME = L"Assets\\Monaco\\monaco_languages.json";
Expand Down Expand Up @@ -240,10 +243,11 @@ inline registry::ChangeSet getRegistryPreviewSetDefaultAppChangeSet(const std::w
std::wstring registryKeyPrefix = L"Software\\Classes\\";

std::wstring appPath = installationDir + L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe";
std::wstring command = appPath + L" \"----ms-protocol:ms-encodedlaunch:App?ContractId=Windows.File&Verb=open&File=%1\"";
std::wstring normalizedAppPath = normalizePathForComRegistration(appPath);
std::wstring command = normalizedAppPath + L" \"----ms-protocol:ms-encodedlaunch:App?ContractId=Windows.File&Verb=open&File=%1\"";

changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"Application", L"ApplicationName", appName });
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"DefaultIcon", std::nullopt, appPath });
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"DefaultIcon", std::nullopt, normalizedAppPath });
changes.push_back({ scope, registryKeyPrefix + fullAppName + L"\\" + L"shell\\open\\command", std::nullopt, command });
changes.push_back({ scope, registryKeyPrefix + L".reg\\OpenWithProgIDs", fullAppName, L"" });

Expand All @@ -257,13 +261,15 @@ inline registry::ChangeSet getRegistryPreviewChangeSet(const std::wstring instal
using vec_t = std::vector<registry::ValueChange>;
vec_t changes;

std::wstring command = installationDir;
command.append(L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe \"%1\"");
std::wstring exePath = installationDir + L"\\WinUI3Apps\\PowerToys.RegistryPreview.exe";
std::wstring normalizedExePath = normalizePathForComRegistration(exePath);
std::wstring command = normalizedExePath + L" \"%1\"";
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview\\command", std::nullopt, command });

std::wstring icon_path = installationDir;
icon_path.append(L"\\WinUI3Apps\\Assets\\RegistryPreview\\RegistryPreview.ico");
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", icon_path });
std::wstring normalizedIconPath = normalizePathForComRegistration(icon_path);
changes.push_back({ scope, L"Software\\Classes\\regfile\\shell\\preview", L"icon", normalizedIconPath });

return { changes };
}
Expand Down
53 changes: 52 additions & 1 deletion src/common/utils/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,37 @@ namespace registry
thumbnail
};

// Helper function to properly quote DLL paths for COM registration
inline std::wstring quotePathIfNeeded(const std::wstring& path)
{
// If path contains spaces, it should be quoted for proper COM registration
if (path.find(L' ') != std::wstring::npos)
{
return L"\"" + path + L"\"";
}
return path;
}

// Helper function to normalize and resolve DLL paths for COM registration
inline std::wstring normalizePathForComRegistration(const std::wstring& path)
{
try
{
// Normalize the path to handle any relative components or inconsistencies
auto normalizedPath = std::filesystem::canonical(std::filesystem::path(path));
auto pathStr = normalizedPath.wstring();

// Quote if needed and return
return quotePathIfNeeded(pathStr);
}
catch (const std::filesystem::filesystem_error&)
{
// If canonicalization fails (file doesn't exist), fall back to the original path
// This can happen during installation when files aren't copied yet
return quotePathIfNeeded(path);
}
}

inline registry::ChangeSet generatePreviewHandler(const PreviewHandlerType handlerType,
const bool perUser,
std::wstring handlerClsid,
Expand Down Expand Up @@ -426,11 +457,17 @@ namespace registry
versionPath += L'\\';
versionPath += powertoysVersion;

// Normalize and quote the DLL path to ensure proper COM registration
std::wstring normalizedPathToHandler = normalizePathForComRegistration(fullPathToHandler);

// Log the path transformation for debugging
Logger::info(L"Registering preview handler: {} -> {}", fullPathToHandler, normalizedPathToHandler);

using vec_t = std::vector<registry::ValueChange>;
// TODO: verify that we actually need all of those
vec_t changes = { { scope, clsidPath, L"DisplayName", displayName },
{ scope, clsidPath, std::nullopt, className },
{ scope, inprocServerPath, std::nullopt, fullPathToHandler },
{ scope, inprocServerPath, std::nullopt, normalizedPathToHandler },
{ scope, inprocServerPath, L"Assembly", assemblyKeyValue },
{ scope, inprocServerPath, L"Class", className },
{ scope, inprocServerPath, L"ThreadingModel", L"Apartment" } };
Expand Down Expand Up @@ -467,6 +504,20 @@ namespace registry

changes.push_back({ scope, clsidPath, L"AppID", previewHostClsid });
changes.push_back({ scope, previewHandlerListPath, handlerClsid, displayName });

// For user-directory installations, add additional AppID configuration to ensure proper COM activation
if (perUser)
{
std::wstring appIdPath = L"Software\\Classes\\AppID\\" + previewHostClsid;
// These settings help ensure the preview handler can be activated properly from different security contexts
changes.push_back({ scope, appIdPath, L"DllSurrogate", L"" }); // Use default surrogate (prevhost.exe)
changes.push_back({ scope, appIdPath, L"AppIDFlags", DWORD(0x4) }); // APPIDREGFLAGS_ACTIVATE_IUSERVER_INDESKTOP

// Add authentication level to ensure proper security context
changes.push_back({ scope, appIdPath, L"AuthenticationLevel", DWORD(1) }); // RPC_C_AUTHN_LEVEL_NONE

Logger::info(L"Added per-user AppID configuration for preview handler in: {}", normalizedPathToHandler);
}
}

return registry::ChangeSet{ .changes = std::move(changes) };
Expand Down