mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge #6199
6199: Register protocol and file extensions on client launch on Windows r=def- a=Robyt3 When launching the client on Windows, associate the protocol `ddnet` and the file extensions `.map` and `.demo` with the client executable. See #6072. ## 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:
commit
b252f2e344
|
@ -58,7 +58,9 @@
|
||||||
#elif defined(CONF_FAMILY_WINDOWS)
|
#elif defined(CONF_FAMILY_WINDOWS)
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#undef _WIN32_WINNT
|
#undef _WIN32_WINNT
|
||||||
#define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */
|
// 0x0501 (Windows XP) is required for mingw to get getaddrinfo to work
|
||||||
|
// 0x0600 (Windows Vista) is required to use RegGetValueW and RegDeleteTreeW
|
||||||
|
#define _WIN32_WINNT 0x0600
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
@ -70,6 +72,7 @@
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <share.h>
|
#include <share.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <shlobj.h> // SHChangeNotify
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
#else
|
#else
|
||||||
|
@ -1436,7 +1439,7 @@ static int priv_net_close_all_sockets(NETSOCKET sock)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONF_FAMILY_WINDOWS)
|
#if defined(CONF_FAMILY_WINDOWS)
|
||||||
static char *windows_format_system_message(int error)
|
static char *windows_format_system_message(unsigned long error)
|
||||||
{
|
{
|
||||||
WCHAR *wide_message;
|
WCHAR *wide_message;
|
||||||
const DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
const DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK;
|
||||||
|
@ -4302,6 +4305,282 @@ CWindowsComLifecycle::~CWindowsComLifecycle()
|
||||||
{
|
{
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void windows_print_error(const char *system, const char *prefix, HRESULT error)
|
||||||
|
{
|
||||||
|
char *message = windows_format_system_message(error);
|
||||||
|
dbg_msg(system, "%s: %s", prefix, message == nullptr ? "unknown error" : message);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::wstring utf8_to_wstring(const char *str)
|
||||||
|
{
|
||||||
|
const int orig_length = str_length(str);
|
||||||
|
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, orig_length, NULL, 0);
|
||||||
|
std::wstring wide_string(size_needed, '\0');
|
||||||
|
dbg_assert(MultiByteToWideChar(CP_UTF8, 0, str, orig_length, &wide_string[0], size_needed) == size_needed, "MultiByteToWideChar failure");
|
||||||
|
return wide_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shell_register_protocol(const char *protocol_name, const char *executable, bool *updated)
|
||||||
|
{
|
||||||
|
const std::wstring protocol_name_wide = utf8_to_wstring(protocol_name);
|
||||||
|
const std::wstring executable_wide = utf8_to_wstring(executable);
|
||||||
|
|
||||||
|
// Open registry key for protocol associations of the current user
|
||||||
|
HKEY handle_subkey_classes;
|
||||||
|
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_register_protocol", "Error opening registry key", result_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the protocol key
|
||||||
|
HKEY handle_subkey_protocol;
|
||||||
|
const LRESULT result_subkey_protocol = RegCreateKeyExW(handle_subkey_classes, protocol_name_wide.c_str(), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_protocol, NULL);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
if(result_subkey_protocol != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error creating registry key", result_subkey_protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value for the key, which specifies the name of the display name of the protocol
|
||||||
|
const std::wstring value_protocol = L"URL:" + protocol_name_wide + L" Protocol";
|
||||||
|
const LRESULT result_value_protocol = RegSetValueExW(handle_subkey_protocol, L"", 0, REG_SZ, (BYTE *)value_protocol.c_str(), (value_protocol.length() + 1) * sizeof(wchar_t));
|
||||||
|
if(result_value_protocol != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error setting registry value", result_value_protocol);
|
||||||
|
RegCloseKey(handle_subkey_protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the "URL Protocol" value, to specify that this key describes a URL protocol
|
||||||
|
const LRESULT result_value_empty = RegSetValueEx(handle_subkey_protocol, L"URL Protocol", 0, REG_SZ, (BYTE *)L"", sizeof(wchar_t));
|
||||||
|
if(result_value_empty != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error setting registry value", result_value_empty);
|
||||||
|
RegCloseKey(handle_subkey_protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the "DefaultIcon" subkey
|
||||||
|
HKEY handle_subkey_icon;
|
||||||
|
const LRESULT result_subkey_icon = RegCreateKeyExW(handle_subkey_protocol, L"DefaultIcon", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_icon, NULL);
|
||||||
|
if(result_subkey_icon != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error creating registry key", result_subkey_icon);
|
||||||
|
RegCloseKey(handle_subkey_protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value for the key, which specifies the icon associated with the protocol
|
||||||
|
const std::wstring value_icon = L"\"" + executable_wide + L"\",0";
|
||||||
|
const LRESULT result_value_icon = RegSetValueExW(handle_subkey_icon, L"", 0, REG_SZ, (BYTE *)value_icon.c_str(), (value_icon.length() + 1) * sizeof(wchar_t));
|
||||||
|
RegCloseKey(handle_subkey_icon);
|
||||||
|
if(result_value_icon != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error setting registry value", result_value_icon);
|
||||||
|
RegCloseKey(handle_subkey_protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the "shell\open\command" subkeys
|
||||||
|
HKEY handle_subkey_shell_open_command;
|
||||||
|
const LRESULT result_subkey_shell_open_command = RegCreateKeyExW(handle_subkey_protocol, L"shell\\open\\command", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_shell_open_command, NULL);
|
||||||
|
RegCloseKey(handle_subkey_protocol);
|
||||||
|
if(result_subkey_shell_open_command != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error creating registry key", result_subkey_shell_open_command);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the previous default value for the key, so we can determine if it changed
|
||||||
|
wchar_t old_value_executable[MAX_PATH + 16];
|
||||||
|
DWORD old_size_executable = sizeof(old_value_executable);
|
||||||
|
const LRESULT result_old_value_executable = RegGetValueW(handle_subkey_shell_open_command, NULL, L"", RRF_RT_REG_SZ, NULL, (BYTE *)old_value_executable, &old_size_executable);
|
||||||
|
const std::wstring value_executable = L"\"" + executable_wide + L"\" \"%1\"";
|
||||||
|
if(result_old_value_executable != ERROR_SUCCESS || wcscmp(old_value_executable, value_executable.c_str()) != 0)
|
||||||
|
{
|
||||||
|
// Set the default value for the key, which specifies the executable command associated with the protocol
|
||||||
|
const LRESULT result_value_executable = RegSetValueExW(handle_subkey_shell_open_command, L"", 0, REG_SZ, (BYTE *)value_executable.c_str(), (value_executable.length() + 1) * sizeof(wchar_t));
|
||||||
|
RegCloseKey(handle_subkey_shell_open_command);
|
||||||
|
if(result_value_executable != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_protocol", "Error setting registry value", result_value_executable);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shell_register_extension(const char *extension, const char *description, const char *executable_name, const char *executable, bool *updated)
|
||||||
|
{
|
||||||
|
const std::wstring extension_wide = utf8_to_wstring(extension);
|
||||||
|
const std::wstring executable_name_wide = utf8_to_wstring(executable_name);
|
||||||
|
const std::wstring description_wide = executable_name_wide + L" " + utf8_to_wstring(description);
|
||||||
|
const std::wstring program_id_wide = executable_name_wide + extension_wide;
|
||||||
|
const std::wstring executable_wide = utf8_to_wstring(executable);
|
||||||
|
|
||||||
|
// Open registry key for file associations of the current user
|
||||||
|
HKEY handle_subkey_classes;
|
||||||
|
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_register_extension", "Error opening registry key", result_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the program ID key
|
||||||
|
HKEY handle_subkey_program_id;
|
||||||
|
const LRESULT result_subkey_program_id = RegCreateKeyExW(handle_subkey_classes, program_id_wide.c_str(), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_program_id, NULL);
|
||||||
|
if(result_subkey_program_id != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error creating registry key", result_subkey_program_id);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value for the key, which specifies the file type description for legacy applications
|
||||||
|
const LRESULT result_description_default = RegSetValueExW(handle_subkey_program_id, L"", 0, REG_SZ, (BYTE *)description_wide.c_str(), (description_wide.length() + 1) * sizeof(wchar_t));
|
||||||
|
if(result_description_default != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error setting registry value", result_description_default);
|
||||||
|
RegCloseKey(handle_subkey_program_id);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the "FriendlyTypeName" value, which specifies the file type description for modern applications
|
||||||
|
const LRESULT result_description_friendly = RegSetValueExW(handle_subkey_program_id, L"FriendlyTypeName", 0, REG_SZ, (BYTE *)description_wide.c_str(), (description_wide.length() + 1) * sizeof(wchar_t));
|
||||||
|
if(result_description_friendly != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error setting registry value", result_description_friendly);
|
||||||
|
RegCloseKey(handle_subkey_program_id);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the "DefaultIcon" subkey
|
||||||
|
HKEY handle_subkey_icon;
|
||||||
|
const LRESULT result_subkey_icon = RegCreateKeyExW(handle_subkey_program_id, L"DefaultIcon", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_icon, NULL);
|
||||||
|
if(result_subkey_icon != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("register_protocol", "Error creating registry key", result_subkey_icon);
|
||||||
|
RegCloseKey(handle_subkey_program_id);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default value for the key, which specifies the icon associated with the program ID
|
||||||
|
const std::wstring value_icon = L"\"" + executable_wide + L"\",0";
|
||||||
|
const LRESULT result_value_icon = RegSetValueExW(handle_subkey_icon, L"", 0, REG_SZ, (BYTE *)value_icon.c_str(), (value_icon.length() + 1) * sizeof(wchar_t));
|
||||||
|
RegCloseKey(handle_subkey_icon);
|
||||||
|
if(result_value_icon != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("register_protocol", "Error setting registry value", result_value_icon);
|
||||||
|
RegCloseKey(handle_subkey_program_id);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the "shell\open\command" subkeys
|
||||||
|
HKEY handle_subkey_shell_open_command;
|
||||||
|
const LRESULT result_subkey_shell_open_command = RegCreateKeyExW(handle_subkey_program_id, L"shell\\open\\command", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_shell_open_command, NULL);
|
||||||
|
RegCloseKey(handle_subkey_program_id);
|
||||||
|
if(result_subkey_shell_open_command != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error creating registry key", result_subkey_shell_open_command);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the previous default value for the key, so we can determine if it changed
|
||||||
|
wchar_t old_value_executable[MAX_PATH + 16];
|
||||||
|
DWORD old_size_executable = sizeof(old_value_executable);
|
||||||
|
const LRESULT result_old_value_executable = RegGetValueW(handle_subkey_shell_open_command, NULL, L"", RRF_RT_REG_SZ, NULL, (BYTE *)old_value_executable, &old_size_executable);
|
||||||
|
const std::wstring value_executable = L"\"" + executable_wide + L"\" \"%1\"";
|
||||||
|
if(result_old_value_executable != ERROR_SUCCESS || wcscmp(old_value_executable, value_executable.c_str()) != 0)
|
||||||
|
{
|
||||||
|
// Set the default value for the key, which specifies the executable command associated with the application
|
||||||
|
const LRESULT result_value_executable = RegSetValueExW(handle_subkey_shell_open_command, L"", 0, REG_SZ, (BYTE *)value_executable.c_str(), (value_executable.length() + 1) * sizeof(wchar_t));
|
||||||
|
RegCloseKey(handle_subkey_shell_open_command);
|
||||||
|
if(result_value_executable != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error setting registry value", result_value_executable);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the file extension key
|
||||||
|
HKEY handle_subkey_extension;
|
||||||
|
const LRESULT result_subkey_extension = RegCreateKeyExW(handle_subkey_classes, extension_wide.c_str(), 0, NULL, 0, KEY_ALL_ACCESS, NULL, &handle_subkey_extension, NULL);
|
||||||
|
RegCloseKey(handle_subkey_classes);
|
||||||
|
if(result_subkey_extension != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error creating registry key", result_subkey_extension);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the previous default value for the key, so we can determine if it changed
|
||||||
|
wchar_t old_value_application[128];
|
||||||
|
DWORD old_size_application = sizeof(old_value_application);
|
||||||
|
const LRESULT result_old_value_application = RegGetValueW(handle_subkey_extension, NULL, L"", RRF_RT_REG_SZ, NULL, (BYTE *)old_value_application, &old_size_application);
|
||||||
|
if(result_old_value_application != ERROR_SUCCESS || wcscmp(old_value_application, program_id_wide.c_str()) != 0)
|
||||||
|
{
|
||||||
|
// Set the default value for the key, which associates the file extension with the program ID
|
||||||
|
const LRESULT result_value_application = RegSetValueExW(handle_subkey_extension, L"", 0, REG_SZ, (BYTE *)program_id_wide.c_str(), (program_id_wide.length() + 1) * sizeof(wchar_t));
|
||||||
|
RegCloseKey(handle_subkey_extension);
|
||||||
|
if(result_value_application != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_register_extension", "Error setting registry value", result_value_application);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shell_unregister(const char *shell_class, bool *updated)
|
||||||
|
{
|
||||||
|
const std::wstring class_wide = utf8_to_wstring(shell_class);
|
||||||
|
|
||||||
|
// Open registry key for protocol and file associations of the current user
|
||||||
|
HKEY handle_subkey_classes;
|
||||||
|
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);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
windows_print_error("shell_unregister", "Error deleting registry key", result_delete);
|
||||||
|
if(result_delete == ERROR_SUCCESS)
|
||||||
|
*updated = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shell_update()
|
||||||
|
{
|
||||||
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t std::hash<NETADDR>::operator()(const NETADDR &Addr) const noexcept
|
size_t std::hash<NETADDR>::operator()(const NETADDR &Addr) const noexcept
|
||||||
|
|
|
@ -2581,6 +2581,63 @@ public:
|
||||||
CWindowsComLifecycle(bool HasWindow);
|
CWindowsComLifecycle(bool HasWindow);
|
||||||
~CWindowsComLifecycle();
|
~CWindowsComLifecycle();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a protocol handler.
|
||||||
|
*
|
||||||
|
* @ingroup Shell
|
||||||
|
*
|
||||||
|
* @param protocol_name The name of the protocol.
|
||||||
|
* @param executable The absolute path of the executable that will be associated with the protocol.
|
||||||
|
* @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_protocol(const char *protocol_name, const char *executable, bool *updated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a file extension.
|
||||||
|
*
|
||||||
|
* @ingroup Shell
|
||||||
|
*
|
||||||
|
* @param extension The file extension, including the leading dot.
|
||||||
|
* @param description A readable description for the file extension.
|
||||||
|
* @param executable_name A unique name that will used to describe the application.
|
||||||
|
* @param executable The absolute path of the executable that will be associated with the file extension.
|
||||||
|
* @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_extension(const char *extension, const char *description, const char *executable_name, const char *executable, bool *updated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters a protocol or file extension handler.
|
||||||
|
*
|
||||||
|
* @ingroup Shell
|
||||||
|
*
|
||||||
|
* @param shell_class The shell class to delete.
|
||||||
|
* For protocols this is the name of the protocol.
|
||||||
|
* For file extensions this is the program ID associated with the file extension.
|
||||||
|
* @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(const char *shell_class, bool *updated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the system that a protocol or file extension has been changed and the shell needs to be updated.
|
||||||
|
*
|
||||||
|
* @ingroup Shell
|
||||||
|
*
|
||||||
|
* @remark This is a potentially expensive operation, so it should only be called when necessary.
|
||||||
|
*/
|
||||||
|
void shell_update();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -280,6 +280,11 @@ public:
|
||||||
virtual CChecksumData *ChecksumData() = 0;
|
virtual CChecksumData *ChecksumData() = 0;
|
||||||
virtual bool InfoTaskRunning() = 0;
|
virtual bool InfoTaskRunning() = 0;
|
||||||
virtual int UdpConnectivity(int NetType) = 0;
|
virtual int UdpConnectivity(int NetType) = 0;
|
||||||
|
|
||||||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||||||
|
virtual void ShellRegister() = 0;
|
||||||
|
virtual void ShellUnregister() = 0;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
class IGameClient : public IInterface
|
class IGameClient : public IInterface
|
||||||
|
|
|
@ -4704,6 +4704,11 @@ int main(int argc, const char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register protocol and file extensions
|
||||||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||||||
|
pClient->ShellRegister();
|
||||||
|
#endif
|
||||||
|
|
||||||
// init SDL
|
// init SDL
|
||||||
if(SDL_Init(0) < 0)
|
if(SDL_Init(0) < 0)
|
||||||
{
|
{
|
||||||
|
@ -4897,3 +4902,53 @@ int CClient::UdpConnectivity(int NetType)
|
||||||
}
|
}
|
||||||
return Connectivity;
|
return Connectivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
if(!aFullPath[0])
|
||||||
|
{
|
||||||
|
dbg_msg("client", "Failed to register protocol and file extensions: could not determine absolute path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Updated = false;
|
||||||
|
if(!shell_register_protocol("ddnet", aFullPath, &Updated))
|
||||||
|
dbg_msg("client", "Failed to register ddnet protocol");
|
||||||
|
if(!shell_register_extension(".map", "Map File", GAME_NAME, aFullPath, &Updated))
|
||||||
|
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(Updated)
|
||||||
|
shell_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClient::ShellUnregister()
|
||||||
|
{
|
||||||
|
bool Updated = false;
|
||||||
|
if(!shell_unregister("ddnet", &Updated))
|
||||||
|
dbg_msg("client", "Failed to unregister ddnet protocol");
|
||||||
|
if(!shell_unregister(GAME_NAME ".map", &Updated))
|
||||||
|
dbg_msg("client", "Failed to unregister .map file extension");
|
||||||
|
if(!shell_unregister(GAME_NAME ".demo", &Updated))
|
||||||
|
dbg_msg("client", "Failed to unregister .demo file extension");
|
||||||
|
if(Updated)
|
||||||
|
shell_update();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -546,6 +546,11 @@ public:
|
||||||
CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
|
CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
|
||||||
bool InfoTaskRunning() override { return m_pDDNetInfoTask != nullptr; }
|
bool InfoTaskRunning() override { return m_pDDNetInfoTask != nullptr; }
|
||||||
int UdpConnectivity(int NetType) override;
|
int UdpConnectivity(int NetType) override;
|
||||||
|
|
||||||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||||||
|
void ShellRegister() override;
|
||||||
|
void ShellUnregister() override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3433,6 +3433,17 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
|
||||||
SUIExEditBoxProperties EditProps;
|
SUIExEditBoxProperties EditProps;
|
||||||
EditProps.m_pEmptyText = Localize("Chat command (e.g. showall 1)");
|
EditProps.m_pEmptyText = Localize("Chat command (e.g. showall 1)");
|
||||||
UI()->DoEditBox(g_Config.m_ClRunOnJoin, &Button, g_Config.m_ClRunOnJoin, sizeof(g_Config.m_ClRunOnJoin), 14.0f, &s_RunOnJoin, false, IGraphics::CORNER_ALL, EditProps);
|
UI()->DoEditBox(g_Config.m_ClRunOnJoin, &Button, g_Config.m_ClRunOnJoin, sizeof(g_Config.m_ClRunOnJoin), 14.0f, &s_RunOnJoin, false, IGraphics::CORNER_ALL, EditProps);
|
||||||
|
|
||||||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||||||
|
static CButtonContainer s_ButtonUnregisterShell;
|
||||||
|
Right.HSplitTop(10.0f, nullptr, &Right);
|
||||||
|
Right.HSplitTop(20.0f, &Button, &Right);
|
||||||
|
if(DoButton_Menu(&s_ButtonUnregisterShell, Localize("Unregister protocol and file extensions"), 0, &Button))
|
||||||
|
{
|
||||||
|
Client()->ShellUnregister();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Updater
|
// Updater
|
||||||
#if defined(CONF_AUTOUPDATE)
|
#if defined(CONF_AUTOUPDATE)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue