OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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/file_associations_win.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include <shlobj.h> | |
10 | |
11 #include "base/command_line.h" | |
12 #include "base/files/file_path.h" | |
13 #include "base/logging.h" | |
14 #include "base/strings/string16.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/win/registry.h" | |
18 | |
19 namespace { | |
20 | |
21 // Key relative to HKEY_CURRENT_USER that provides a user-writable alias for | |
22 // HKEY_CLASSES_ROOT. | |
23 const wchar_t kRegClassesRootAlias[] = L"Software\\Classes"; | |
24 const wchar_t kRegDefaultIcon[] = L"\\DefaultIcon"; | |
25 const wchar_t kRegShellOpen[] = L"\\shell\\open\\command"; | |
26 const wchar_t kRegOpenWithProgids[] = L"\\OpenWithProgids"; | |
27 | |
28 // Writes a value to the Windows registry under HKEY_CURRENT_USER, creating the | |
29 // key if it does not already exist. If |value_name| is the empty string, writes | |
30 // to the default value for the given key. If |keep_existing| is true, will not | |
31 // overwrite an existing value. If an error occurs, logs an error and returns | |
32 // false. | |
33 bool WriteCurrentUserRegistryValue(const base::string16& key_name, | |
34 const base::string16& value_name, | |
35 const base::string16& value_data, | |
36 bool keep_existing) { | |
37 base::win::RegKey key; | |
38 if (key.Create(HKEY_CURRENT_USER, key_name.c_str(), KEY_ALL_ACCESS) != | |
39 ERROR_SUCCESS) { | |
40 LOG(ERROR) << "Could not create HKEY_CURRENT_USER\\" << key_name; | |
jackhou1
2014/08/19 08:16:54
I think in general logging that will not be seen i
Matt Giuca
2014/08/19 08:24:48
Hmm OK. Well the problem is that these errors are
| |
41 return false; | |
42 } | |
43 if (keep_existing && key.HasValue(NULL)) | |
44 return true; | |
45 if (key.WriteValue(value_name.empty() ? NULL : value_name.c_str(), | |
46 value_data.c_str()) != ERROR_SUCCESS) { | |
47 LOG(ERROR) << "Could not write HKEY_CURRENT_USER\\" << key_name << "." | |
48 << (value_name.empty() ? L"(Default)" : value_name); | |
49 return false; | |
50 } | |
51 return true; | |
52 } | |
53 | |
54 // Writes a value to the Windows registry, overwriting any existing data. | |
55 bool WriteCurrentUserRegistryValue(const base::string16& key_name, | |
56 const base::string16& value_name, | |
57 const base::string16& value_data) { | |
58 return WriteCurrentUserRegistryValue(key_name, value_name, value_data, false); | |
59 } | |
60 | |
61 // Class helper for AddWindowsFileAssociations. Objects of this class live only | |
62 // during the AddWindowsFileAssociations operation. | |
63 class FileAssociator { | |
64 public: | |
65 FileAssociator(const base::string16& progid); | |
66 | |
67 // Creates the registry key for the file association class. This is the key | |
68 // that supplies information about the class (such as its name and command | |
69 // line), and is referenced by all file extensions sharing the association. | |
70 bool RegisterClass(const base::string16& file_type_name, | |
71 const base::FilePath& icon_path, | |
72 const base::CommandLine& command_line); | |
73 | |
74 // Deletes the registry key for the file association class. | |
75 bool DeleteClass(); | |
76 | |
77 // Adds a file extension as a supported type of this class. |ext| is the file | |
78 // extension (without a leading '.'). | |
79 bool AssociateExtension(const base::string16& ext); | |
80 | |
81 private: | |
82 // The class name for this association. | |
83 base::string16 progid_; | |
84 }; | |
85 | |
86 FileAssociator::FileAssociator(const base::string16& progid) : progid_(progid) { | |
87 } | |
88 | |
89 bool FileAssociator::RegisterClass(const base::string16& file_type_name, | |
90 const base::FilePath& icon_path, | |
91 const base::CommandLine& command_line) { | |
92 // Open or create a key HKEY_CLASSES_ROOT\PROGID. | |
93 base::string16 class_key_name = kRegClassesRootAlias; | |
94 class_key_name += L"\\"; | |
95 class_key_name += progid_; | |
96 | |
97 // Set the file type's user-visible name. | |
98 if (!WriteCurrentUserRegistryValue(class_key_name, L"", file_type_name)) | |
99 return false; | |
100 | |
101 // Set the file type's icon. | |
102 if (!WriteCurrentUserRegistryValue( | |
103 class_key_name + kRegDefaultIcon, L"", icon_path.value() + L",0")) { | |
104 return false; | |
105 } | |
106 | |
107 // Set the command line to execute for the "open" verb. | |
108 if (!WriteCurrentUserRegistryValue(class_key_name + kRegShellOpen, | |
109 L"", | |
110 command_line.GetCommandLineString())) { | |
111 return false; | |
112 } | |
113 | |
114 return true; | |
115 } | |
116 | |
117 bool FileAssociator::DeleteClass() { | |
118 // Delete the key HKEY_CLASSES_ROOT\PROGID. | |
119 base::win::RegKey key; | |
120 if (key.Open(HKEY_CURRENT_USER, kRegClassesRootAlias, KEY_ALL_ACCESS) != | |
121 ERROR_SUCCESS) { | |
122 LOG(ERROR) << "Could not open HKEY_CURRENT_USER\\" << kRegClassesRootAlias; | |
123 return false; | |
124 } | |
125 | |
126 if (key.DeleteKey(progid_.c_str()) != ERROR_SUCCESS) { | |
127 LOG(ERROR) << "Could not delete HKEY_CURRENT_USER\\" << kRegClassesRootAlias | |
128 << "\\" << progid_; | |
129 return false; | |
130 } | |
131 | |
132 return true; | |
133 } | |
134 | |
135 bool FileAssociator::AssociateExtension(const base::string16& ext) { | |
136 // Open or create a key HKEY_CLASSES_ROOT\.EXT. | |
137 base::string16 ext_key_name = kRegClassesRootAlias; | |
138 ext_key_name += L"\\."; | |
139 ext_key_name += ext; | |
140 | |
141 // Under OpenWithProgids, create an empty value with this class's name. | |
142 if (!WriteCurrentUserRegistryValue( | |
143 ext_key_name + kRegOpenWithProgids, progid_, L"")) { | |
144 return false; | |
145 } | |
146 | |
147 // If the extension key has no default value (the default class for this | |
148 // file type) is blank, register this application as the default handler. | |
149 // Otherwise, leave the existing default as-is. | |
150 return WriteCurrentUserRegistryValue(ext_key_name, L"", progid_, true); | |
151 } | |
152 | |
153 } // namespace | |
154 | |
155 bool AddWindowsFileAssociations(const std::string& progid, | |
156 const base::CommandLine& command_line, | |
157 const std::string& file_type_name, | |
158 const base::FilePath& icon_path, | |
159 const std::set<std::string>& file_extensions) { | |
160 FileAssociator associator(base::UTF8ToUTF16(progid)); | |
161 | |
162 // Create a class for this app. | |
163 if (!associator.RegisterClass( | |
164 base::UTF8ToUTF16(file_type_name), icon_path, command_line)) { | |
165 return false; | |
166 } | |
167 | |
168 // Associate each extension that the app can handle with the class. | |
169 for (std::set<std::string>::const_iterator it = file_extensions.begin(); | |
170 it != file_extensions.end(); | |
171 ++it) { | |
172 if (!associator.AssociateExtension(base::UTF8ToUTF16(*it))) | |
173 return false; | |
174 } | |
175 | |
176 // Success. Tell Windows Explorer to update its cache. | |
177 SHChangeNotify( | |
178 SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, NULL); | |
179 return true; | |
180 } | |
181 | |
182 bool DeleteWindowsFileAssociations(const std::string& progid) { | |
jackhou1
2014/08/19 08:16:54
Is there a way to delete the entries added by Asso
Matt Giuca
2014/08/19 08:24:48
We could, but it would mean either:
a) Taking a li
jackhou1
2014/08/19 08:40:03
What if we add a value to the progid key that cont
Matt Giuca
2014/08/22 03:48:58
Yeah that sounds reasonable. But I'd like to do it
| |
183 FileAssociator associator(base::UTF8ToUTF16(progid)); | |
184 | |
185 // Delete the class for this app. | |
186 associator.DeleteClass(); | |
187 | |
188 // Note: there is no need to remove the association from the extension to the | |
189 // class, as Windows will ignore it. See: | |
190 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144148.aspx | |
191 | |
192 // Success. Tell Windows Explorer to update its cache. | |
193 SHChangeNotify( | |
194 SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, NULL); | |
195 return true; | |
196 } | |
OLD | NEW |