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: Created June 21, 2014, 8:25 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 {
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
OLDNEW
« src/plugin/PluginWbPassThrough.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