C++ 实现 Pin to TaskBar in win10

将快捷方式 “固定到任务栏” 的功能在 Win10 上一直无法实现,困扰了我许久。但是最近体验迅雷U享版的时候发现,程序一安装完居然自动固定到了任务栏,所以最近有空又重新研究了下。

Win7下这样一行代码就行了,但是在Win10上这个方法被限制了。

1
ShellExecute(NULL, "taskbarpin", "path to link", NULL, NULL, 0)

谷歌一番后发现了这篇 文章 实现了这功能,代码是C#的。实现方式是模拟用户点击快捷方式的右键菜单中的“固定到任务栏”。

C++实现

默认情况下代码获取到的快捷方式右键菜单项是没有“固定到任务栏”的,查资料发现是Win10又添加了限制,判断了当前程序名是否是 explorer.exe,否则返回的项不会包含“固定到任务栏”或“从任务栏取消固定”。

基本思路:

1.取得当前程序的PEB(通过NtQuerySystemInformation)  
2.修改 PEB 里 PRTL_USER_PROCESS_PARAMETERS 中的 ImagePathName  
3.列出菜单项模拟点击

注意: 新构造的 ImagePathName 长度不能比原来的 ImagePathName.Length 长,不然修改会失败。

代码

在 Win7,Win8.1,Win10上测试通过,XP上运行没有效果。

注: Win10 LTSB 2015 (1511)无效,但360浏览器和迅雷U享版都支持,暂时无解 :(。 2018年2月27日

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
#include <windows.h>
#include <Winternl.h>

#include <atlbase.h>
#include <Shlobj.h>

void ChangeImageName(HANDLE hProcess)
{
typedef NTSTATUS(NTAPI* NtQueryInformationProcessPtr)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);

HMODULE hModule = GetModuleHandle(L"ntdll.dll");
NtQueryInformationProcessPtr NtQueryInformationProcess =
(NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");

if (!NtQueryInformationProcess)
return;

PROCESS_BASIC_INFORMATION pbi = { 0 };
ULONG ulRetLength = 0;
NTSTATUS ntStatus = NtQueryInformationProcess(
hProcess,
ProcessBasicInformation,
&pbi,
sizeof(PROCESS_BASIC_INFORMATION),
&ulRetLength);

BOOL bRet = (ntStatus >= 0);
if (!bRet)
{
fprintf(stderr, "Could not get process information. Status = %X\n",
ntStatus);
return;
}

PPEB ppeb = pbi.PebBaseAddress;
PRTL_USER_PROCESS_PARAMETERS pRtlProcParam = ppeb->ProcessParameters;


//ref https://stackoverflow.com/a/38880235
WCHAR szImagePath[MAX_PATH] = { 0 };
wcscpy_s(szImagePath, pRtlProcParam->ImagePathName.Buffer);
PathRemoveFileSpec(szImagePath);
PathAddBackslash(szImagePath);
wcscat_s(szImagePath, L"explorer.exe");

int len = wcslen(szImagePath) * sizeof(WCHAR);
int originalLen = pRtlProcParam->ImagePathName.Length;
if (len > originalLen)
{
fwprintf(stderr, L"new ImagePathName cannot be longer than the original one\n");
return;
}
SecureZeroMemory(pRtlProcParam->ImagePathName.Buffer, originalLen);
memcpy(pRtlProcParam->ImagePathName.Buffer, szImagePath, len);
};

bool PinUnpinTaskbar(LPCWSTR szFilePath, bool pin)
{
HRESULT hr = S_OK;

WCHAR szVerbName[MAX_PATH] = { 0 };
HINSTANCE ht = LoadLibrary(L"Shell32.dll");

//5386 is the DLL index for"Pin to Tas&kbar"
//ref.//http://www.win7dll.info/shell32_dll.html
UINT uID = pin ? 5386 : 5387;
LoadString(ht, uID, szVerbName, MAX_PATH);

CComPtr<IShellDispatch> pShellDisp;

hr = CoCreateInstance(CLSID_Shell, nullptr, CLSCTX_SERVER, IID_IShellDispatch, reinterpret_cast<LPVOID*>(&pShellDisp));

if (FAILED(hr))
return false;

CComPtr<Folder> pFolder;
CComPtr<FolderItem> pFolderItem;

WCHAR szTempPath[MAX_PATH] = { 0 };

//folder
wcscpy_s(szTempPath, szFilePath);
PathRemoveFileSpec(szTempPath);
CComVariant vDir(szTempPath);
hr = pShellDisp->NameSpace(vDir, &pFolder);

if (FAILED(hr))
return false;

//file name
wcscpy_s(szTempPath, szFilePath);
PathStripPath(szTempPath);
hr = pFolder->ParseName(CComBSTR(szTempPath), &pFolderItem);

if (FAILED(hr))
return false;

CComPtr<FolderItemVerbs> itemVerbs;
hr = pFolderItem->Verbs(&itemVerbs);

if (FAILED(hr))
return false;

LONG itemCount = 0;
itemVerbs->get_Count(&itemCount);

CComBSTR ccombstr(szVerbName);
for (int i = 0; i < itemCount; i++)
{
CComPtr<FolderItemVerb> verb;
itemVerbs->Item(CComVariant(i), &verb);
if (verb)
{
CComBSTR name;
hr = verb->get_Name(&name);

if (SUCCEEDED(hr) && ccombstr == name)
{
verb->DoIt();
fprintf(stdout, "verb founded, do it.\n");
return true;
}
}
}
return false;
}

int main()
{
ChangeImageName(GetCurrentProcess());

::CoInitialize(NULL);
PinUnpinTaskbar(L"C:\\WINDOWS\\system32\\notepad.exe", true);
::CoUninitialize();

return 0;
}

参考

Pinning a network program with a Jump List to the taskbar programmatically in windows 10 – Part 1
https://stackoverflow.com/a/38880235
Manually Enumerating Process Modules