6217: Register application separately to specify its displayed name r=def- a=Robyt3

Follow-up from #6199.

Adding version information to the executable (#6203) doesn't result in the name being shown in the Windows settings. There is a separate registry key where applications can register a readable name.

See: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration

## Checklist

- [X] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Robert Müller <robytemueller@gmail.com>
This commit is contained in:
bors[bot] 2023-01-02 21:55:37 +00:00 committed by GitHub
commit 29bf49ae31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 166 additions and 25 deletions

View file

@ -4324,6 +4324,12 @@ static std::wstring utf8_to_wstring(const char *str)
return wide_string;
}
static std::wstring filename_from_path(const std::wstring &path)
{
const size_t pos = path.find_last_of(L"/\\");
return pos == std::wstring::npos ? path : path.substr(pos + 1);
}
bool shell_register_protocol(const char *protocol_name, const char *executable, bool *updated)
{
const std::wstring protocol_name_wide = utf8_to_wstring(protocol_name);
@ -4416,6 +4422,10 @@ bool shell_register_protocol(const char *protocol_name, const char *executable,
*updated = true;
}
else
{
RegCloseKey(handle_subkey_shell_open_command);
}
return true;
}
@ -4548,11 +4558,64 @@ bool shell_register_extension(const char *extension, const char *description, co
*updated = true;
}
else
{
RegCloseKey(handle_subkey_extension);
}
return true;
}
bool shell_unregister(const char *shell_class, bool *updated)
bool shell_register_application(const char *name, const char *executable, bool *updated)
{
const std::wstring name_wide = utf8_to_wstring(name);
const std::wstring executable_filename = filename_from_path(utf8_to_wstring(executable));
// Open registry key for application registrations
HKEY handle_subkey_applications;
const LRESULT result_subkey_applications = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\Applications", 0, KEY_ALL_ACCESS, &handle_subkey_applications);
if(result_subkey_applications != ERROR_SUCCESS)
{
windows_print_error("shell_register_application", "Error opening registry key", result_subkey_applications);
return false;
}
// Create the program key
HKEY handle_subkey_program;
const LRESULT result_subkey_program = RegCreateKeyExW(handle_subkey_applications, executable_filename.c_str(), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_program, NULL);
RegCloseKey(handle_subkey_applications);
if(result_subkey_program != ERROR_SUCCESS)
{
windows_print_error("shell_register_application", "Error creating registry key", result_subkey_program);
return false;
}
// Get the previous default value for the key, so we can determine if it changed
wchar_t old_value_executable[MAX_PATH];
DWORD old_size_executable = sizeof(old_value_executable);
const LRESULT result_old_value_executable = RegGetValueW(handle_subkey_program, NULL, L"FriendlyAppName", RRF_RT_REG_SZ, NULL, (BYTE *)old_value_executable, &old_size_executable);
if(result_old_value_executable != ERROR_SUCCESS || wcscmp(old_value_executable, name_wide.c_str()) != 0)
{
// Set the "FriendlyAppName" value, which specifies the displayed name of the application
const LRESULT result_program_name = RegSetValueExW(handle_subkey_program, L"FriendlyAppName", 0, REG_SZ, (BYTE *)name_wide.c_str(), (name_wide.length() + 1) * sizeof(wchar_t));
RegCloseKey(handle_subkey_program);
if(result_program_name != ERROR_SUCCESS)
{
windows_print_error("shell_register_application", "Error setting registry value", result_program_name);
return false;
}
*updated = true;
}
else
{
RegCloseKey(handle_subkey_program);
}
return true;
}
bool shell_unregister_class(const char *shell_class, bool *updated)
{
const std::wstring class_wide = utf8_to_wstring(shell_class);
@ -4561,18 +4624,49 @@ bool shell_unregister(const char *shell_class, bool *updated)
const LRESULT result_subkey_classes = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Classes", 0, KEY_ALL_ACCESS, &handle_subkey_classes);
if(result_subkey_classes != ERROR_SUCCESS)
{
windows_print_error("shell_unregister", "Error opening registry key", result_subkey_classes);
windows_print_error("shell_unregister_class", "Error opening registry key", result_subkey_classes);
return false;
}
// Delete the registry keys for the shell class (protocol or program ID)
LRESULT result_delete = RegDeleteTreeW(handle_subkey_classes, class_wide.c_str());
RegCloseKey(handle_subkey_classes);
if(result_delete != ERROR_SUCCESS && result_delete != ERROR_FILE_NOT_FOUND)
if(result_delete == ERROR_SUCCESS)
{
windows_print_error("shell_unregister", "Error deleting registry key", result_delete);
if(result_delete == ERROR_SUCCESS)
*updated = true;
*updated = true;
}
else if(result_delete != ERROR_FILE_NOT_FOUND)
{
windows_print_error("shell_unregister_class", "Error deleting registry key", result_delete);
return false;
}
return true;
}
bool shell_unregister_application(const char *executable, bool *updated)
{
const std::wstring executable_filename = filename_from_path(utf8_to_wstring(executable));
// Open registry key for application registrations
HKEY handle_subkey_applications;
const LRESULT result_subkey_applications = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Classes\\Applications", 0, KEY_ALL_ACCESS, &handle_subkey_applications);
if(result_subkey_applications != ERROR_SUCCESS)
{
windows_print_error("shell_unregister_application", "Error opening registry key", result_subkey_applications);
return false;
}
// Delete the registry keys for the application description
LRESULT result_delete = RegDeleteTreeW(handle_subkey_applications, executable_filename.c_str());
RegCloseKey(handle_subkey_applications);
if(result_delete == ERROR_SUCCESS)
{
*updated = true;
}
else if(result_delete != ERROR_FILE_NOT_FOUND)
{
windows_print_error("shell_unregister_application", "Error deleting registry key", result_delete);
return false;
}

View file

@ -2614,6 +2614,21 @@ bool shell_register_protocol(const char *protocol_name, const char *executable,
*/
bool shell_register_extension(const char *extension, const char *description, const char *executable_name, const char *executable, bool *updated);
/**
* Registers an application.
*
* @ingroup Shell
*
* @param name Readable name of the application.
* @param executable The absolute path of the executable being registered.
* @param updated Pointer to a variable that will be set to true, iff the shell needs to be updated.
*
* @return true on success, false on failure.
*
* @remark The caller must later call shell_update, iff the shell needs to be updated.
*/
bool shell_register_application(const char *name, const char *executable, bool *updated);
/**
* Unregisters a protocol or file extension handler.
*
@ -2628,7 +2643,21 @@ bool shell_register_extension(const char *extension, const char *description, co
*
* @remark The caller must later call shell_update, iff the shell needs to be updated.
*/
bool shell_unregister(const char *shell_class, bool *updated);
bool shell_unregister_class(const char *shell_class, bool *updated);
/**
* Unregisters an application.
*
* @ingroup Shell
*
* @param executable The absolute path of the executable being unregistered.
* @param updated Pointer to a variable that will be set to true, iff the shell needs to be updated.
*
* @return true on success, false on failure.
*
* @remark The caller must later call shell_update, iff the shell needs to be updated.
*/
bool shell_unregister_application(const char *executable, bool *updated);
/**
* Notifies the system that a protocol or file extension has been changed and the shell needs to be updated.

View file

@ -4906,22 +4906,8 @@ int CClient::UdpConnectivity(int NetType)
#if defined(CONF_FAMILY_WINDOWS)
void CClient::ShellRegister()
{
char aBinaryPath[IO_MAX_PATH_LENGTH];
Storage()->GetBinaryPath(PLAT_CLIENT_EXEC, aBinaryPath, sizeof(aBinaryPath));
char aFullPath[IO_MAX_PATH_LENGTH];
if(fs_is_relative_path(aBinaryPath))
{
if(fs_getcwd(aFullPath, sizeof(aFullPath)))
{
str_append(aFullPath, "/", sizeof(aFullPath));
str_append(aFullPath, aBinaryPath, sizeof(aFullPath));
}
else
aFullPath[0] = '\0';
}
else
str_copy(aFullPath, aBinaryPath);
Storage()->GetBinaryPathAbsolute(PLAT_CLIENT_EXEC, aFullPath, sizeof(aFullPath));
if(!aFullPath[0])
{
dbg_msg("client", "Failed to register protocol and file extensions: could not determine absolute path");
@ -4935,19 +4921,31 @@ void CClient::ShellRegister()
dbg_msg("client", "Failed to register .map file extension");
if(!shell_register_extension(".demo", "Demo File", GAME_NAME, aFullPath, &Updated))
dbg_msg("client", "Failed to register .demo file extension");
if(!shell_register_application(GAME_NAME, aFullPath, &Updated))
dbg_msg("client", "Failed to register application");
if(Updated)
shell_update();
}
void CClient::ShellUnregister()
{
char aFullPath[IO_MAX_PATH_LENGTH];
Storage()->GetBinaryPathAbsolute(PLAT_CLIENT_EXEC, aFullPath, sizeof(aFullPath));
if(!aFullPath[0])
{
dbg_msg("client", "Failed to unregister protocol and file extensions: could not determine absolute path");
return;
}
bool Updated = false;
if(!shell_unregister("ddnet", &Updated))
if(!shell_unregister_class("ddnet", &Updated))
dbg_msg("client", "Failed to unregister ddnet protocol");
if(!shell_unregister(GAME_NAME ".map", &Updated))
if(!shell_unregister_class(GAME_NAME ".map", &Updated))
dbg_msg("client", "Failed to unregister .map file extension");
if(!shell_unregister(GAME_NAME ".demo", &Updated))
if(!shell_unregister_class(GAME_NAME ".demo", &Updated))
dbg_msg("client", "Failed to unregister .demo file extension");
if(!shell_unregister_application(aFullPath, &Updated))
dbg_msg("client", "Failed to unregister application");
if(Updated)
shell_update();
}

View file

@ -677,6 +677,25 @@ public:
return pBuffer;
}
const char *GetBinaryPathAbsolute(const char *pFilename, char *pBuffer, unsigned BufferSize) override
{
char aBinaryPath[IO_MAX_PATH_LENGTH];
GetBinaryPath(PLAT_CLIENT_EXEC, aBinaryPath, sizeof(aBinaryPath));
if(fs_is_relative_path(aBinaryPath))
{
if(fs_getcwd(pBuffer, BufferSize))
{
str_append(pBuffer, "/", BufferSize);
str_append(pBuffer, aBinaryPath, BufferSize);
}
else
pBuffer[0] = '\0';
}
else
str_copy(pBuffer, aBinaryPath, BufferSize);
return pBuffer;
}
static IStorage *Create(int StorageType, int NumArgs, const char **ppArguments)
{
CStorage *pStorage = new CStorage();

View file

@ -57,6 +57,7 @@ public:
virtual bool RemoveBinaryFile(const char *pFilename) = 0;
virtual bool RenameBinaryFile(const char *pOldFilename, const char *pNewFilename) = 0;
virtual const char *GetBinaryPath(const char *pFilename, char *pBuffer, unsigned BufferSize) = 0;
virtual const char *GetBinaryPathAbsolute(const char *pFilename, char *pBuffer, unsigned BufferSize) = 0;
static void StripPathAndExtension(const char *pFilename, char *pBuffer, int BufferSize);
static const char *FormatTmpPath(char *aBuf, unsigned BufSize, const char *pPath);