Index: chrome/browser/file_associations_win.cc |
diff --git a/chrome/browser/file_associations_win.cc b/chrome/browser/file_associations_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..605003eeebc8e42b0be5489bf614a822115abe43 |
--- /dev/null |
+++ b/chrome/browser/file_associations_win.cc |
@@ -0,0 +1,196 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/file_associations_win.h" |
robertshield
2014/08/25 02:45:22
I think these files should be called shell_file_as
|
+ |
+#include <shlobj.h> |
+ |
+#include <vector> |
+ |
+#include "base/command_line.h" |
+#include "base/files/file_path.h" |
+#include "base/logging.h" |
+#include "base/strings/string16.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/win/registry.h" |
+ |
+namespace { |
+ |
+// Key relative to HKEY_CURRENT_USER that provides a user-writable alias for |
+// HKEY_CLASSES_ROOT. |
+const wchar_t kRegClassesRootAlias[] = L"Software\\Classes"; |
cpu_(ooo_6.6-7.5)
2014/08/22 22:03:33
I am going to say we have this kind of logic somew
Matt Giuca
2014/08/25 00:58:18
See comment at the top of file_associations_win.h
robertshield
2014/08/25 02:45:22
There is a fair amount of duplication here with th
|
+const wchar_t kRegDefaultIcon[] = L"\\DefaultIcon"; |
+const wchar_t kRegShellOpen[] = L"\\shell\\open\\command"; |
+const wchar_t kRegOpenWithProgids[] = L"\\OpenWithProgids"; |
+ |
+// Writes a value to the Windows registry under HKEY_CURRENT_USER, creating the |
+// key if it does not already exist. If |value_name| is the empty string, writes |
+// to the default value for the given key. If |keep_existing| is true, will not |
+// overwrite an existing value. If an error occurs, logs an error and returns |
+// false. |
+bool WriteCurrentUserRegistryValue(const base::string16& key_name, |
+ const base::string16& value_name, |
+ const base::string16& value_data, |
+ bool keep_existing) { |
+ base::win::RegKey key; |
+ if (key.Create(HKEY_CURRENT_USER, key_name.c_str(), KEY_ALL_ACCESS) != |
+ ERROR_SUCCESS) { |
+ LOG(ERROR) << "Could not create HKEY_CURRENT_USER\\" << key_name; |
+ return false; |
+ } |
+ if (keep_existing && key.HasValue(NULL)) |
+ return true; |
+ if (key.WriteValue(value_name.empty() ? NULL : value_name.c_str(), |
+ value_data.c_str()) != ERROR_SUCCESS) { |
+ LOG(ERROR) << "Could not write HKEY_CURRENT_USER\\" << key_name << "." |
+ << (value_name.empty() ? L"(Default)" : value_name); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+// Writes a value to the Windows registry, overwriting any existing data. |
+bool WriteCurrentUserRegistryValue(const base::string16& key_name, |
+ const base::string16& value_name, |
+ const base::string16& value_data) { |
+ return WriteCurrentUserRegistryValue(key_name, value_name, value_data, false); |
+} |
+ |
+// Class helper for AddWindowsFileAssociations. Objects of this class live only |
+// during the AddWindowsFileAssociations operation. |
+class FileAssociator { |
+ public: |
+ explicit FileAssociator(const base::string16& progid); |
+ |
+ // Creates the registry key for the file association class. This is the key |
+ // that supplies information about the class (such as its name and command |
+ // line), and is referenced by all file extensions sharing the association. |
+ bool RegisterClass(const base::string16& file_type_name, |
+ const base::FilePath& icon_path, |
+ const base::CommandLine& command_line); |
+ |
+ // Deletes the registry key for the file association class. |
+ bool DeleteClass(); |
+ |
+ // Adds a file extension as a supported type of this class. |ext| is the file |
+ // extension (without a leading '.'). |
+ bool AssociateExtension(const base::string16& ext); |
+ |
+ private: |
+ // The class name for this association. |
+ base::string16 progid_; |
+}; |
+ |
+FileAssociator::FileAssociator(const base::string16& progid) : progid_(progid) { |
+} |
+ |
+bool FileAssociator::RegisterClass(const base::string16& file_type_name, |
+ const base::FilePath& icon_path, |
+ const base::CommandLine& command_line) { |
+ // Open or create a key HKEY_CLASSES_ROOT\PROGID. |
+ base::string16 class_key_name = kRegClassesRootAlias; |
+ class_key_name += L"\\"; |
+ class_key_name += progid_; |
+ |
+ // Set the file type's user-visible name. |
+ if (!WriteCurrentUserRegistryValue(class_key_name, L"", file_type_name)) |
+ return false; |
+ |
+ // Set the file type's icon. |
+ if (!WriteCurrentUserRegistryValue( |
+ class_key_name + kRegDefaultIcon, L"", icon_path.value() + L",0")) { |
+ return false; |
+ } |
+ |
+ // Set the command line to execute for the "open" verb. |
+ if (!WriteCurrentUserRegistryValue(class_key_name + kRegShellOpen, |
+ L"", |
+ command_line.GetCommandLineString())) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool FileAssociator::DeleteClass() { |
+ // Delete the key HKEY_CLASSES_ROOT\PROGID. |
+ base::win::RegKey key; |
+ if (key.Open(HKEY_CURRENT_USER, kRegClassesRootAlias, KEY_ALL_ACCESS) != |
+ ERROR_SUCCESS) { |
+ LOG(ERROR) << "Could not open HKEY_CURRENT_USER\\" << kRegClassesRootAlias; |
+ return false; |
+ } |
+ |
+ if (key.DeleteKey(progid_.c_str()) != ERROR_SUCCESS) { |
+ LOG(ERROR) << "Could not delete HKEY_CURRENT_USER\\" << kRegClassesRootAlias |
+ << "\\" << progid_; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool FileAssociator::AssociateExtension(const base::string16& ext) { |
+ // Open or create a key HKEY_CLASSES_ROOT\.EXT. |
+ base::string16 ext_key_name = kRegClassesRootAlias; |
+ ext_key_name += L"\\."; |
+ ext_key_name += ext; |
+ |
+ // Under OpenWithProgids, create an empty value with this class's name. |
robertshield
2014/08/25 02:45:22
Add a comment that this only for WinXP support.
Matt Giuca
2014/08/25 03:15:42
I'm lost... this is the only place where I say "as
robertshield
2014/08/25 13:13:45
Ick, sorry, I confused this vs OpenWithList which
|
+ if (!WriteCurrentUserRegistryValue( |
+ ext_key_name + kRegOpenWithProgids, progid_, L"")) { |
+ return false; |
+ } |
+ |
+ // If the extension key has no default value (the default class for this |
+ // file type) is blank, register this application as the default handler. |
+ // Otherwise, leave the existing default as-is. |
+ return WriteCurrentUserRegistryValue(ext_key_name, L"", progid_, true); |
+} |
+ |
+} // namespace |
+ |
+bool AddWindowsFileAssociations(const std::string& progid, |
+ const base::CommandLine& command_line, |
+ const std::string& file_type_name, |
+ const base::FilePath& icon_path, |
+ const std::set<std::string>& file_extensions) { |
+ FileAssociator associator(base::UTF8ToUTF16(progid)); |
+ |
+ // Create a class for this app. |
+ if (!associator.RegisterClass( |
+ base::UTF8ToUTF16(file_type_name), icon_path, command_line)) { |
+ return false; |
+ } |
+ |
+ // Associate each extension that the app can handle with the class. |
+ for (std::set<std::string>::const_iterator it = file_extensions.begin(); |
+ it != file_extensions.end(); |
+ ++it) { |
+ if (!associator.AssociateExtension(base::UTF8ToUTF16(*it))) |
+ return false; |
+ } |
+ |
+ // Success. Tell Windows Explorer to update its cache. |
+ SHChangeNotify( |
+ SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, NULL); |
robertshield
2014/08/25 02:45:22
The CL makes no mention of when/how this will be c
|
+ return true; |
+} |
+ |
+bool DeleteWindowsFileAssociations(const std::string& progid) { |
+ FileAssociator associator(base::UTF8ToUTF16(progid)); |
+ |
+ // Delete the class for this app. |
+ associator.DeleteClass(); |
+ |
+ // Note: there is no need to remove the association from the extension to the |
+ // class, as Windows will ignore it. See: |
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/cc144148.aspx |
+ |
+ // Success. Tell Windows Explorer to update its cache. |
+ SHChangeNotify( |
+ SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, NULL, NULL); |
+ return true; |
+} |