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"
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"
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
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();
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
)) {
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
;
66 case SHORTCUT_CREATE_ALWAYS
:
67 InitializeShortcutInterfaces(NULL
, &i_shell_link
, &i_persist_file
);
69 case SHORTCUT_UPDATE_EXISTING
:
70 InitializeShortcutInterfaces(shortcut_path
.value().c_str(), &i_shell_link
,
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
);
87 // Return false immediately upon failure to initialize shortcut interfaces.
88 if (!i_persist_file
.get())
91 if ((properties
.options
& ShortcutProperties::PROPERTIES_TARGET
) &&
92 FAILED(i_shell_link
->SetPath(properties
.target
.value().c_str()))) {
96 if ((properties
.options
& ShortcutProperties::PROPERTIES_WORKING_DIR
) &&
97 FAILED(i_shell_link
->SetWorkingDirectory(
98 properties
.working_dir
.value().c_str()))) {
102 if (properties
.options
& ShortcutProperties::PROPERTIES_ARGUMENTS
) {
103 if (FAILED(i_shell_link
->SetArguments(properties
.arguments
.c_str())))
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
,
109 i_shell_link
->SetArguments(current_arguments
);
113 if ((properties
.options
& ShortcutProperties::PROPERTIES_DESCRIPTION
) &&
114 FAILED(i_shell_link
->SetDescription(properties
.description
.c_str()))) {
118 if ((properties
.options
& ShortcutProperties::PROPERTIES_ICON
) &&
119 FAILED(i_shell_link
->SetIconLocation(properties
.icon
.value().c_str(),
120 properties
.icon_index
))) {
125 (properties
.options
& ShortcutProperties::PROPERTIES_APP_ID
) != 0;
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())
135 !SetAppIdForPropertyStore(property_store
, properties
.app_id
.c_str())) {
139 !SetBooleanValueForPropertyStore(property_store
,
140 PKEY_AppUserModel_IsDualMode
,
141 properties
.dual_mode
)) {
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
160 const bool succeeded
= SUCCEEDED(result
);
162 if (shortcut_existed
) {
163 // TODO(gab): SHCNE_UPDATEITEM might be sufficient here; further testing
165 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
167 SHChangeNotify(SHCNE_CREATE
, SHCNF_PATH
, shortcut_path
.value().c_str(),
175 bool ResolveShortcut(const FilePath
& shortcut_path
,
176 FilePath
* target_path
,
178 base::ThreadRestrictions::AssertIOAllowed();
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
);
189 ScopedComPtr
<IPersistFile
> persist
;
190 // Query IShellLink for the IPersistFile interface.
191 result
= persist
.QueryFrom(i_shell_link
);
195 // Load the shell link.
196 result
= persist
->Load(shortcut_path
.value().c_str(), STGM_READ
);
200 WCHAR temp
[MAX_PATH
];
202 // Try to find the target of a shortcut.
203 result
= i_shell_link
->Resolve(0, SLR_NO_UI
| SLR_NOSEARCH
);
207 result
= i_shell_link
->GetPath(temp
, MAX_PATH
, NULL
, SLGP_UNCPRIORITY
);
211 *target_path
= FilePath(temp
);
215 result
= i_shell_link
->GetArguments(temp
, MAX_PATH
);
219 *args
= string16(temp
);
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
)
231 int result
= reinterpret_cast<int>(ShellExecute(NULL
, L
"taskbarpin", shortcut
,
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
)
243 int result
= reinterpret_cast<int>(ShellExecute(NULL
, L
"taskbarunpin",
244 shortcut
, NULL
, NULL
, 0));