Chromium Code Reviews| 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..d2a6f4574b4fa7c7e3f9b9718c966110a95ab31c |
| --- /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" |
| + |
| +#include <vector> |
| + |
| +#include <shlobj.h> |
| + |
| +#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"; |
| +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; |
|
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
|
| + 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: |
| + 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. |
| + 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); |
| + return true; |
| +} |
| + |
| +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
|
| + 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; |
| +} |