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 { | |
Eric
2014/06/21 20:45:36
Registry_Key should be used to refactor use of the
| |
134 /** | |
135 * An open key in the system registry. | |
136 */ | |
137 class Registry_Key | |
138 { | |
139 /** | |
140 * Handle to an open registry key. | |
141 */ | |
142 HKEY _key; | |
Oleksandr
2014/06/22 21:53:17
Nit: Either m_key, as have in other parts of code,
Eric
2014/06/25 18:52:02
Done.
| |
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 | |
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 ); | |
Oleksandr
2014/06/22 21:53:17
Nit: no spaces after and before the parentheses he
| |
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_prefined( Predefined root ) | |
Oleksandr
2014/06/22 21:53:17
I guess should've been hkey_of_predefined?
Eric
2014/06/25 18:52:02
Yes. Derp.
Done.
| |
188 { | |
189 static HKEY predefined_hkeys[ 5 ] = | |
Oleksandr
2014/06/22 21:53:17
Nit: no spaces before and after brackets here and
Oleksandr
2014/07/15 18:25:11
It's fixed here, but not everywhere else :)
On 201
| |
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 | |
202 Registry_Key::Registry_Key( Predefined root, std::wstring key_name ) | |
203 : _is_predefined( key_name.empty() ) | |
204 { | |
205 HRESULT hr = RegOpenKeyExW( hkey_of_prefined( root ), key_name.c_str(), 0, K EY_QUERY_VALUE, &_key ); | |
206 if ( hr != ERROR_SUCCESS || !_key ) | |
207 { | |
208 throw std::runtime_error( "Failure in RegOpenKeyExW" ); | |
209 } | |
210 } | |
211 | |
212 Registry_Key::~Registry_Key() | |
213 { | |
214 if ( ! _is_predefined ) | |
215 { | |
216 RegCloseKey( _key ); | |
217 } | |
218 } | |
219 | |
220 std::wstring Registry_Key::value_wstring( std::wstring name ) const | |
221 { | |
222 /* | |
223 * Step one is to determine the presence of the value, along with its type a nd byte size. | |
224 */ | |
225 DWORD type; | |
226 DWORD size = 0; | |
227 HRESULT hr = ::RegQueryValueExW( _key, name.c_str(), 0, &type, 0, &size ); | |
228 if ( hr != ERROR_SUCCESS ) | |
229 { | |
230 throw std::runtime_error( "Failure in RegQueryValueEx to query name" ); | |
231 } | |
232 if ( type != REG_SZ ) | |
233 { | |
234 throw std::runtime_error( "Value is not string type" ); | |
235 } | |
236 | |
237 /* | |
238 * Step two is to allocate a buffer for the string and query for its value. | |
239 * Note that 'size' is the size in bytes, which we need for the system call, | |
240 * but that 'psize' is the size in words, which we need to manipulate the wchar_t array 'p'. | |
241 */ | |
242 // Round the byte size up to the nearest multiple of two. | |
243 size = ( size + 1 ) & ~ DWORD(1); | |
244 size_t psize = size >> 1; | |
245 // 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. | |
246 std::unique_ptr< wchar_t[] > p( new wchar_t[ psize ] ); | |
247 hr = RegQueryValueExW( _key, name.c_str(), 0, 0, reinterpret_cast<BYTE *>( p .get() ), &size ); | |
248 if ( hr != ERROR_SUCCESS ) | |
249 { | |
250 throw std::runtime_error( "Failure in RegQueryValueExW to retrieve value" ); | |
251 } | |
252 | |
253 /* | |
254 * Step three is to construct a return string. | |
255 * | |
256 * There's the possibility of an extra terminating null character in the ret urn value of the query. | |
257 * If so, we have to decrement the length of the return value to eliminate i t. | |
258 * If it persists, it will interfere with later string operations such as co ncatenation. | |
259 */ | |
260 if ( p[ psize - 1 ] == L'\0' ) | |
261 { | |
262 --psize; | |
263 } | |
264 return std::wstring( p.get(), psize ); | |
265 } | |
266 | |
267 /** | |
268 * Internal implementation of the IE version string. | |
269 * | |
270 * This version throws exceptions for its errors, relying on its caller to han dle them. | |
271 * | |
272 * Quoting http://support.microsoft.com/kb/969393: | |
273 * "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." | |
274 * [EH 2014-06-20] My current version of IE 11 is reporting these values: | |
275 * Version 9.11.9600.17041 | |
276 * svcVersion 11.0.9600.17041 | |
277 */ | |
278 | |
279 std::wstring IE_version_string() | |
280 { | |
281 Registry_Key IE_key( Registry_Key::Predefined::HKLM, L"Software\\Microsoft\\ Internet Explorer" ); | |
282 std::wstring version( IE_key.value_wstring( L"Version" ) ); | |
283 /* | |
284 * We're expecting a version string that matches the regex "^[1-9]\.". | |
285 * Since IE versions 10 and 11 use a version string that begins with "9.", t his simplistic parsing method works adequately. | |
286 * Warning: IE version 12 and later might not behave the same, so this funct ion should be reviewed with each new release. | |
287 */ | |
288 if ( version[1] != '.' || version[0] < L'0' || version[0] > L'9' ) | |
289 { | |
290 throw std::runtime_error( "IE version string has unexpected format" ); | |
Wladimir Palant
2014/06/23 06:54:49
So, we crash and burn if the we see an unexpected
Eric
2014/06/25 18:52:02
IE_version_string() is only called (at present) by
| |
291 } | |
292 if ( version[0] != L'9' ) | |
293 { | |
294 return version; | |
295 } | |
296 // Assert the major version in the "Version" value is 9. | |
297 /* | |
298 * Version 9 might either be an actual version 9 or it might represent a ver sion >= 10 | |
299 * If the value named "svcVersion" exists, we'll report that instead. | |
300 */ | |
301 try | |
302 { | |
303 return IE_key.value_wstring( L"svcVersion" ); | |
304 } | |
305 catch ( ... ) | |
306 { | |
307 return version; | |
308 } | |
309 } | |
310 } | |
311 | |
312 | |
313 std::wstring ABP::IE::installed_version_string() | |
314 { | |
315 try | |
316 { | |
317 return IE_version_string(); | |
318 } | |
319 catch ( ... ) | |
320 { | |
321 return L""; | |
322 } | |
323 } | |
324 | |
325 int ABP::IE::installed_major_version() | |
326 { | |
327 try | |
328 { | |
329 std::wstring version = IE_version_string(); | |
330 /* | |
331 * If the second character is a period, we assume that the major version is a single digit. | |
332 */ | |
333 if ( version[ 1 ] == L'.' ) | |
334 { | |
335 return version[ 0 ] - L'0'; | |
336 } | |
337 // Assert IE_version_string() did not verify the syntax of the version strin g. | |
338 /* | |
339 * In this case, we'll assume that the version returned is two digits follow ed by a period. | |
340 * If not, we'll assume an error. | |
341 */ | |
342 if ( version[ 0 ] < L'0' || version[ 0 ] > L'9' || version[ 1 ] < L'0' || ve rsion[ 1 ] > L'9' || version[ 2 ] != L'.' ) | |
343 { | |
344 return 0; | |
345 } | |
346 return 10 * ( version[ 0 ] - L'0' ) + ( version[ 1 ] - L'0' ); | |
347 } | |
348 catch ( ... ) | |
349 { | |
350 return 0; | |
351 } | |
352 } | |
353 | |
354 | |
OLD | NEW |