discord_register_win.cpp
1 #include "discord_rpc.h" 2 #include "discord_register.h" 3 4 #define WIN32_LEAN_AND_MEAN 5 #define NOMCX 6 #define NOSERVICE 7 #define NOIME 8 #include <windows.h> 9 #include <psapi.h> 10 #include <cstdio> 11 12 /** 13 * Updated fixes for MinGW and WinXP 14 * This block is written the way it does not involve changing the rest of the code 15 * Checked to be compiling 16 * 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW 17 * #include guarded, functions redirected to <string.h> substitutes 18 * 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h> 19 * The entire function is rewritten 20 */ 21 #ifdef __MINGW32__ 22 #include <wchar.h> 23 /// strsafe.h fixes 24 static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...) 25 { 26 HRESULT ret; 27 va_list va; 28 va_start(va, pszFormat); 29 cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault 30 // othervise 31 ret = vsnwprintf(pszDest, cbDest, pszFormat, va); 32 pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned 33 va_end(va); 34 return ret; 35 } 36 #else 37 #include <cwchar> 38 #include <strsafe.h> 39 #endif // __MINGW32__ 40 41 /// winreg.h fixes 42 #ifndef LSTATUS 43 #define LSTATUS LONG 44 #endif 45 #ifdef RegSetKeyValueW 46 #undefine RegSetKeyValueW 47 #endif 48 #define RegSetKeyValueW regset 49 static LSTATUS regset(HKEY hkey, 50 LPCWSTR subkey, 51 LPCWSTR name, 52 DWORD type, 53 const void* data, 54 DWORD len) 55 { 56 HKEY htkey = hkey, hsubkey = nullptr; 57 LSTATUS ret; 58 if (subkey && subkey[0]) { 59 if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) != 60 ERROR_SUCCESS) 61 return ret; 62 htkey = hsubkey; 63 } 64 ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len); 65 if (hsubkey && hsubkey != hkey) 66 RegCloseKey(hsubkey); 67 return ret; 68 } 69 70 static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command) 71 { 72 // https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx 73 // we want to register games so we can run them as discord-<appid>:// 74 // Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions. 75 76 wchar_t exeFilePath[MAX_PATH]; 77 DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH); 78 wchar_t openCommand[1024]; 79 80 if (command && command[0]) { 81 StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command); 82 } 83 else { 84 // StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath); 85 StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath); 86 } 87 88 wchar_t protocolName[64]; 89 StringCbPrintfW(protocolName, sizeof(protocolName), L"discord-%s", applicationId); 90 wchar_t protocolDescription[128]; 91 StringCbPrintfW( 92 protocolDescription, sizeof(protocolDescription), L"URL:Run game %s protocol", applicationId); 93 wchar_t urlProtocol = 0; 94 95 wchar_t keyName[256]; 96 StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%s", protocolName); 97 HKEY key; 98 auto status = 99 RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr); 100 if (status != ERROR_SUCCESS) { 101 fprintf(stderr, "Error creating key\n"); 102 return; 103 } 104 DWORD len; 105 LSTATUS result; 106 len = (DWORD)lstrlenW(protocolDescription) + 1; 107 result = 108 RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t)); 109 if (FAILED(result)) { 110 fprintf(stderr, "Error writing description\n"); 111 } 112 113 len = (DWORD)lstrlenW(protocolDescription) + 1; 114 result = RegSetKeyValueW(key, nullptr, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t)); 115 if (FAILED(result)) { 116 fprintf(stderr, "Error writing description\n"); 117 } 118 119 result = RegSetKeyValueW( 120 key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t)); 121 if (FAILED(result)) { 122 fprintf(stderr, "Error writing icon\n"); 123 } 124 125 len = (DWORD)lstrlenW(openCommand) + 1; 126 result = RegSetKeyValueW( 127 key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t)); 128 if (FAILED(result)) { 129 fprintf(stderr, "Error writing command\n"); 130 } 131 RegCloseKey(key); 132 } 133 134 extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command) 135 { 136 wchar_t appId[32]; 137 MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); 138 139 wchar_t openCommand[1024]; 140 const wchar_t* wcommand = nullptr; 141 if (command && command[0]) { 142 const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand); 143 MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen); 144 wcommand = openCommand; 145 } 146 147 Discord_RegisterW(appId, wcommand); 148 } 149 150 extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, 151 const char* steamId) 152 { 153 wchar_t appId[32]; 154 MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32); 155 156 wchar_t wSteamId[32]; 157 MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32); 158 159 HKEY key; 160 auto status = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_READ, &key); 161 if (status != ERROR_SUCCESS) { 162 fprintf(stderr, "Error opening Steam key\n"); 163 return; 164 } 165 166 wchar_t steamPath[MAX_PATH]; 167 DWORD pathBytes = sizeof(steamPath); 168 status = RegQueryValueExW(key, L"SteamExe", nullptr, nullptr, (BYTE*)steamPath, &pathBytes); 169 RegCloseKey(key); 170 if (status != ERROR_SUCCESS || pathBytes < 1) { 171 fprintf(stderr, "Error reading SteamExe key\n"); 172 return; 173 } 174 175 DWORD pathChars = pathBytes / sizeof(wchar_t); 176 for (DWORD i = 0; i < pathChars; ++i) { 177 if (steamPath[i] == L'/') { 178 steamPath[i] = L'\\'; 179 } 180 } 181 182 wchar_t command[1024]; 183 StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://rungameid/%s", steamPath, wSteamId); 184 185 Discord_RegisterW(appId, command); 186 }