1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/installer/util/taskbar_util.h"
#include <objbase.h>
#include <shellapi.h>
#include <shlobj.h>
#include <wrl/client.h>
#include "base/files/file_path.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
#include "chrome/install_static/install_util.h"
namespace {
enum class PinnedListModifyCaller { kExplorer = 4 };
constexpr GUID CLSID_TaskbandPin = {
0x90aa3a4e,
0x1cba,
0x4233,
{0xb8, 0xbb, 0x53, 0x57, 0x73, 0xd4, 0x84, 0x49}};
constexpr wchar_t kInstallerPinned[] = L"InstallerPinned";
// Set by tests to control whether or not pinning to taskbar is allowed.
CanPinToTaskBarDelegateFunctionPtr g_can_pin_to_taskbar;
// Undocumented COM interface for manipulating taskbar pinned list.
class __declspec(uuid("0DD79AE2-D156-45D4-9EEB-3B549769E940")) IPinnedList3
: public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE EnumObjects() = 0;
virtual HRESULT STDMETHODCALLTYPE GetPinnableInfo() = 0;
virtual HRESULT STDMETHODCALLTYPE IsPinnable() = 0;
virtual HRESULT STDMETHODCALLTYPE Resolve() = 0;
virtual HRESULT STDMETHODCALLTYPE LegacyModify() = 0;
virtual HRESULT STDMETHODCALLTYPE GetChangeCount() = 0;
virtual HRESULT STDMETHODCALLTYPE IsPinned(PCIDLIST_ABSOLUTE) = 0;
virtual HRESULT STDMETHODCALLTYPE GetPinnedItem() = 0;
virtual HRESULT STDMETHODCALLTYPE GetAppIDForPinnedItem() = 0;
virtual HRESULT STDMETHODCALLTYPE ItemChangeNotify() = 0;
virtual HRESULT STDMETHODCALLTYPE UpdateForRemovedItemsAsNecessary() = 0;
virtual HRESULT STDMETHODCALLTYPE PinShellLink() = 0;
virtual HRESULT STDMETHODCALLTYPE GetPinnedItemForAppID() = 0;
virtual HRESULT STDMETHODCALLTYPE Modify(PCIDLIST_ABSOLUTE unpin,
PCIDLIST_ABSOLUTE pin,
PinnedListModifyCaller caller) = 0;
};
// ScopedPIDLFromPath class, and the idea of using IPinnedList3::Modify,
// are thanks to Gee Law <https://geelaw.blog/entries/msedge-pins/>
class ScopedPIDLFromPath {
public:
explicit ScopedPIDLFromPath(PCWSTR path)
: p_id_list_(ILCreateFromPath(path)) {}
~ScopedPIDLFromPath() {
if (p_id_list_)
ILFree(p_id_list_);
}
PIDLIST_ABSOLUTE Get() const { return p_id_list_; }
private:
PIDLIST_ABSOLUTE const p_id_list_;
};
// Returns the taskbar pinned list if successful, an empty ComPtr otherwise.
Microsoft::WRL::ComPtr<IPinnedList3> GetTaskbarPinnedList() {
if (base::win::GetVersion() < base::win::Version::WIN10_RS5)
return nullptr;
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list;
if (FAILED(CoCreateInstance(CLSID_TaskbandPin, nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pinned_list)))) {
return nullptr;
}
return pinned_list;
}
// Use IPinnedList3 to pin shortcut to taskbar on WIN10_RS5 and above.
// Returns true if pinning was successful.
// static
bool PinShortcutWithIPinnedList3(const base::FilePath& shortcut) {
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
if (!pinned_list)
return false;
ScopedPIDLFromPath item_id_list(shortcut.value().data());
HRESULT hr = pinned_list->Modify(nullptr, item_id_list.Get(),
PinnedListModifyCaller::kExplorer);
return SUCCEEDED(hr);
}
// Use IPinnedList3 to unpin shortcut to taskbar on WIN10_RS5 and above.
// Returns true if unpinning was successful.
// static
bool UnpinShortcutWithIPinnedList3(const base::FilePath& shortcut) {
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
if (!pinned_list)
return false;
ScopedPIDLFromPath item_id_list(shortcut.value().data());
HRESULT hr = pinned_list->Modify(item_id_list.Get(), nullptr,
PinnedListModifyCaller::kExplorer);
return SUCCEEDED(hr);
}
} // namespace
bool CanPinShortcutToTaskbar() {
// "Pin to taskbar" isn't directly supported in Windows 10, but WIN10_RS5 has
// some undocumented interfaces to do pinning.
return base::win::GetVersion() >= base::win::Version::WIN10_RS5;
}
bool PinShortcutToTaskbar(const base::FilePath& shortcut) {
if (g_can_pin_to_taskbar && !(*g_can_pin_to_taskbar)()) {
return false;
}
return PinShortcutWithIPinnedList3(shortcut);
}
bool UnpinShortcutFromTaskbar(const base::FilePath& shortcut) {
return UnpinShortcutWithIPinnedList3(shortcut);
}
std::optional<bool> IsShortcutPinnedToTaskbar(const base::FilePath& shortcut) {
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
if (!pinned_list.Get())
return std::nullopt;
ScopedPIDLFromPath item_id_list(shortcut.value().data());
HRESULT hr = pinned_list->IsPinned(item_id_list.Get());
// S_OK means `shortcut` is pinned, S_FALSE means it's not pinned.
return SUCCEEDED(hr) ? std::optional<bool>(hr == S_OK) : std::nullopt;
}
void SetCanPinToTaskbarDelegate(CanPinToTaskBarDelegateFunctionPtr delegate) {
g_can_pin_to_taskbar = delegate;
}
bool SetInstallerPinnedChromeToTaskbar(bool installed) {
base::win::RegKey key;
if (key.Create(HKEY_CURRENT_USER, install_static::GetRegistryPath().c_str(),
KEY_SET_VALUE) == ERROR_SUCCESS) {
return key.WriteValue(kInstallerPinned, installed ? 1 : 0) == ERROR_SUCCESS;
}
return false;
}
std::optional<bool> GetInstallerPinnedChromeToTaskbar() {
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, install_static::GetRegistryPath().c_str(),
KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
DWORD installer_pinned = 0;
LONG result = key.ReadValueDW(kInstallerPinned, &installer_pinned);
if (result == ERROR_SUCCESS)
return installer_pinned != 0;
}
return std::nullopt;
}
|