Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / base / win / shortcut.cc
blobeb7a72c6044dc63f1488891700416d8f71799f11
1 // Copyright (c) 2012 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.
5 #include "base/win/shortcut.h"
7 #include <shellapi.h>
8 #include <shlobj.h>
9 #include <propkey.h>
11 #include "base/file_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/win/scoped_comptr.h"
14 #include "base/win/win_util.h"
15 #include "base/win/windows_version.h"
17 namespace base {
18 namespace win {
20 namespace {
22 // Initializes |i_shell_link| and |i_persist_file| (releasing them first if they
23 // are already initialized).
24 // If |shortcut| is not NULL, loads |shortcut| into |i_persist_file|.
25 // If any of the above steps fail, both |i_shell_link| and |i_persist_file| will
26 // be released.
27 void InitializeShortcutInterfaces(
28 const wchar_t* shortcut,
29 ScopedComPtr<IShellLink>* i_shell_link,
30 ScopedComPtr<IPersistFile>* i_persist_file) {
31 i_shell_link->Release();
32 i_persist_file->Release();
33 if (FAILED(i_shell_link->CreateInstance(CLSID_ShellLink, NULL,
34 CLSCTX_INPROC_SERVER)) ||
35 FAILED(i_persist_file->QueryFrom(*i_shell_link)) ||
36 (shortcut && FAILED((*i_persist_file)->Load(shortcut, STGM_READWRITE)))) {
37 i_shell_link->Release();
38 i_persist_file->Release();
42 } // namespace
44 bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path,
45 const ShortcutProperties& properties,
46 ShortcutOperation operation) {
47 base::ThreadRestrictions::AssertIOAllowed();
49 // A target is required unless |operation| is SHORTCUT_UPDATE_EXISTING.
50 if (operation != SHORTCUT_UPDATE_EXISTING &&
51 !(properties.options & ShortcutProperties::PROPERTIES_TARGET)) {
52 NOTREACHED();
53 return false;
56 bool shortcut_existed = file_util::PathExists(shortcut_path);
58 // Interfaces to the old shortcut when replacing an existing shortcut.
59 ScopedComPtr<IShellLink> old_i_shell_link;
60 ScopedComPtr<IPersistFile> old_i_persist_file;
62 // Interfaces to the shortcut being created/updated.
63 ScopedComPtr<IShellLink> i_shell_link;
64 ScopedComPtr<IPersistFile> i_persist_file;
65 switch (operation) {
66 case SHORTCUT_CREATE_ALWAYS:
67 InitializeShortcutInterfaces(NULL, &i_shell_link, &i_persist_file);
68 break;
69 case SHORTCUT_UPDATE_EXISTING:
70 InitializeShortcutInterfaces(shortcut_path.value().c_str(), &i_shell_link,
71 &i_persist_file);
72 break;
73 case SHORTCUT_REPLACE_EXISTING:
74 InitializeShortcutInterfaces(shortcut_path.value().c_str(),
75 &old_i_shell_link, &old_i_persist_file);
76 // Confirm |shortcut_path| exists and is a shortcut by verifying
77 // |old_i_persist_file| was successfully initialized in the call above. If
78 // so, initialize the interfaces to begin writing a new shortcut (to
79 // overwrite the current one if successful).
80 if (old_i_persist_file.get())
81 InitializeShortcutInterfaces(NULL, &i_shell_link, &i_persist_file);
82 break;
83 default:
84 NOTREACHED();
87 // Return false immediately upon failure to initialize shortcut interfaces.
88 if (!i_persist_file.get())
89 return false;
91 if ((properties.options & ShortcutProperties::PROPERTIES_TARGET) &&
92 FAILED(i_shell_link->SetPath(properties.target.value().c_str()))) {
93 return false;
96 if ((properties.options & ShortcutProperties::PROPERTIES_WORKING_DIR) &&
97 FAILED(i_shell_link->SetWorkingDirectory(
98 properties.working_dir.value().c_str()))) {
99 return false;
102 if (properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
103 if (FAILED(i_shell_link->SetArguments(properties.arguments.c_str())))
104 return false;
105 } else if (old_i_persist_file.get()) {
106 wchar_t current_arguments[MAX_PATH] = {0};
107 if (SUCCEEDED(old_i_shell_link->GetArguments(current_arguments,
108 MAX_PATH))) {
109 i_shell_link->SetArguments(current_arguments);
113 if ((properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) &&
114 FAILED(i_shell_link->SetDescription(properties.description.c_str()))) {
115 return false;
118 if ((properties.options & ShortcutProperties::PROPERTIES_ICON) &&
119 FAILED(i_shell_link->SetIconLocation(properties.icon.value().c_str(),
120 properties.icon_index))) {
121 return false;
124 bool has_app_id =
125 (properties.options & ShortcutProperties::PROPERTIES_APP_ID) != 0;
126 bool has_dual_mode =
127 (properties.options & ShortcutProperties::PROPERTIES_DUAL_MODE) != 0;
128 if ((has_app_id || has_dual_mode) &&
129 GetVersion() >= VERSION_WIN7) {
130 ScopedComPtr<IPropertyStore> property_store;
131 if (FAILED(property_store.QueryFrom(i_shell_link)) || !property_store.get())
132 return false;
134 if (has_app_id &&
135 !SetAppIdForPropertyStore(property_store, properties.app_id.c_str())) {
136 return false;
138 if (has_dual_mode &&
139 !SetBooleanValueForPropertyStore(property_store,
140 PKEY_AppUserModel_IsDualMode,
141 properties.dual_mode)) {
142 return false;
146 // Release the interfaces to the old shortcut to make sure it doesn't prevent
147 // overwriting it if needed.
148 old_i_persist_file.Release();
149 old_i_shell_link.Release();
151 HRESULT result = i_persist_file->Save(shortcut_path.value().c_str(), TRUE);
153 // Release the interfaces in case the SHChangeNotify call below depends on
154 // the operations above being fully completed.
155 i_persist_file.Release();
156 i_shell_link.Release();
158 // If we successfully created/updated the icon, notify the shell that we have
159 // done so.
160 const bool succeeded = SUCCEEDED(result);
161 if (succeeded) {
162 if (shortcut_existed) {
163 // TODO(gab): SHCNE_UPDATEITEM might be sufficient here; further testing
164 // required.
165 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
166 } else {
167 SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, shortcut_path.value().c_str(),
168 NULL);
172 return succeeded;
175 bool ResolveShortcut(const FilePath& shortcut_path,
176 FilePath* target_path,
177 string16* args) {
178 base::ThreadRestrictions::AssertIOAllowed();
180 HRESULT result;
181 ScopedComPtr<IShellLink> i_shell_link;
183 // Get pointer to the IShellLink interface.
184 result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
185 CLSCTX_INPROC_SERVER);
186 if (FAILED(result))
187 return false;
189 ScopedComPtr<IPersistFile> persist;
190 // Query IShellLink for the IPersistFile interface.
191 result = persist.QueryFrom(i_shell_link);
192 if (FAILED(result))
193 return false;
195 // Load the shell link.
196 result = persist->Load(shortcut_path.value().c_str(), STGM_READ);
197 if (FAILED(result))
198 return false;
200 WCHAR temp[MAX_PATH];
201 if (target_path) {
202 // Try to find the target of a shortcut.
203 result = i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH);
204 if (FAILED(result))
205 return false;
207 result = i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY);
208 if (FAILED(result))
209 return false;
211 *target_path = FilePath(temp);
214 if (args) {
215 result = i_shell_link->GetArguments(temp, MAX_PATH);
216 if (FAILED(result))
217 return false;
219 *args = string16(temp);
221 return true;
224 bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
225 base::ThreadRestrictions::AssertIOAllowed();
227 // "Pin to taskbar" is only supported after Win7.
228 if (GetVersion() < VERSION_WIN7)
229 return false;
231 int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
232 NULL, NULL, 0));
233 return result > 32;
236 bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
237 base::ThreadRestrictions::AssertIOAllowed();
239 // "Unpin from taskbar" is only supported after Win7.
240 if (base::win::GetVersion() < base::win::VERSION_WIN7)
241 return false;
243 int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
244 shortcut, NULL, NULL, 0));
245 return result > 32;
248 } // namespace win
249 } // namespace base