1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 struct RegistryKeyWrapper
27 {
RegistryKeyWrapperjuce::RegistryKeyWrapper28     RegistryKeyWrapper (String name, bool createForWriting, DWORD wow64Flags)
29     {
30         if (HKEY rootKey = getRootKey (name))
31         {
32             name = name.substring (name.indexOfChar ('\\') + 1);
33 
34             auto lastSlash = name.lastIndexOfChar ('\\');
35             valueName = name.substring (lastSlash + 1);
36             wideCharValueName = valueName.toWideCharPointer();
37 
38             name = name.substring (0, lastSlash);
39             auto wideCharName = name.toWideCharPointer();
40             DWORD result;
41 
42             if (createForWriting)
43                 RegCreateKeyEx (rootKey, wideCharName, 0, 0, REG_OPTION_NON_VOLATILE,
44                                 KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result);
45             else
46                 RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key);
47         }
48     }
49 
~RegistryKeyWrapperjuce::RegistryKeyWrapper50     ~RegistryKeyWrapper()
51     {
52         if (key != nullptr)
53             RegCloseKey (key);
54     }
55 
getRootKeyjuce::RegistryKeyWrapper56     static HKEY getRootKey (const String& name) noexcept
57     {
58         if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\"))  return HKEY_CURRENT_USER;
59         if (name.startsWithIgnoreCase ("HKCU\\"))               return HKEY_CURRENT_USER;
60         if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) return HKEY_LOCAL_MACHINE;
61         if (name.startsWithIgnoreCase ("HKLM\\"))               return HKEY_LOCAL_MACHINE;
62         if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\"))  return HKEY_CLASSES_ROOT;
63         if (name.startsWithIgnoreCase ("HKCR\\"))               return HKEY_CLASSES_ROOT;
64         if (name.startsWithIgnoreCase ("HKEY_USERS\\"))         return HKEY_USERS;
65         if (name.startsWithIgnoreCase ("HKU\\"))                return HKEY_USERS;
66 
67         jassertfalse; // The name starts with an unknown root key (or maybe an old Win9x type)
68         return 0;
69     }
70 
setValuejuce::RegistryKeyWrapper71     static bool setValue (const String& regValuePath, const DWORD type,
72                           const void* data, size_t dataSize, const DWORD wow64Flags)
73     {
74         const RegistryKeyWrapper key (regValuePath, true, wow64Flags);
75 
76         return key.key != nullptr
77                 && RegSetValueEx (key.key, key.wideCharValueName, 0, type,
78                                   reinterpret_cast<const BYTE*> (data),
79                                   (DWORD) dataSize) == ERROR_SUCCESS;
80     }
81 
getBinaryValuejuce::RegistryKeyWrapper82     static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& result, DWORD wow64Flags)
83     {
84         const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
85 
86         if (key.key != nullptr)
87         {
88             for (unsigned long bufferSize = 1024; ; bufferSize *= 2)
89             {
90                 result.setSize (bufferSize, false);
91                 DWORD type = REG_NONE;
92 
93                 auto err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &type,
94                                             (LPBYTE) result.getData(), &bufferSize);
95 
96                 if (err == ERROR_SUCCESS)
97                 {
98                     result.setSize (bufferSize, false);
99                     return type;
100                 }
101 
102                 if (err != ERROR_MORE_DATA)
103                     break;
104             }
105         }
106 
107         return REG_NONE;
108     }
109 
getValuejuce::RegistryKeyWrapper110     static String getValue (const String& regValuePath, const String& defaultValue, DWORD wow64Flags)
111     {
112         MemoryBlock buffer;
113 
114         switch (getBinaryValue (regValuePath, buffer, wow64Flags))
115         {
116             case REG_SZ:    return static_cast<const WCHAR*> (buffer.getData());
117             case REG_DWORD: return String ((int) *reinterpret_cast<const DWORD*> (buffer.getData()));
118             default:        break;
119         }
120 
121         return defaultValue;
122     }
123 
keyExistsjuce::RegistryKeyWrapper124     static bool keyExists (const String& regKeyPath, const DWORD wow64Flags)
125     {
126         return RegistryKeyWrapper (regKeyPath + "\\", false, wow64Flags).key != nullptr;
127     }
128 
valueExistsjuce::RegistryKeyWrapper129     static bool valueExists (const String& regValuePath, const DWORD wow64Flags)
130     {
131         const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
132 
133         if (key.key == nullptr)
134             return false;
135 
136         unsigned char buffer [512];
137         unsigned long bufferSize = sizeof (buffer);
138         DWORD type = 0;
139 
140         auto result = RegQueryValueEx (key.key, key.wideCharValueName,
141                                        0, &type, buffer, &bufferSize);
142 
143         return result == ERROR_SUCCESS || result == ERROR_MORE_DATA;
144     }
145 
146     HKEY key = nullptr;
147     const wchar_t* wideCharValueName = nullptr;
148     String valueName;
149 
150     JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper)
151 };
152 
getBinaryValue(const String & regValuePath,MemoryBlock & result,WoW64Mode mode)153 uint32 JUCE_CALLTYPE WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result, WoW64Mode mode)
154 {
155     return RegistryKeyWrapper::getBinaryValue (regValuePath, result, (DWORD) mode);
156 }
157 
getValue(const String & regValuePath,const String & defaultValue,WoW64Mode mode)158 String JUCE_CALLTYPE WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue, WoW64Mode mode)
159 {
160     return RegistryKeyWrapper::getValue (regValuePath, defaultValue, (DWORD) mode);
161 }
162 
setValue(const String & regValuePath,const String & value,WoW64Mode mode)163 bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const String& value, WoW64Mode mode)
164 {
165     return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(),
166                                          CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer()), mode);
167 }
168 
setValue(const String & regValuePath,const uint32 value,WoW64Mode mode)169 bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint32 value, WoW64Mode mode)
170 {
171     return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value), (DWORD) mode);
172 }
173 
setValue(const String & regValuePath,const uint64 value,WoW64Mode mode)174 bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint64 value, WoW64Mode mode)
175 {
176     return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value), (DWORD) mode);
177 }
178 
setValue(const String & regValuePath,const MemoryBlock & value,WoW64Mode mode)179 bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode)
180 {
181     return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize(), (DWORD) mode);
182 }
183 
valueExists(const String & regValuePath,WoW64Mode mode)184 bool JUCE_CALLTYPE WindowsRegistry::valueExists (const String& regValuePath, WoW64Mode mode)
185 {
186     return RegistryKeyWrapper::valueExists (regValuePath, (DWORD) mode);
187 }
188 
keyExists(const String & regKeyPath,WoW64Mode mode)189 bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regKeyPath, WoW64Mode mode)
190 {
191     return RegistryKeyWrapper::keyExists (regKeyPath, (DWORD) mode);
192 }
193 
deleteValue(const String & regValuePath,WoW64Mode mode)194 bool JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode)
195 {
196     const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode);
197 
198     return key.key != nullptr && RegDeleteValue (key.key, key.wideCharValueName) == ERROR_SUCCESS;
199 }
200 
deleteKeyNonRecursive(const String & regKeyPath,WindowsRegistry::WoW64Mode mode)201 static bool deleteKeyNonRecursive (const String& regKeyPath, WindowsRegistry::WoW64Mode mode)
202 {
203     const RegistryKeyWrapper key (regKeyPath, true, (DWORD) mode);
204 
205     return key.key != nullptr && RegDeleteKey (key.key, key.wideCharValueName) == ERROR_SUCCESS;
206 }
207 
deleteKey(const String & regKeyPath,WoW64Mode mode)208 bool JUCE_CALLTYPE WindowsRegistry::deleteKey (const String& regKeyPath, WoW64Mode mode)
209 {
210     if (deleteKeyNonRecursive (regKeyPath, mode))
211         return true;
212 
213     for (const RegistryKeyWrapper key (regKeyPath + "\\", false, (DWORD) mode);;)
214     {
215         wchar_t subKey[MAX_PATH + 1] = {};
216         DWORD subKeySize = MAX_PATH;
217 
218         if (RegEnumKeyEx (key.key, 0, subKey, &subKeySize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS
219              || ! deleteKey (regKeyPath + "\\" + String (subKey), mode))
220             break;
221     }
222 
223     return deleteKeyNonRecursive (regKeyPath, mode);
224 }
225 
registerFileAssociation(const String & fileExtension,const String & symbolicDescription,const String & fullDescription,const File & targetExecutable,const int iconResourceNumber,const bool registerForCurrentUserOnly,WoW64Mode mode)226 bool JUCE_CALLTYPE WindowsRegistry::registerFileAssociation (const String& fileExtension,
227                                                              const String& symbolicDescription,
228                                                              const String& fullDescription,
229                                                              const File& targetExecutable,
230                                                              const int iconResourceNumber,
231                                                              const bool registerForCurrentUserOnly,
232                                                              WoW64Mode mode)
233 {
234     auto root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\"
235                                            : "HKEY_CLASSES_ROOT\\";
236     auto key = root + symbolicDescription;
237 
238     return setValue (root + fileExtension + "\\", symbolicDescription, mode)
239         && setValue (key + "\\", fullDescription, mode)
240         && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", mode)
241         && (iconResourceNumber == 0
242               || setValue (key + "\\DefaultIcon\\",
243                            targetExecutable.getFullPathName() + "," + String (iconResourceNumber)));
244 }
245 
246 // These methods are deprecated:
getValueWow64(const String & p,const String & defVal)247 String WindowsRegistry::getValueWow64 (const String& p, const String& defVal)  { return getValue (p, defVal, WoW64_64bit); }
valueExistsWow64(const String & p)248 bool WindowsRegistry::valueExistsWow64 (const String& p)                       { return valueExists (p, WoW64_64bit); }
keyExistsWow64(const String & p)249 bool WindowsRegistry::keyExistsWow64 (const String& p)                         { return keyExists (p, WoW64_64bit); }
250 
251 } // namespace juce
252