| 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 |