OLD | NEW |
| (Empty) |
1 // Copyright 2003-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Shell functions | |
17 | |
18 #include "omaha/base/shell.h" | |
19 | |
20 #include <shlobj.h> | |
21 #include <shellapi.h> | |
22 #include "omaha/base/app_util.h" | |
23 #include "omaha/base/browser_utils.h" | |
24 #include "omaha/base/const_utils.h" | |
25 #include "omaha/base/debug.h" | |
26 #include "omaha/base/error.h" | |
27 #include "omaha/base/file.h" | |
28 #include "omaha/base/logging.h" | |
29 #include "omaha/base/path.h" | |
30 #include "omaha/base/reg_key.h" | |
31 #include "omaha/base/scoped_any.h" | |
32 #include "omaha/base/string.h" | |
33 #include "omaha/base/system.h" | |
34 #include "omaha/base/utils.h" | |
35 | |
36 namespace omaha { | |
37 | |
38 // create and store a shortcut | |
39 // uses shell IShellLink and IPersistFile interfaces | |
40 HRESULT Shell::CreateLink(const TCHAR *source, | |
41 const TCHAR *destination, | |
42 const TCHAR *working_dir, | |
43 const TCHAR *arguments, | |
44 const TCHAR *description, | |
45 WORD hotkey_virtual_key_code, | |
46 WORD hotkey_modifiers, | |
47 const TCHAR *icon) { | |
48 ASSERT1(source); | |
49 ASSERT1(destination); | |
50 ASSERT1(working_dir); | |
51 ASSERT1(arguments); | |
52 ASSERT1(description); | |
53 | |
54 scoped_co_init co_init(COINIT_APARTMENTTHREADED); | |
55 HRESULT hr = co_init.hresult(); | |
56 if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) { | |
57 UTIL_LOG(LEVEL_ERROR, (_T("[Shell::CreateLink - failed to co_init]"), hr)); | |
58 return hr; | |
59 } | |
60 | |
61 UTIL_LOG(L1, (_T("[Create shell link]") | |
62 _T("[source %s dest %s dir %s arg %s desc %s hotkey %x:%x]"), | |
63 source, destination, working_dir, arguments, description, | |
64 hotkey_modifiers, hotkey_virtual_key_code)); | |
65 | |
66 // Get a pointer to the IShellLink interface | |
67 CComPtr<IShellLink> shell_link; | |
68 | |
69 RET_IF_FAILED(shell_link.CoCreateInstance(CLSID_ShellLink)); | |
70 ASSERT(shell_link, (L"")); | |
71 | |
72 // Set the path to the shortcut target and add the description | |
73 VERIFY1(SUCCEEDED(shell_link->SetPath(source))); | |
74 VERIFY1(SUCCEEDED(shell_link->SetArguments(arguments))); | |
75 VERIFY1(SUCCEEDED(shell_link->SetDescription(description))); | |
76 VERIFY1(SUCCEEDED(shell_link->SetWorkingDirectory(working_dir))); | |
77 | |
78 // If we are given an icon, then set it | |
79 // For now, we always use the first icon if this happens to have multiple ones | |
80 if (icon) { | |
81 VERIFY1(SUCCEEDED(shell_link->SetIconLocation(icon, 0))); | |
82 } | |
83 | |
84 // C4201: nonstandard extension used : nameless struct/union | |
85 #pragma warning(disable : 4201) | |
86 union { | |
87 WORD flags; | |
88 struct { // little-endian machine: | |
89 WORD virtual_key:8; // low order byte | |
90 WORD modifiers:8; // high order byte | |
91 }; | |
92 } hot_key; | |
93 #pragma warning(default : 4201) | |
94 | |
95 hot_key.virtual_key = hotkey_virtual_key_code; | |
96 hot_key.modifiers = hotkey_modifiers; | |
97 | |
98 if (hot_key.flags) { | |
99 shell_link->SetHotkey(hot_key.flags); | |
100 } | |
101 | |
102 // Query IShellLink for the IPersistFile interface for saving the shortcut in | |
103 // persistent storage | |
104 CComQIPtr<IPersistFile> persist_file(shell_link); | |
105 if (!persist_file) | |
106 return E_FAIL; | |
107 | |
108 // Save the link by calling IPersistFile::Save | |
109 RET_IF_FAILED(persist_file->Save(destination, TRUE)); | |
110 | |
111 return S_OK; | |
112 } | |
113 | |
114 HRESULT Shell::RemoveLink(const TCHAR *link) { | |
115 ASSERT(link, (L"")); | |
116 ASSERT(*link, (L"")); | |
117 | |
118 return File::Remove(link); | |
119 } | |
120 | |
121 // Open a URL in a new browser window | |
122 HRESULT Shell::OpenLinkInNewWindow(const TCHAR* url, UseBrowser use_browser) { | |
123 ASSERT1(url); | |
124 | |
125 HRESULT hr = S_OK; | |
126 CString browser_path; | |
127 | |
128 // Try to open with default browser | |
129 if (use_browser == USE_DEFAULT_BROWSER) { | |
130 // Load full browser path from regkey | |
131 hr = GetDefaultBrowserPath(&browser_path); | |
132 | |
133 // If there is a default browser and it is not AOL, load the url in that | |
134 // browser | |
135 if (SUCCEEDED(hr) && !String_Contains(browser_path, _T("aol"))) { | |
136 if (!browser_path.IsEmpty()) { | |
137 // Have we figured out how to append the URL onto the browser path? | |
138 bool acceptable_url = false; | |
139 | |
140 if (ReplaceCString(browser_path, _T("\"%1\""), url)) { | |
141 // the "browser.exe "%1"" case | |
142 acceptable_url = true; | |
143 } else if (ReplaceCString(browser_path, _T("%1"), url)) { | |
144 // the "browser.exe %1 "case | |
145 acceptable_url = true; | |
146 } else if (ReplaceCString(browser_path, _T("-nohome"), url)) { | |
147 // the "browser.exe -nohome" case | |
148 acceptable_url = true; | |
149 } else { | |
150 // the browser.exe case. | |
151 // simply append the quoted url. | |
152 EnclosePath(&browser_path); | |
153 browser_path.AppendChar(_T(' ')); | |
154 CString quoted_url(url); | |
155 EnclosePath("ed_url); | |
156 browser_path.Append(quoted_url); | |
157 acceptable_url = true; | |
158 } | |
159 | |
160 if (acceptable_url) { | |
161 hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL); | |
162 if (SUCCEEDED(hr)) { | |
163 return S_OK; | |
164 } else { | |
165 UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]") | |
166 _T("[failed to start default browser to open url]") | |
167 _T("[%s][0x%x]"), url, hr)); | |
168 } | |
169 } | |
170 } | |
171 } | |
172 } | |
173 | |
174 // Try to open with IE if can't open with default browser or required | |
175 if (use_browser == USE_DEFAULT_BROWSER || | |
176 use_browser == USE_INTERNET_EXPLORER) { | |
177 hr = RegKey::GetValue(kRegKeyIeClass, kRegValueIeClass, &browser_path); | |
178 if (SUCCEEDED(hr)) { | |
179 hr = System::ShellExecuteProcess(browser_path, url, NULL, NULL); | |
180 if (SUCCEEDED(hr)) { | |
181 return S_OK; | |
182 } else { | |
183 UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]") | |
184 _T("[failed to start IE to open url][%s][0x%x]"), | |
185 url, hr)); | |
186 } | |
187 } | |
188 } | |
189 | |
190 // Try to open with Firefox if can't open with default browser or required | |
191 if (use_browser == USE_DEFAULT_BROWSER || use_browser == USE_FIREFOX) { | |
192 hr = RegKey::GetValue(kRegKeyFirefox, kRegValueFirefox, &browser_path); | |
193 if (SUCCEEDED(hr) && !browser_path.IsEmpty()) { | |
194 ReplaceCString(browser_path, _T("%1"), url); | |
195 hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL); | |
196 if (SUCCEEDED(hr)) { | |
197 return S_OK; | |
198 } else { | |
199 UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]") | |
200 _T("[failed to start Firefox to open url][%s][0x%x]"), | |
201 url, hr)); | |
202 } | |
203 } | |
204 } | |
205 | |
206 // ShellExecute the url directly as a last resort | |
207 hr = Shell::Execute(url); | |
208 if (FAILED(hr)) { | |
209 UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]") | |
210 _T("[failed to run ShellExecute to open url][%s][0x%x]"), | |
211 url, hr)); | |
212 } | |
213 | |
214 return hr; | |
215 } | |
216 | |
217 HRESULT Shell::Execute(const TCHAR* file) { | |
218 ASSERT1(file); | |
219 | |
220 // Prepare everything required for ::ShellExecuteEx(). | |
221 SHELLEXECUTEINFO sei; | |
222 SetZero(sei); | |
223 sei.cbSize = sizeof(sei); | |
224 sei.fMask = SEE_MASK_FLAG_NO_UI | // Do not display an error message box. | |
225 SEE_MASK_NOZONECHECKS | // Do not perform a zone check. | |
226 SEE_MASK_NOASYNC; // Wait to complete before returning. | |
227 // Pass NULL for hwnd. This will have ShellExecuteExEnsureParent() | |
228 // create a dummy parent window for us. | |
229 // sei.hwnd = NULL; | |
230 sei.lpVerb = _T("open"); | |
231 sei.lpFile = file; | |
232 // No parameters to pass | |
233 // sei.lpParameters = NULL; | |
234 // Use parent's starting directory | |
235 // sei.lpDirectory = NULL; | |
236 sei.nShow = SW_SHOWNORMAL; | |
237 | |
238 // Use ShellExecuteExEnsureParent to ensure that we always have a parent HWND. | |
239 // We need to use the HWND Property to be acknowledged as a Foreground | |
240 // Application on Vista. Otherwise, the elevation prompt will appear minimized | |
241 // on the taskbar. | |
242 if (!ShellExecuteExEnsureParent(&sei)) { | |
243 HRESULT hr(HRESULTFromLastError()); | |
244 ASSERT(false, | |
245 (_T("Shell::Execute - ShellExecuteEx failed][%s][0x%x]"), file, hr)); | |
246 return hr; | |
247 } | |
248 | |
249 return S_OK; | |
250 } | |
251 | |
252 HRESULT Shell::BasicGetSpecialFolder(DWORD csidl, CString* folder_path) { | |
253 ASSERT1(folder_path); | |
254 | |
255 // Get a ITEMIDLIST* (called a PIDL in the MSDN documentation) to the | |
256 // special folder | |
257 scoped_any<ITEMIDLIST*, close_co_task_free> folder_location; | |
258 RET_IF_FAILED(::SHGetFolderLocation(NULL, | |
259 csidl, | |
260 NULL, | |
261 0, | |
262 address(folder_location))); | |
263 ASSERT(get(folder_location), (_T(""))); | |
264 | |
265 // Get an interface to the Desktop folder | |
266 CComPtr<IShellFolder> desktop_folder; | |
267 RET_IF_FAILED(::SHGetDesktopFolder(&desktop_folder)); | |
268 ASSERT1(desktop_folder); | |
269 | |
270 // Ask the desktop for the display name of the special folder | |
271 STRRET str_return; | |
272 SetZero(str_return); | |
273 str_return.uType = STRRET_WSTR; | |
274 RET_IF_FAILED(desktop_folder->GetDisplayNameOf(get(folder_location), | |
275 SHGDN_FORPARSING, | |
276 &str_return)); | |
277 | |
278 // Get the display name of the special folder and return it | |
279 scoped_any<wchar_t*, close_co_task_free> folder_name; | |
280 RET_IF_FAILED(::StrRetToStr(&str_return, | |
281 get(folder_location), | |
282 address(folder_name))); | |
283 *folder_path = get(folder_name); | |
284 | |
285 return S_OK; | |
286 } | |
287 | |
288 HRESULT Shell::GetSpecialFolder(DWORD csidl, | |
289 bool create_if_missing, | |
290 CString* folder_path) { | |
291 ASSERT(folder_path, (L"")); | |
292 | |
293 HRESULT hr = Shell::BasicGetSpecialFolder(csidl, folder_path); | |
294 | |
295 // If the folder does not exist, ::SHGetFolderLocation may return error | |
296 // code ERROR_FILE_NOT_FOUND. | |
297 if (create_if_missing) { | |
298 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || | |
299 (SUCCEEDED(hr) && !File::Exists(*folder_path))) { | |
300 hr = Shell::BasicGetSpecialFolder(csidl | CSIDL_FLAG_CREATE, folder_path); | |
301 } | |
302 } | |
303 ASSERT(FAILED(hr) || File::Exists(*folder_path), (_T(""))); | |
304 | |
305 return hr; | |
306 } | |
307 | |
308 #pragma warning(disable : 4510 4610) | |
309 // C4510: default constructor could not be generated | |
310 // C4610: struct can never be instantiated - user defined constructor required | |
311 struct { | |
312 const TCHAR* name; | |
313 const DWORD csidl; | |
314 } const folder_mappings[] = { | |
315 L"APPDATA", CSIDL_APPDATA, | |
316 L"DESKTOP", CSIDL_DESKTOPDIRECTORY, | |
317 L"LOCALAPPDATA", CSIDL_LOCAL_APPDATA, | |
318 L"MYMUSIC", CSIDL_MYMUSIC, | |
319 L"MYPICTURES", CSIDL_MYPICTURES, | |
320 L"PROGRAMFILES", CSIDL_PROGRAM_FILES, | |
321 L"PROGRAMFILESCOMMON", CSIDL_PROGRAM_FILES_COMMON, | |
322 L"PROGRAMS", CSIDL_PROGRAMS, | |
323 L"STARTMENU", CSIDL_STARTMENU, | |
324 L"STARTUP", CSIDL_STARTUP, | |
325 L"SYSTEM", CSIDL_SYSTEM, | |
326 L"WINDOWS", CSIDL_WINDOWS, | |
327 }; | |
328 #pragma warning(default : 4510 4610) | |
329 | |
330 HRESULT Shell::GetSpecialFolderKeywordsMapping( | |
331 std::map<CString, CString>* special_folders_map) { | |
332 ASSERT1(special_folders_map); | |
333 | |
334 special_folders_map->clear(); | |
335 | |
336 for (size_t i = 0; i < arraysize(folder_mappings); ++i) { | |
337 CString name(folder_mappings[i].name); | |
338 DWORD csidl(folder_mappings[i].csidl); | |
339 CString folder; | |
340 HRESULT hr = GetSpecialFolder(csidl, false, &folder); | |
341 if (FAILED(hr)) { | |
342 UTIL_LOG(LE, (_T("[Shell::GetSpecialFolderKeywordsMapping]") | |
343 _T("[failed to retrieve %s]"), name)); | |
344 continue; | |
345 } | |
346 special_folders_map->insert(std::make_pair(name, folder)); | |
347 } | |
348 | |
349 // Get the current module directory | |
350 CString module_dir = app_util::GetModuleDirectory(::GetModuleHandle(NULL)); | |
351 ASSERT1(module_dir.GetLength() > 0); | |
352 special_folders_map->insert(std::make_pair(_T("CURRENTMODULEDIR"), | |
353 module_dir)); | |
354 | |
355 return S_OK; | |
356 } | |
357 | |
358 HRESULT Shell::DeleteDirectory(const TCHAR* dir) { | |
359 ASSERT1(dir && *dir); | |
360 | |
361 if (!SafeDirectoryNameForDeletion(dir)) { | |
362 return E_INVALIDARG; | |
363 } | |
364 | |
365 uint32 dir_len = lstrlen(dir); | |
366 if (dir_len >= MAX_PATH) { | |
367 return E_INVALIDARG; | |
368 } | |
369 | |
370 // the 'from' must be double-terminated with 0. Reserve space for one more | |
371 // zero at the end | |
372 TCHAR from[MAX_PATH + 1] = {0}; | |
373 lstrcpyn(from, dir, MAX_PATH); | |
374 from[1 + dir_len] = 0; // the second zero terminator. | |
375 | |
376 SHFILEOPSTRUCT file_op = {0}; | |
377 | |
378 file_op.hwnd = 0; | |
379 file_op.wFunc = FO_DELETE; | |
380 file_op.pFrom = from; | |
381 file_op.pTo = 0; | |
382 file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; | |
383 | |
384 // ::SHFileOperation returns non-zero on errors | |
385 return ::SHFileOperation(&file_op) ? HRESULTFromLastError() : S_OK; | |
386 } | |
387 | |
388 HRESULT Shell::GetApplicationExecutablePath(const CString& exe, | |
389 CString* path) { | |
390 ASSERT1(path); | |
391 | |
392 CString reg_key_name = AppendRegKeyPath(kRegKeyApplicationPath, exe); | |
393 return RegKey::GetValue(reg_key_name, kRegKeyPathValue, path); | |
394 } | |
395 | |
396 } // namespace omaha | |
397 | |
OLD | NEW |