Left: | ||
Right: |
OLD | NEW |
---|---|
1 #include <memory> | 1 #include <memory> |
2 #include <stdexcept> | 2 #include <stdexcept> |
3 #include <vector> | 3 #include <vector> |
4 | 4 |
5 #include <Windows.h> | 5 #include <Windows.h> |
6 #include <ShlObj.h> | 6 #include <ShlObj.h> |
7 | 7 |
8 #include "Utils.h" | 8 #include "Utils.h" |
9 | 9 |
10 namespace | 10 namespace |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
121 } | 121 } |
122 | 122 |
123 void ReplaceString(std::wstring& input, const std::wstring placeholder, const st d::wstring replacement) | 123 void ReplaceString(std::wstring& input, const std::wstring placeholder, const st d::wstring replacement) |
124 { | 124 { |
125 size_t replaceStart = input.find(placeholder); | 125 size_t replaceStart = input.find(placeholder); |
126 if (replaceStart != std::string::npos) | 126 if (replaceStart != std::string::npos) |
127 { | 127 { |
128 input.replace(replaceStart, placeholder.length(), replacement); | 128 input.replace(replaceStart, placeholder.length(), replacement); |
129 } | 129 } |
130 } | 130 } |
131 | |
132 namespace | |
133 { | |
134 /** | |
135 * An open key in the system registry. | |
136 */ | |
137 class Registry_Key | |
Oleksandr
2014/07/15 18:25:11
I'd vote to move class definition to Utils.h and l
Eric
2014/07/20 18:18:46
We actually need a more radical organization than
| |
138 { | |
139 /** | |
140 * Handle to an open registry key. | |
141 */ | |
142 HKEY key; | |
143 | |
144 /** | |
145 * Flag whether the registry key is predefined or not. | |
146 * | |
147 * Predefined key values are considered "always open" and should not be clos ed in the destructor. | |
148 */ | |
149 bool is_predefined; | |
150 | |
151 public: | |
152 /** | |
153 * Enumeration for predefined root keys. | |
154 */ | |
155 enum class Predefined | |
156 : unsigned int | |
Oleksandr
2014/07/15 18:25:11
I don't understand the concept behind this class.
Eric
2014/07/20 18:18:46
The way the Windows registry API works, you must a
Oleksandr
2014/07/25 22:23:46
I think this is an overkill. I'd be ok if we assum
Eric
2014/07/26 15:54:45
I'm inclined to agree with you for practical reaso
| |
157 { | |
158 HKCR = 0, // HKEY_CLASSES_ROOT | |
159 HKCC, // HKEY_CURRENT_CONFIG | |
160 HKCU, // HKEY_CURRENT_USER | |
161 HKLM, // HKEY_LOCAL_MACHINE | |
162 HKU // HKEY_USERS | |
163 }; | |
164 | |
165 /** | |
166 * Constructor to open a key starting an one of the predefined roots. | |
167 * Opens the key with read-only access. | |
168 */ | |
169 Registry_Key( Predefined root, std::wstring key_name ); | |
170 | |
171 /** | |
172 * Destructor closes the key if it's not a predefined one. | |
173 */ | |
174 ~Registry_Key(); | |
175 | |
176 /** | |
177 * Retrieve a value from a name-value pair within the present key. | |
178 * | |
179 * Throws if the name is not found within the dictionary. | |
180 * Throws if the name is found but is not a string. | |
181 */ | |
182 std::wstring value_wstring( std::wstring name ) const; | |
183 | |
184 private: | |
185 /** | |
186 */ | |
187 inline HKEY hkey_of_predefined( Predefined root ) | |
188 { | |
189 static HKEY predefined_hkeys[5] = | |
190 { | |
191 HKEY_CLASSES_ROOT, | |
192 HKEY_CURRENT_CONFIG, | |
193 HKEY_CURRENT_USER, | |
194 HKEY_LOCAL_MACHINE, | |
195 HKEY_USERS | |
196 }; | |
197 return predefined_hkeys[ (unsigned int) root ]; | |
198 } | |
199 }; | |
200 | |
201 Registry_Key::Registry_Key( Predefined root, std::wstring key_name ) | |
202 : is_predefined( key_name.empty() ) | |
203 { | |
204 HRESULT hr = RegOpenKeyExW( hkey_of_predefined( root ), key_name.c_str(), 0, KEY_QUERY_VALUE, &key ); | |
205 if ( hr != ERROR_SUCCESS || !key ) | |
206 { | |
207 throw std::runtime_error( "Failure in RegOpenKeyExW" ); | |
208 } | |
209 } | |
210 | |
211 Registry_Key::~Registry_Key() | |
212 { | |
213 if ( ! is_predefined ) | |
214 { | |
215 RegCloseKey( key ); | |
216 } | |
217 } | |
218 | |
219 std::wstring Registry_Key::value_wstring( std::wstring name ) const | |
220 { | |
221 /* | |
222 * Step one is to determine the presence of the value, along with its type a nd byte size. | |
223 */ | |
224 DWORD type; | |
225 DWORD size = 0; | |
226 HRESULT hr = ::RegQueryValueExW( key, name.c_str(), 0, &type, 0, &size ); | |
227 if ( hr != ERROR_SUCCESS ) | |
228 { | |
229 throw std::runtime_error( "Failure in RegQueryValueEx to query name" ); | |
230 } | |
231 if ( type != REG_SZ ) | |
232 { | |
233 throw std::runtime_error( "Value is not string type" ); | |
Oleksandr
2014/07/15 18:25:11
Just returning an empty string and logging the err
Eric
2014/07/20 18:18:46
As a rule, eliminating exception handling shouldn'
| |
234 } | |
235 | |
236 /* | |
237 * Step two is to allocate a buffer for the string and query for its value. | |
238 * Note that 'size' is the size in bytes, which we need for the system call, | |
239 * but that 'psize' is the size in words, which we need to manipulate the wchar_t array 'p'. | |
240 */ | |
241 // Round the byte size up to the nearest multiple of two. | |
242 size = ( size + 1 ) & ~ DWORD(1); | |
243 size_t psize = size >> 1; | |
244 // We need to allocate a temporary buffer to receive the value, because ther e's no interface to write directly into the buffer of std::basic_string. | |
245 std::unique_ptr< wchar_t[] > p( new wchar_t[ psize ] ); | |
Oleksandr
2014/07/15 18:25:11
std::wstring p;
p.resize(psize);
RegQueryValueExW(
Eric
2014/07/20 18:18:46
Except that string buffers are 'const' and you're
| |
246 hr = RegQueryValueExW( key, name.c_str(), 0, 0, reinterpret_cast<BYTE *>( p. get() ), &size ); | |
247 if ( hr != ERROR_SUCCESS ) | |
248 { | |
249 throw std::runtime_error( "Failure in RegQueryValueExW to retrieve value" ); | |
250 } | |
251 | |
252 /* | |
253 * Step three is to construct a return string. | |
254 * | |
255 * There's the possibility of an extra terminating null character in the ret urn value of the query. | |
256 * If so, we have to decrement the length of the return value to eliminate i t. | |
257 * If it persists, it will interfere with later string operations such as co ncatenation. | |
258 */ | |
259 if ( p[ psize - 1 ] == L'\0' ) | |
260 { | |
261 --psize; | |
262 } | |
263 return std::wstring( p.get(), psize ); | |
264 } | |
265 | |
266 /** | |
267 * Internal implementation of the IE version string. | |
268 * | |
269 * This version throws exceptions for its errors, relying on its caller to han dle them. | |
270 * | |
271 * Quoting http://support.microsoft.com/kb/969393: | |
272 * "Note The version string value for Internet Explorer 10 is 9.10.9200.1638 4, and the svcVersion string value is 10.0.9200.16384." | |
273 * [EH 2014-06-20] My current version of IE 11 is reporting these values: | |
274 * Version 9.11.9600.17041 | |
275 * svcVersion 11.0.9600.17041 | |
276 */ | |
Oleksandr
2014/07/15 18:25:11
I think it's better to remove most of the comments
Eric
2014/07/20 18:18:46
Well, that's what the problem is, that the documen
| |
277 | |
278 std::wstring IE_version_string() | |
279 { | |
280 Registry_Key IE_key( Registry_Key::Predefined::HKLM, L"Software\\Microsoft\\ Internet Explorer" ); | |
281 std::wstring version( IE_key.value_wstring( L"Version" ) ); | |
282 /* | |
283 * We're expecting a version string that matches the regex "^[1-9]\.". | |
284 * Since IE versions 10 and 11 use a version string that begins with "9.", t his simplistic parsing method works adequately. | |
285 * Warning: IE version 12 and later might not behave the same, so this funct ion should be reviewed with each new release. | |
286 */ | |
287 if ( version[1] != '.' || version[0] < L'0' || version[0] > L'9' ) | |
288 { | |
289 throw std::runtime_error( "IE version string has unexpected format" ); | |
290 } | |
291 if ( version[0] != L'9' ) | |
292 { | |
293 return version; | |
294 } | |
295 // Assert the major version in the "Version" value is 9. | |
296 /* | |
297 * Version 9 might either be an actual version 9 or it might represent a ver sion >= 10 | |
298 * If the value named "svcVersion" exists, we'll report that instead. | |
299 */ | |
300 try | |
301 { | |
302 return IE_key.value_wstring( L"svcVersion" ); | |
303 } | |
304 catch ( ... ) | |
305 { | |
306 return version; | |
307 } | |
308 } | |
309 } | |
310 | |
311 | |
312 std::wstring ABP::IE::installed_version_string() | |
313 { | |
314 try | |
315 { | |
316 return IE_version_string(); | |
317 } | |
318 catch ( ... ) | |
Oleksandr
2014/07/15 18:25:11
Catching specifically std::runtime_error would mak
Eric
2014/07/20 18:18:46
I referred to this 'catch' block above. If we had
| |
319 { | |
320 return L""; | |
321 } | |
322 } | |
323 | |
324 int ABP::IE::installed_major_version() | |
325 { | |
326 try | |
327 { | |
328 std::wstring version = IE_version_string(); | |
329 /* | |
330 * If the second character is a period, we assume that the major version is a single digit. | |
331 */ | |
332 if ( version[ 1 ] == L'.' ) | |
333 { | |
334 return version[ 0 ] - L'0'; | |
335 } | |
336 // Assert IE_version_string() did not verify the syntax of the version strin g. | |
337 /* | |
338 * In this case, we'll assume that the version returned is two digits follow ed by a period. | |
339 * If not, we'll assume an error. | |
340 */ | |
341 if ( version[ 0 ] < L'0' || version[ 0 ] > L'9' || version[ 1 ] < L'0' || ve rsion[ 1 ] > L'9' || version[ 2 ] != L'.' ) | |
342 { | |
343 return 0; | |
344 } | |
345 return 10 * ( version[ 0 ] - L'0' ) + ( version[ 1 ] - L'0' ); | |
346 } | |
347 catch ( ... ) | |
348 { | |
349 return 0; | |
350 } | |
351 } | |
352 | |
353 | |
OLD | NEW |