 Issue 5171515343503360:
  Issue #41 - Bring method of determining IE version up to date  (Closed)
    
  
    Issue 5171515343503360:
  Issue #41 - Bring method of determining IE version up to date  (Closed) 
  | 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 |