| Index: src/shared/Utils.cpp |
| =================================================================== |
| --- a/src/shared/Utils.cpp |
| +++ b/src/shared/Utils.cpp |
| @@ -128,3 +128,226 @@ |
| input.replace(replaceStart, placeholder.length(), replacement); |
| } |
| } |
| + |
| +namespace |
| +{ |
| + /** |
| + * An open key in the system registry. |
| + */ |
| + 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
|
| + { |
| + /** |
| + * Handle to an open registry key. |
| + */ |
| + HKEY key; |
| + |
| + /** |
| + * 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 |
|
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
|
| + { |
| + 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 ); |
| + |
| + /** |
| + * 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_predefined( Predefined root ) |
| + { |
| + static HKEY predefined_hkeys[5] = |
| + { |
| + 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_predefined( 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" ); |
|
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'
|
| + } |
| + |
| + /* |
| + * 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 ] ); |
|
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
|
| + 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 |
| + */ |
|
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
|
| + |
| + 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" ); |
| + } |
| + 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 ( ... ) |
|
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
|
| + { |
| + 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; |
| + } |
| +} |
| + |
| + |