| 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 |