diff --git a/src/base/system.cpp b/src/base/system.cpp index 0be3e07d2..bf282fbe4 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -58,7 +58,9 @@ #elif defined(CONF_FAMILY_WINDOWS) #define WIN32_LEAN_AND_MEAN #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 +#define _WIN32_WINNT 0x0600 #include #include #include @@ -70,6 +72,7 @@ #include #include #include +#include // SHChangeNotify #include #include #else @@ -4321,7 +4324,7 @@ static std::wstring utf8_to_wstring(const char *str) return wide_string; } -bool shell_register_protocol(const char *protocol_name, const char *executable) +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); @@ -4395,20 +4398,29 @@ bool shell_register_protocol(const char *protocol_name, const char *executable) return false; } - // Set the default value for the key, which specifies the executable command associated with the protocol + // 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\""; - 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) + if(result_old_value_executable != ERROR_SUCCESS || wcscmp(old_value_executable, value_executable.c_str()) != 0) { - windows_print_error("shell_register_protocol", "Error setting registry value", result_value_executable); - return false; + // 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 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); @@ -4489,15 +4501,24 @@ bool shell_register_extension(const char *extension, const char *description, co return false; } - // Set the default value for the key, which specifies the executable command associated with the application + // 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\""; - 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) + if(result_old_value_executable != ERROR_SUCCESS || wcscmp(old_value_executable, value_executable.c_str()) != 0) { - windows_print_error("shell_register_extension", "Error setting registry value", result_value_executable); - RegCloseKey(handle_subkey_classes); - return false; + // 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 @@ -4510,17 +4531,31 @@ bool shell_register_extension(const char *extension, const char *description, co return false; } - // 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) + // 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) { - windows_print_error("shell_register_extension", "Error setting registry value", result_value_application); - return false; + // 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; } + +void shell_update() +{ + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); +} #endif size_t std::hash::operator()(const NETADDR &Addr) const noexcept diff --git a/src/base/system.h b/src/base/system.h index b9b220f2d..27195e22e 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -2578,10 +2578,13 @@ public: * * @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 shell_register_protocol(const char *protocol_name, const char *executable, bool *updated); /** * Registers a file extension. @@ -2592,10 +2595,22 @@ bool shell_register_protocol(const char *protocol_name, const char *executable); * @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 shell_register_extension(const char *extension, const char *description, const char *executable_name, const char *executable, 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 /**