 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) 
  | Index: src/shared/Utils.cpp | 
| =================================================================== | 
| --- a/src/shared/Utils.cpp | 
| +++ b/src/shared/Utils.cpp | 
| @@ -128,3 +128,227 @@ | 
| input.replace(replaceStart, placeholder.length(), replacement); | 
| } | 
| } | 
| + | 
| +namespace | 
| +{ | 
| 
Eric
2014/06/21 20:45:36
Registry_Key should be used to refactor use of the
 | 
| + /** | 
| + * An open key in the system registry. | 
| + */ | 
| + class Registry_Key | 
| + { | 
| + /** | 
| + * Handle to an open registry key. | 
| + */ | 
| + 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.
 | 
| + | 
| + /** | 
| + * Flag whether the registry key is predefined or not. | 
| + * | 
| + * Predefined key values are considered "always open" and should not be closed in the destructor. | 
| + */ | 
| + bool _is_predefined; | 
| + | 
| + public: | 
| + /** | 
| + * Enumeration for predefined root keys. | 
| + */ | 
| + enum class Predefined | 
| + : unsigned int | 
| + { | 
| + HKCR = 0, // HKEY_CLASSES_ROOT | 
| + HKCC, // HKEY_CURRENT_CONFIG | 
| + HKCU, // HKEY_CURRENT_USER | 
| + HKLM, // HKEY_LOCAL_MACHINE | 
| + HKU // HKEY_USERS | 
| + }; | 
| + | 
| + /** | 
| + * Constructor to open a key starting an one of the predefined roots. | 
| + * Opens the key with read-only access. | 
| + */ | 
| + Registry_Key( Predefined root, std::wstring key_name ); | 
| 
Oleksandr
2014/06/22 21:53:17
Nit: no spaces after and before the parentheses he
 | 
| + | 
| + /** | 
| + * Destructor closes the key if it's not a predefined one. | 
| + */ | 
| + ~Registry_Key(); | 
| + | 
| + /** | 
| + * Retrieve a value from a name-value pair within the present key. | 
| + * | 
| + * Throws if the name is not found within the dictionary. | 
| + * Throws if the name is found but is not a string. | 
| + */ | 
| + std::wstring value_wstring( std::wstring name ) const; | 
| + | 
| + private: | 
| + /** | 
| + */ | 
| + 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.
 | 
| + { | 
| + 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
 | 
| + { | 
| + HKEY_CLASSES_ROOT, | 
| + HKEY_CURRENT_CONFIG, | 
| + HKEY_CURRENT_USER, | 
| + HKEY_LOCAL_MACHINE, | 
| + HKEY_USERS | 
| + }; | 
| + return predefined_hkeys[ (unsigned int) root ]; | 
| + } | 
| + | 
| + }; | 
| + | 
| + Registry_Key::Registry_Key( Predefined root, std::wstring key_name ) | 
| + : _is_predefined( key_name.empty() ) | 
| + { | 
| + HRESULT hr = RegOpenKeyExW( hkey_of_prefined( root ), key_name.c_str(), 0, KEY_QUERY_VALUE, &_key ); | 
| + if ( hr != ERROR_SUCCESS || !_key ) | 
| + { | 
| + throw std::runtime_error( "Failure in RegOpenKeyExW" ); | 
| + } | 
| + } | 
| + | 
| + Registry_Key::~Registry_Key() | 
| + { | 
| + if ( ! _is_predefined ) | 
| + { | 
| + RegCloseKey( _key ); | 
| + } | 
| + } | 
| + | 
| + std::wstring Registry_Key::value_wstring( std::wstring name ) const | 
| + { | 
| + /* | 
| + * Step one is to determine the presence of the value, along with its type and byte size. | 
| + */ | 
| + DWORD type; | 
| + DWORD size = 0; | 
| + HRESULT hr = ::RegQueryValueExW( _key, name.c_str(), 0, &type, 0, &size ); | 
| + if ( hr != ERROR_SUCCESS ) | 
| + { | 
| + throw std::runtime_error( "Failure in RegQueryValueEx to query name" ); | 
| + } | 
| + if ( type != REG_SZ ) | 
| + { | 
| + throw std::runtime_error( "Value is not string type" ); | 
| + } | 
| + | 
| + /* | 
| + * Step two is to allocate a buffer for the string and query for its value. | 
| + * Note that 'size' is the size in bytes, which we need for the system call, | 
| + * but that 'psize' is the size in words, which we need to manipulate the wchar_t array 'p'. | 
| + */ | 
| + // Round the byte size up to the nearest multiple of two. | 
| + size = ( size + 1 ) & ~ DWORD(1); | 
| + size_t psize = size >> 1; | 
| + // We need to allocate a temporary buffer to receive the value, because there's no interface to write directly into the buffer of std::basic_string. | 
| + std::unique_ptr< wchar_t[] > p( new wchar_t[ psize ] ); | 
| + hr = RegQueryValueExW( _key, name.c_str(), 0, 0, reinterpret_cast<BYTE *>( p.get() ), &size ); | 
| + if ( hr != ERROR_SUCCESS ) | 
| + { | 
| + throw std::runtime_error( "Failure in RegQueryValueExW to retrieve value" ); | 
| + } | 
| + | 
| + /* | 
| + * Step three is to construct a return string. | 
| + * | 
| + * There's the possibility of an extra terminating null character in the return value of the query. | 
| + * If so, we have to decrement the length of the return value to eliminate it. | 
| + * If it persists, it will interfere with later string operations such as concatenation. | 
| + */ | 
| + if ( p[ psize - 1 ] == L'\0' ) | 
| + { | 
| + --psize; | 
| + } | 
| + return std::wstring( p.get(), psize ); | 
| + } | 
| + | 
| + /** | 
| + * Internal implementation of the IE version string. | 
| + * | 
| + * This version throws exceptions for its errors, relying on its caller to handle them. | 
| + * | 
| + * Quoting http://support.microsoft.com/kb/969393: | 
| + * "Note The version string value for Internet Explorer 10 is 9.10.9200.16384, and the svcVersion string value is 10.0.9200.16384." | 
| + * [EH 2014-06-20] My current version of IE 11 is reporting these values: | 
| + * Version 9.11.9600.17041 | 
| + * svcVersion 11.0.9600.17041 | 
| + */ | 
| + | 
| + std::wstring IE_version_string() | 
| + { | 
| + Registry_Key IE_key( Registry_Key::Predefined::HKLM, L"Software\\Microsoft\\Internet Explorer" ); | 
| + std::wstring version( IE_key.value_wstring( L"Version" ) ); | 
| + /* | 
| + * We're expecting a version string that matches the regex "^[1-9]\.". | 
| + * Since IE versions 10 and 11 use a version string that begins with "9.", this simplistic parsing method works adequately. | 
| + * Warning: IE version 12 and later might not behave the same, so this function should be reviewed with each new release. | 
| + */ | 
| + if ( version[1] != '.' || version[0] < L'0' || version[0] > L'9' ) | 
| + { | 
| + 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
 | 
| + } | 
| + if ( version[0] != L'9' ) | 
| + { | 
| + return version; | 
| + } | 
| + // Assert the major version in the "Version" value is 9. | 
| + /* | 
| + * Version 9 might either be an actual version 9 or it might represent a version >= 10 | 
| + * If the value named "svcVersion" exists, we'll report that instead. | 
| + */ | 
| + try | 
| + { | 
| + return IE_key.value_wstring( L"svcVersion" ); | 
| + } | 
| + catch ( ... ) | 
| + { | 
| + return version; | 
| + } | 
| + } | 
| +} | 
| + | 
| + | 
| +std::wstring ABP::IE::installed_version_string() | 
| +{ | 
| + try | 
| + { | 
| + return IE_version_string(); | 
| + } | 
| + catch ( ... ) | 
| + { | 
| + return L""; | 
| + } | 
| +} | 
| + | 
| +int ABP::IE::installed_major_version() | 
| +{ | 
| + try | 
| + { | 
| + std::wstring version = IE_version_string(); | 
| + /* | 
| + * If the second character is a period, we assume that the major version is a single digit. | 
| + */ | 
| + if ( version[ 1 ] == L'.' ) | 
| + { | 
| + return version[ 0 ] - L'0'; | 
| + } | 
| + // Assert IE_version_string() did not verify the syntax of the version string. | 
| + /* | 
| + * In this case, we'll assume that the version returned is two digits followed by a period. | 
| + * If not, we'll assume an error. | 
| + */ | 
| + if ( version[ 0 ] < L'0' || version[ 0 ] > L'9' || version[ 1 ] < L'0' || version[ 1 ] > L'9' || version[ 2 ] != L'.' ) | 
| + { | 
| + return 0; | 
| + } | 
| + return 10 * ( version[ 0 ] - L'0' ) + ( version[ 1 ] - L'0' ); | 
| + } | 
| + catch ( ... ) | 
| + { | 
| + return 0; | 
| + } | 
| +} | 
| + | 
| + |