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

Side by Side 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.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW
« 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