199 lines
		
	
	
	
		
			7 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
	
		
			7 KiB
			
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2013 GitHub, Inc.
 | |
| // Use of this source code is governed by the MIT license that can be
 | |
| // found in the LICENSE file.
 | |
| 
 | |
| #include "atom/common/platform_util.h"
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <commdlg.h>
 | |
| #include <dwmapi.h>
 | |
| #include <shellapi.h>
 | |
| #include <shlobj.h>
 | |
| 
 | |
| #include "base/bind.h"
 | |
| #include "base/bind_helpers.h"
 | |
| #include "base/files/file_path.h"
 | |
| #include "base/logging.h"
 | |
| #include "base/strings/string_util.h"
 | |
| #include "base/strings/utf_string_conversions.h"
 | |
| #include "base/win/registry.h"
 | |
| #include "base/win/scoped_co_mem.h"
 | |
| #include "base/win/scoped_comptr.h"
 | |
| #include "base/win/windows_version.h"
 | |
| #include "url/gurl.h"
 | |
| #include "ui/base/win/shell.h"
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Old ShellExecute crashes the process when the command for a given scheme
 | |
| // is empty. This function tells if it is.
 | |
| bool ValidateShellCommandForScheme(const std::string& scheme) {
 | |
|   base::win::RegKey key;
 | |
|   base::string16 registry_path = base::ASCIIToUTF16(scheme) +
 | |
|                                  L"\\shell\\open\\command";
 | |
|   key.Open(HKEY_CLASSES_ROOT, registry_path.c_str(), KEY_READ);
 | |
|   if (!key.Valid())
 | |
|     return false;
 | |
|   DWORD size = 0;
 | |
|   key.ReadValue(NULL, NULL, &size, NULL);
 | |
|   if (size <= 2)
 | |
|     return false;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| namespace platform_util {
 | |
| 
 | |
| void ShowItemInFolder(const base::FilePath& full_path) {
 | |
|   base::FilePath dir = full_path.DirName().AsEndingWithSeparator();
 | |
|   // ParseDisplayName will fail if the directory is "C:", it must be "C:\\".
 | |
|   if (dir.empty())
 | |
|     return;
 | |
| 
 | |
|   typedef HRESULT (WINAPI *SHOpenFolderAndSelectItemsFuncPtr)(
 | |
|       PCIDLIST_ABSOLUTE pidl_Folder,
 | |
|       UINT cidl,
 | |
|       PCUITEMID_CHILD_ARRAY pidls,
 | |
|       DWORD flags);
 | |
| 
 | |
|   static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr =
 | |
|     NULL;
 | |
|   static bool initialize_open_folder_proc = true;
 | |
|   if (initialize_open_folder_proc) {
 | |
|     initialize_open_folder_proc = false;
 | |
|     // The SHOpenFolderAndSelectItems API is exposed by shell32 version 6
 | |
|     // and does not exist in Win2K. We attempt to retrieve this function export
 | |
|     // from shell32 and if it does not exist, we just invoke ShellExecute to
 | |
|     // open the folder thus losing the functionality to select the item in
 | |
|     // the process.
 | |
|     HMODULE shell32_base = GetModuleHandle(L"shell32.dll");
 | |
|     if (!shell32_base) {
 | |
|       NOTREACHED() << " " << __FUNCTION__ << "(): Can't open shell32.dll";
 | |
|       return;
 | |
|     }
 | |
|     open_folder_and_select_itemsPtr =
 | |
|         reinterpret_cast<SHOpenFolderAndSelectItemsFuncPtr>
 | |
|             (GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems"));
 | |
|   }
 | |
|   if (!open_folder_and_select_itemsPtr) {
 | |
|     ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   base::win::ScopedComPtr<IShellFolder> desktop;
 | |
|   HRESULT hr = SHGetDesktopFolder(desktop.Receive());
 | |
|   if (FAILED(hr))
 | |
|     return;
 | |
| 
 | |
|   base::win::ScopedCoMem<ITEMIDLIST> dir_item;
 | |
|   hr = desktop->ParseDisplayName(NULL, NULL,
 | |
|                                  const_cast<wchar_t *>(dir.value().c_str()),
 | |
|                                  NULL, &dir_item, NULL);
 | |
|   if (FAILED(hr))
 | |
|     return;
 | |
| 
 | |
|   base::win::ScopedCoMem<ITEMIDLIST> file_item;
 | |
|   hr = desktop->ParseDisplayName(NULL, NULL,
 | |
|       const_cast<wchar_t *>(full_path.value().c_str()),
 | |
|       NULL, &file_item, NULL);
 | |
|   if (FAILED(hr))
 | |
|     return;
 | |
| 
 | |
|   const ITEMIDLIST* highlight[] = { file_item };
 | |
| 
 | |
|   hr = (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight),
 | |
|                                           highlight, NULL);
 | |
| 
 | |
|   if (FAILED(hr)) {
 | |
|     // On some systems, the above call mysteriously fails with "file not
 | |
|     // found" even though the file is there.  In these cases, ShellExecute()
 | |
|     // seems to work as a fallback (although it won't select the file).
 | |
|     if (hr == ERROR_FILE_NOT_FOUND) {
 | |
|       ShellExecute(NULL, L"open", dir.value().c_str(), NULL, NULL, SW_SHOW);
 | |
|     } else {
 | |
|       LPTSTR message = NULL;
 | |
|       DWORD message_length = FormatMessage(
 | |
|           FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
 | |
|           0, hr, 0, reinterpret_cast<LPTSTR>(&message), 0, NULL);
 | |
|       LOG(WARNING) << " " << __FUNCTION__
 | |
|                    << "(): Can't open full_path = \""
 | |
|                    << full_path.value() << "\""
 | |
|                    << " hr = " << hr
 | |
|                    << " " << reinterpret_cast<LPTSTR>(&message);
 | |
|       if (message)
 | |
|         LocalFree(message);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OpenItem(const base::FilePath& full_path) {
 | |
|   ui::win::OpenItemViaShell(full_path);
 | |
| }
 | |
| 
 | |
| void OpenExternal(const GURL& url) {
 | |
|   // Quote the input scheme to be sure that the command does not have
 | |
|   // parameters unexpected by the external program. This url should already
 | |
|   // have been escaped.
 | |
|   std::string escaped_url = url.spec();
 | |
|   escaped_url.insert(0, "\"");
 | |
|   escaped_url += "\"";
 | |
| 
 | |
|   // According to Mozilla in uriloader/exthandler/win/nsOSHelperAppService.cpp:
 | |
|   // "Some versions of windows (Win2k before SP3, Win XP before SP1) crash in
 | |
|   // ShellExecute on long URLs (bug 161357 on bugzilla.mozilla.org). IE 5 and 6
 | |
|   // support URLS of 2083 chars in length, 2K is safe."
 | |
|   const size_t kMaxUrlLength = 2048;
 | |
|   if (escaped_url.length() > kMaxUrlLength) {
 | |
|     NOTREACHED();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (base::win::GetVersion() < base::win::VERSION_WIN7) {
 | |
|     if (!ValidateShellCommandForScheme(url.scheme()))
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   if (reinterpret_cast<ULONG_PTR>(ShellExecuteA(NULL, "open",
 | |
|                                                 escaped_url.c_str(), NULL, NULL,
 | |
|                                                 SW_SHOWNORMAL)) <= 32) {
 | |
|     // We fail to execute the call. We could display a message to the user.
 | |
|     // TODO(nsylvain): we should also add a dialog to warn on errors. See
 | |
|     // bug 1136923.
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool MoveItemToTrash(const base::FilePath& path) {
 | |
|   // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
 | |
|   // so we have to use wcscpy because wcscpy_s writes non-NULLs
 | |
|   // into the rest of the buffer.
 | |
|   wchar_t double_terminated_path[MAX_PATH + 1] = {0};
 | |
| #pragma warning(suppress:4996)  // don't complain about wcscpy deprecation
 | |
|   wcscpy(double_terminated_path, path.value().c_str());
 | |
| 
 | |
|   SHFILEOPSTRUCT file_operation = {0};
 | |
|   file_operation.wFunc = FO_DELETE;
 | |
|   file_operation.pFrom = double_terminated_path;
 | |
|   file_operation.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION;
 | |
|   int err = SHFileOperation(&file_operation);
 | |
| 
 | |
|   // Since we're passing flags to the operation telling it to be silent,
 | |
|   // it's possible for the operation to be aborted/cancelled without err
 | |
|   // being set (although MSDN doesn't give any scenarios for how this can
 | |
|   // happen).  See MSDN for SHFileOperation and SHFILEOPTSTRUCT.
 | |
|   if (file_operation.fAnyOperationsAborted)
 | |
|     return false;
 | |
| 
 | |
|   // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting
 | |
|   // an empty directory and some return 0x402 when they should be returning
 | |
|   // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402.  Windows 7
 | |
|   // can return DE_INVALIDFILES (0x7C) for nonexistent directories.
 | |
|   return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == DE_INVALIDFILES);
 | |
| }
 | |
| 
 | |
| void Beep() {
 | |
|   MessageBeep(MB_OK);
 | |
| }
 | |
| 
 | |
| }  // namespace platform_util
 | 
