OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ie_importer.h" | |
6 | |
7 #include <intshcut.h> | |
8 #include <pstore.h> | |
9 #include <shlobj.h> | |
10 #include <urlhist.h> | |
11 #include <algorithm> | |
12 | |
13 #include "base/file_util.h" | |
14 #include "base/registry.h" | |
15 #include "base/string_util.h" | |
16 #include "base/time.h" | |
17 #include "base/win_util.h" | |
18 #include "chrome/browser/bookmarks/bookmark_model.h" | |
19 #include "chrome/browser/ie7_password.h" | |
20 #include "chrome/browser/template_url_model.h" | |
21 #include "chrome/common/l10n_util.h" | |
22 #include "chrome/common/time_format.h" | |
23 #include "chrome/common/win_util.h" | |
24 #include "googleurl/src/gurl.h" | |
25 | |
26 #include "generated_resources.h" | |
27 | |
28 namespace { | |
29 | |
30 // Gets the creation time of the given file or directory. | |
31 static Time GetFileCreationTime(const std::wstring& file) { | |
32 Time creation_time; | |
33 ScopedHandle file_handle( | |
34 CreateFile(file.c_str(), GENERIC_READ, | |
35 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
36 NULL, OPEN_EXISTING, | |
37 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL)); | |
38 FILETIME creation_filetime; | |
39 if (GetFileTime(file_handle, &creation_filetime, NULL, NULL)) | |
40 creation_time = Time::FromFileTime(creation_filetime); | |
41 return creation_time; | |
42 } | |
43 | |
44 } // namespace | |
45 | |
46 // static | |
47 // {E161255A-37C3-11D2-BCAA-00C04fD929DB} | |
48 const GUID IEImporter::kPStoreAutocompleteGUID = {0xe161255a, 0x37c3, 0x11d2, | |
49 {0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb}}; | |
50 // {A79029D6-753E-4e27-B807-3D46AB1545DF} | |
51 const GUID IEImporter::kUnittestGUID = { 0xa79029d6, 0x753e, 0x4e27, | |
52 {0xb8, 0x7, 0x3d, 0x46, 0xab, 0x15, 0x45, 0xdf}}; | |
53 | |
54 void IEImporter::StartImport(ProfileInfo profile_info, | |
55 uint16 items, | |
56 ProfileWriter* writer, | |
57 ImporterHost* host) { | |
58 writer_ = writer; | |
59 source_path_ = profile_info.source_path; | |
60 importer_host_ = host; | |
61 | |
62 NotifyStarted(); | |
63 | |
64 // Some IE settings (such as Protected Storage) is obtained via COM APIs. | |
65 win_util::ScopedCOMInitializer com_initializer; | |
66 | |
67 if ((items & HOME_PAGE) && !cancelled()) | |
68 ImportHomepage(); // Doesn't have a UI item. | |
69 // The order here is important! | |
70 if ((items & FAVORITES) && !cancelled()) { | |
71 NotifyItemStarted(FAVORITES); | |
72 ImportFavorites(); | |
73 NotifyItemEnded(FAVORITES); | |
74 } | |
75 if ((items & SEARCH_ENGINES) && !cancelled()) { | |
76 NotifyItemStarted(SEARCH_ENGINES); | |
77 ImportSearchEngines(); | |
78 NotifyItemEnded(SEARCH_ENGINES); | |
79 } | |
80 if ((items & PASSWORDS) && !cancelled()) { | |
81 NotifyItemStarted(PASSWORDS); | |
82 // Always import IE6 passwords. | |
83 ImportPasswordsIE6(); | |
84 | |
85 if (CurrentIEVersion() >= 7) | |
86 ImportPasswordsIE7(); | |
87 NotifyItemEnded(PASSWORDS); | |
88 } | |
89 if ((items & HISTORY) && !cancelled()) { | |
90 NotifyItemStarted(HISTORY); | |
91 ImportHistory(); | |
92 NotifyItemEnded(HISTORY); | |
93 } | |
94 NotifyEnded(); | |
95 } | |
96 | |
97 void IEImporter::ImportFavorites() { | |
98 std::wstring path; | |
99 | |
100 FavoritesInfo info; | |
101 if (!GetFavoritesInfo(&info)) | |
102 return; | |
103 | |
104 BookmarkVector bookmarks; | |
105 ParseFavoritesFolder(info, &bookmarks); | |
106 | |
107 if (!bookmarks.empty() && !cancelled()) { | |
108 main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, | |
109 &ProfileWriter::AddBookmarkEntry, bookmarks)); | |
110 } | |
111 } | |
112 | |
113 void IEImporter::ImportPasswordsIE6() { | |
114 GUID AutocompleteGUID = kPStoreAutocompleteGUID; | |
115 if (!source_path_.empty()) { | |
116 // We supply a fake GUID for testting. | |
117 AutocompleteGUID = kUnittestGUID; | |
118 } | |
119 | |
120 // The PStoreCreateInstance function retrieves an interface pointer | |
121 // to a storage provider. But this function has no associated import | |
122 // library or header file, we must call it using the LoadLibrary() | |
123 // and GetProcAddress() functions. | |
124 typedef HRESULT (WINAPI *PStoreCreateFunc)(IPStore**, DWORD, DWORD, DWORD); | |
125 HMODULE pstorec_dll = LoadLibrary(L"pstorec.dll"); | |
126 PStoreCreateFunc PStoreCreateInstance = | |
127 (PStoreCreateFunc)GetProcAddress(pstorec_dll, "PStoreCreateInstance"); | |
128 | |
129 CComPtr<IPStore> pstore; | |
130 HRESULT result = PStoreCreateInstance(&pstore, 0, 0, 0); | |
131 if (result != S_OK) { | |
132 FreeLibrary(pstorec_dll); | |
133 return; | |
134 } | |
135 | |
136 std::vector<AutoCompleteInfo> ac_list; | |
137 | |
138 // Enumerates AutoComplete items in the protected database. | |
139 CComPtr<IEnumPStoreItems> item; | |
140 result = pstore->EnumItems(0, &AutocompleteGUID, | |
141 &AutocompleteGUID, 0, &item); | |
142 if (result != PST_E_OK) { | |
143 pstore.Release(); | |
144 FreeLibrary(pstorec_dll); | |
145 return; | |
146 } | |
147 | |
148 wchar_t* item_name; | |
149 while (!cancelled() && SUCCEEDED(item->Next(1, &item_name, 0))) { | |
150 DWORD length = 0; | |
151 unsigned char* buffer = NULL; | |
152 result = pstore->ReadItem(0, &AutocompleteGUID, &AutocompleteGUID, | |
153 item_name, &length, &buffer, NULL, 0); | |
154 if (SUCCEEDED(result)) { | |
155 AutoCompleteInfo ac; | |
156 ac.key = item_name; | |
157 std::wstring data; | |
158 data.insert(0, reinterpret_cast<wchar_t*>(buffer), | |
159 length / sizeof(wchar_t)); | |
160 | |
161 // The key name is always ended with ":StringData". | |
162 const wchar_t kDataSuffix[] = L":StringData"; | |
163 size_t i = ac.key.rfind(kDataSuffix); | |
164 if (i != std::wstring::npos && ac.key.substr(i) == kDataSuffix) { | |
165 ac.key.erase(i); | |
166 ac.is_url = (ac.key.find(L"://") != std::wstring::npos); | |
167 ac_list.push_back(ac); | |
168 SplitString(data, L'\0', &ac_list[ac_list.size() - 1].data); | |
169 } | |
170 CoTaskMemFree(buffer); | |
171 } | |
172 CoTaskMemFree(item_name); | |
173 } | |
174 // Releases them before unload the dll. | |
175 item.Release(); | |
176 pstore.Release(); | |
177 FreeLibrary(pstorec_dll); | |
178 | |
179 size_t i; | |
180 for (i = 0; i < ac_list.size(); i++) { | |
181 if (!ac_list[i].is_url || ac_list[i].data.size() < 2) | |
182 continue; | |
183 | |
184 GURL url(ac_list[i].key.c_str()); | |
185 if (!(LowerCaseEqualsASCII(url.scheme(), "http") || | |
186 LowerCaseEqualsASCII(url.scheme(), "https"))) { | |
187 continue; | |
188 } | |
189 | |
190 PasswordForm form; | |
191 GURL::Replacements rp; | |
192 rp.ClearUsername(); | |
193 rp.ClearPassword(); | |
194 rp.ClearQuery(); | |
195 rp.ClearRef(); | |
196 form.origin = url.ReplaceComponents(rp); | |
197 form.username_value = ac_list[i].data[0]; | |
198 form.password_value = ac_list[i].data[1]; | |
199 form.signon_realm = url.GetOrigin().spec(); | |
200 | |
201 // This is not precise, because a scheme of https does not imply a valid | |
202 // certificate was presented; however we assign it this way so that if we | |
203 // import a password from IE whose scheme is https, we give it the benefit | |
204 // of the doubt and DONT auto-fill it unless the form appears under | |
205 // valid SSL conditions. | |
206 form.ssl_valid = url.SchemeIsSecure(); | |
207 | |
208 // Goes through the list to find out the username field | |
209 // of the web page. | |
210 size_t list_it, item_it; | |
211 for (list_it = 0; list_it < ac_list.size(); ++list_it) { | |
212 if (ac_list[list_it].is_url) | |
213 continue; | |
214 | |
215 for (item_it = 0; item_it < ac_list[list_it].data.size(); ++item_it) | |
216 if (ac_list[list_it].data[item_it] == form.username_value) { | |
217 form.username_element = ac_list[list_it].key; | |
218 break; | |
219 } | |
220 } | |
221 | |
222 main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, | |
223 &ProfileWriter::AddPasswordForm, form)); | |
224 } | |
225 } | |
226 | |
227 void IEImporter::ImportPasswordsIE7() { | |
228 if (!source_path_.empty()) { | |
229 // We have been called from the unit tests. Don't import real passwords. | |
230 return; | |
231 } | |
232 | |
233 const wchar_t kStorage2Path[] = | |
234 L"Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2"; | |
235 | |
236 RegKey key(HKEY_CURRENT_USER, kStorage2Path, KEY_READ); | |
237 RegistryValueIterator reg_iterator(HKEY_CURRENT_USER, kStorage2Path); | |
238 while (reg_iterator.Valid() && !cancelled()) { | |
239 // Get the size of the encrypted data. | |
240 DWORD value_len = 0; | |
241 if (key.ReadValue(reg_iterator.Name(), NULL, &value_len) && value_len) { | |
242 // Query the encrypted data. | |
243 std::vector<unsigned char> value; | |
244 value.resize(value_len); | |
245 if (key.ReadValue(reg_iterator.Name(), &value.front(), &value_len)) { | |
246 IE7PasswordInfo password_info; | |
247 password_info.url_hash = reg_iterator.Name(); | |
248 password_info.encrypted_data = value; | |
249 password_info.date_created = Time::Now(); | |
250 main_loop_->PostTask(FROM_HERE, | |
251 NewRunnableMethod(writer_, | |
252 &ProfileWriter::AddIE7PasswordInfo, | |
253 password_info)); | |
254 } | |
255 } | |
256 | |
257 ++reg_iterator; | |
258 } | |
259 } | |
260 | |
261 // Reads history information from COM interface. | |
262 void IEImporter::ImportHistory() { | |
263 const std::string kSchemes[] = {"http", "https", "ftp", "file"}; | |
264 int total_schemes = arraysize(kSchemes); | |
265 | |
266 CComPtr<IUrlHistoryStg2> url_history_stg2; | |
267 HRESULT result; | |
268 result = url_history_stg2.CoCreateInstance(CLSID_CUrlHistory, NULL, | |
269 CLSCTX_INPROC_SERVER); | |
270 if (FAILED(result)) | |
271 return; | |
272 CComPtr<IEnumSTATURL> enum_url; | |
273 if (SUCCEEDED(result = url_history_stg2->EnumUrls(&enum_url))) { | |
274 std::vector<history::URLRow> rows; | |
275 STATURL stat_url; | |
276 ULONG fetched; | |
277 while (!cancelled() && | |
278 (result = enum_url->Next(1, &stat_url, &fetched)) == S_OK) { | |
279 std::wstring url_string; | |
280 std::wstring title_string; | |
281 if (stat_url.pwcsUrl) { | |
282 url_string = stat_url.pwcsUrl; | |
283 CoTaskMemFree(stat_url.pwcsUrl); | |
284 } | |
285 if (stat_url.pwcsTitle) { | |
286 title_string = stat_url.pwcsTitle; | |
287 CoTaskMemFree(stat_url.pwcsTitle); | |
288 } | |
289 | |
290 GURL url(url_string); | |
291 // Skips the URLs that are invalid or have other schemes. | |
292 if (!url.is_valid() || | |
293 (std::find(kSchemes, kSchemes + total_schemes, url.scheme()) == | |
294 kSchemes + total_schemes)) | |
295 continue; | |
296 | |
297 history::URLRow row(url); | |
298 row.set_title(title_string); | |
299 row.set_last_visit(Time::FromFileTime(stat_url.ftLastVisited)); | |
300 if (stat_url.dwFlags == STATURL_QUERYFLAG_TOPLEVEL) { | |
301 row.set_visit_count(1); | |
302 row.set_hidden(false); | |
303 } else { | |
304 row.set_hidden(true); | |
305 } | |
306 | |
307 rows.push_back(row); | |
308 } | |
309 | |
310 if (!rows.empty() && !cancelled()) { | |
311 main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, | |
312 &ProfileWriter::AddHistoryPage, rows)); | |
313 } | |
314 } | |
315 } | |
316 | |
317 void IEImporter::ImportSearchEngines() { | |
318 // On IE, search engines are stored in the registry, under: | |
319 // Software\Microsoft\Internet Explorer\SearchScopes | |
320 // Each key represents a search engine. The URL value contains the URL and | |
321 // the DisplayName the name. | |
322 // The default key's name is contained under DefaultScope. | |
323 const wchar_t kSearchScopePath[] = | |
324 L"Software\\Microsoft\\Internet Explorer\\SearchScopes"; | |
325 | |
326 RegKey key(HKEY_CURRENT_USER, kSearchScopePath, KEY_READ); | |
327 std::wstring default_search_engine_name; | |
328 const TemplateURL* default_search_engine = NULL; | |
329 std::map<std::wstring, TemplateURL*> search_engines_map; | |
330 key.ReadValue(L"DefaultScope", &default_search_engine_name); | |
331 RegistryKeyIterator key_iterator(HKEY_CURRENT_USER, kSearchScopePath); | |
332 while (key_iterator.Valid()) { | |
333 std::wstring sub_key_name = kSearchScopePath; | |
334 sub_key_name.append(L"\\").append(key_iterator.Name()); | |
335 RegKey sub_key(HKEY_CURRENT_USER, sub_key_name.c_str(), KEY_READ); | |
336 std::wstring url; | |
337 if (!sub_key.ReadValue(L"URL", &url) || url.empty()) { | |
338 LOG(INFO) << "No URL for IE search engine at " << key_iterator.Name(); | |
339 ++key_iterator; | |
340 continue; | |
341 } | |
342 // For the name, we try the default value first (as Live Search uses a | |
343 // non displayable name in DisplayName, and the readable name under the | |
344 // default value). | |
345 std::wstring name; | |
346 if (!sub_key.ReadValue(NULL, &name) || name.empty()) { | |
347 // Try the displayable name. | |
348 if (!sub_key.ReadValue(L"DisplayName", &name) || name.empty()) { | |
349 LOG(INFO) << "No name for IE search engine at " << key_iterator.Name(); | |
350 ++key_iterator; | |
351 continue; | |
352 } | |
353 } | |
354 | |
355 std::map<std::wstring, TemplateURL*>::iterator t_iter = | |
356 search_engines_map.find(url); | |
357 TemplateURL* template_url = | |
358 (t_iter != search_engines_map.end()) ? t_iter->second : NULL; | |
359 if (!template_url) { | |
360 // First time we see that URL. | |
361 template_url = new TemplateURL(); | |
362 template_url->set_short_name(name); | |
363 template_url->SetURL(url, 0, 0); | |
364 // Give this a keyword to facilitate tab-to-search, if possible. | |
365 template_url->set_keyword(TemplateURLModel::GenerateKeyword(GURL(url), | |
366 false)); | |
367 template_url->set_show_in_default_list(true); | |
368 search_engines_map[url] = template_url; | |
369 } | |
370 if (template_url && key_iterator.Name() == default_search_engine_name) { | |
371 DCHECK(!default_search_engine); | |
372 default_search_engine = template_url; | |
373 } | |
374 ++key_iterator; | |
375 } | |
376 | |
377 // ProfileWriter::AddKeywords() requires a vector and we have a map. | |
378 std::map<std::wstring, TemplateURL*>::iterator t_iter; | |
379 std::vector<TemplateURL*> search_engines; | |
380 int default_search_engine_index = -1; | |
381 for (t_iter = search_engines_map.begin(); t_iter != search_engines_map.end(); | |
382 ++t_iter) { | |
383 search_engines.push_back(t_iter->second); | |
384 if (default_search_engine == t_iter->second) { | |
385 default_search_engine_index = | |
386 static_cast<int>(search_engines.size()) - 1; | |
387 } | |
388 } | |
389 main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, | |
390 &ProfileWriter::AddKeywords, search_engines, default_search_engine_index, | |
391 true)); | |
392 } | |
393 | |
394 void IEImporter::ImportHomepage() { | |
395 const wchar_t kIESettingsMain[] = | |
396 L"Software\\Microsoft\\Internet Explorer\\Main"; | |
397 const wchar_t kIEHomepage[] = L"Start Page"; | |
398 const wchar_t kIEDefaultHomepage[] = L"Default_Page_URL"; | |
399 | |
400 RegKey key(HKEY_CURRENT_USER, kIESettingsMain, KEY_READ); | |
401 std::wstring homepage_url; | |
402 if (!key.ReadValue(kIEHomepage, &homepage_url) || homepage_url.empty()) | |
403 return; | |
404 | |
405 GURL homepage = GURL(homepage_url); | |
406 if (!homepage.is_valid()) | |
407 return; | |
408 | |
409 // Check to see if this is the default website and skip import. | |
410 RegKey keyDefault(HKEY_LOCAL_MACHINE, kIESettingsMain, KEY_READ); | |
411 std::wstring default_homepage_url; | |
412 if (keyDefault.ReadValue(kIEDefaultHomepage, &default_homepage_url) && | |
413 !default_homepage_url.empty()) { | |
414 if (homepage.spec() == GURL(default_homepage_url).spec()) | |
415 return; | |
416 } | |
417 | |
418 main_loop_->PostTask(FROM_HERE, NewRunnableMethod(writer_, | |
419 &ProfileWriter::AddHomepage, homepage)); | |
420 } | |
421 | |
422 bool IEImporter::GetFavoritesInfo(IEImporter::FavoritesInfo *info) { | |
423 if (!source_path_.empty()) { | |
424 // Source path exists during testing. | |
425 info->path = source_path_; | |
426 file_util::AppendToPath(&info->path, L"Favorites"); | |
427 info->links_folder = L"Links"; | |
428 return true; | |
429 } | |
430 | |
431 // IE stores the favorites in the Favorites under user profile's folder. | |
432 wchar_t buffer[MAX_PATH]; | |
433 if (FAILED(SHGetFolderPath(NULL, CSIDL_FAVORITES, NULL, | |
434 SHGFP_TYPE_CURRENT, buffer))) | |
435 return false; | |
436 info->path = buffer; | |
437 | |
438 // There is a Links folder under Favorites folder in Windows Vista, but it | |
439 // is not recording in Vista's registry. So in Vista, we assume the Links | |
440 // folder is under Favorites folder since it looks like there is not name | |
441 // different in every language version of Windows Vista. | |
442 if (win_util::GetWinVersion() != win_util::WINVERSION_VISTA) { | |
443 // The Link folder name is stored in the registry. | |
444 DWORD buffer_length = sizeof(buffer); | |
445 if (!ReadFromRegistry(HKEY_CURRENT_USER, | |
446 L"Software\\Microsoft\\Internet Explorer\\Toolbar", | |
447 L"LinksFolderName", buffer, &buffer_length)) | |
448 return false; | |
449 info->links_folder = buffer; | |
450 } else { | |
451 info->links_folder = L"Links"; | |
452 } | |
453 | |
454 // Gets the creation time of the user's profile folder. | |
455 if (FAILED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, | |
456 SHGFP_TYPE_CURRENT, buffer))) | |
457 return false; | |
458 info->profile_creation_time = GetFileCreationTime(buffer); | |
459 | |
460 return true; | |
461 } | |
462 | |
463 void IEImporter::ParseFavoritesFolder(const FavoritesInfo& info, | |
464 BookmarkVector* bookmarks) { | |
465 std::wstring ie_folder = l10n_util::GetString(IDS_BOOKMARK_GROUP_FROM_IE); | |
466 BookmarkVector toolbar_bookmarks; | |
467 std::wstring file; | |
468 std::vector<std::wstring> file_list; | |
469 file_util::FileEnumerator file_enumerator(info.path, true, | |
470 file_util::FileEnumerator::FILES); | |
471 while (!(file = file_enumerator.Next()).empty() && !cancelled()) | |
472 file_list.push_back(file); | |
473 | |
474 // Keep the bookmarks in alphabetical order. | |
475 std::sort(file_list.begin(), file_list.end()); | |
476 | |
477 for (std::vector<std::wstring>::iterator it = file_list.begin(); | |
478 it != file_list.end(); ++it) { | |
479 std::wstring filename = file_util::GetFilenameFromPath(*it); | |
480 std::wstring extension = file_util::GetFileExtensionFromPath(filename); | |
481 if (!LowerCaseEqualsASCII(extension, "url")) | |
482 continue; | |
483 | |
484 // We don't import default bookmarks from IE, e.g. "Customize links", | |
485 // "Free Hotmail". To detect these, we compare the creation time of the | |
486 // .url file with that of the profile folder. The file's creation time | |
487 // should be 2 minute later (to allow jitter). | |
488 Time creation = GetFileCreationTime(*it); | |
489 if (info.profile_creation_time != Time() && | |
490 creation - info.profile_creation_time <= TimeDelta::FromMinutes(2)) | |
491 continue; | |
492 | |
493 // Skip the bookmark with invalid URL. | |
494 GURL url = GURL(ResolveInternetShortcut(*it)); | |
495 if (!url.is_valid()) | |
496 continue; | |
497 | |
498 // Remove the dot and the file extension, and the directory path. | |
499 std::wstring relative_path = it->substr(info.path.size(), | |
500 it->size() - filename.size() - info.path.size()); | |
501 TrimString(relative_path, L"\\", &relative_path); | |
502 | |
503 ProfileWriter::BookmarkEntry entry; | |
504 entry.title = filename.substr(0, filename.size() - (extension.size() + 1)); | |
505 entry.url = url; | |
506 entry.creation_time = creation; | |
507 if (!relative_path.empty()) | |
508 SplitString(relative_path, file_util::kPathSeparator, &entry.path); | |
509 | |
510 // Flatten the bookmarks in Link folder onto bookmark toolbar. Otherwise, | |
511 // put it into "Other bookmarks". | |
512 if (first_run() && | |
513 (entry.path.size() > 0 && entry.path[0] == info.links_folder)) { | |
514 entry.in_toolbar = true; | |
515 entry.path.erase(entry.path.begin()); | |
516 toolbar_bookmarks.push_back(entry); | |
517 } else { | |
518 // After the first run, we put the bookmarks in a "Imported From IE" | |
519 // folder, so that we don't mess up the "Other bookmarks". | |
520 if (!first_run()) | |
521 entry.path.insert(entry.path.begin(), ie_folder); | |
522 bookmarks->push_back(entry); | |
523 } | |
524 } | |
525 bookmarks->insert(bookmarks->begin(), toolbar_bookmarks.begin(), | |
526 toolbar_bookmarks.end()); | |
527 } | |
528 | |
529 std::wstring IEImporter::ResolveInternetShortcut(const std::wstring& file) { | |
530 win_util::CoMemReleaser<wchar_t> url; | |
531 CComPtr<IUniformResourceLocator> url_locator; | |
532 HRESULT result = url_locator.CoCreateInstance(CLSID_InternetShortcut, NULL, | |
533 CLSCTX_INPROC_SERVER); | |
534 if (FAILED(result)) | |
535 return std::wstring(); | |
536 | |
537 CComPtr<IPersistFile> persist_file; | |
538 result = url_locator.QueryInterface(&persist_file); | |
539 if (FAILED(result)) | |
540 return std::wstring(); | |
541 | |
542 // Loads the Internet Shortcut from persistent storage. | |
543 result = persist_file->Load(file.c_str(), STGM_READ); | |
544 if (FAILED(result)) | |
545 return std::wstring(); | |
546 | |
547 result = url_locator->GetURL(&url); | |
548 // GetURL can return S_FALSE (FAILED(S_FALSE) is false) when url == NULL. | |
549 if (FAILED(result) || (url == NULL)) | |
550 return std::wstring(); | |
551 | |
552 return std::wstring(url); | |
553 } | |
554 | |
555 int IEImporter::CurrentIEVersion() const { | |
556 static int version = -1; | |
557 if (version < 0) { | |
558 wchar_t buffer[128]; | |
559 DWORD buffer_length = sizeof(buffer); | |
560 bool result = ReadFromRegistry(HKEY_LOCAL_MACHINE, | |
561 L"Software\\Microsoft\\Internet Explorer", | |
562 L"Version", buffer, &buffer_length); | |
563 version = (result ? _wtoi(buffer) : 0); | |
564 } | |
565 return version; | |
566 } | |
567 | |
OLD | NEW |