Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: src/shared/Utils.cpp

Issue 5171515343503360: Issue #41 - Bring method of determining IE version up to date (Closed)
Patch Set: Second version Created June 25, 2014, 6:53 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
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;
+ }
+}
+
+
« src/engine/Main.cpp ('K') | « src/shared/Utils.h ('k') | test/Util_Test.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld