/ src / discord_register_win.cpp
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  }