Process Hacker
affinity.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * process affinity editor
4  *
5  * Copyright (C) 2010-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * The affinity dialog was originally created to support the modification
25  * of process affinity masks, but now supports modifying thread affinity
26  * and generic masks.
27  */
28 
29 #include <phapp.h>
30 #include <windowsx.h>
31 
32 typedef struct _AFFINITY_DIALOG_CONTEXT
33 {
34  PPH_PROCESS_ITEM ProcessItem;
35  PPH_THREAD_ITEM ThreadItem;
36  ULONG_PTR AffinityMask;
37  ULONG_PTR NewAffinityMask;
39 
40 INT_PTR CALLBACK PhpProcessAffinityDlgProc(
41  _In_ HWND hwndDlg,
42  _In_ UINT uMsg,
43  _In_ WPARAM wParam,
44  _In_ LPARAM lParam
45  );
46 
48  _In_ HWND ParentWindowHandle,
49  _In_opt_ PPH_PROCESS_ITEM ProcessItem,
50  _In_opt_ PPH_THREAD_ITEM ThreadItem
51  )
52 {
54 
55  assert(!!ProcessItem != !!ThreadItem); // make sure we have one and not the other
56 
57  context.ProcessItem = ProcessItem;
58  context.ThreadItem = ThreadItem;
59 
60  DialogBoxParam(
62  MAKEINTRESOURCE(IDD_AFFINITY),
63  ParentWindowHandle,
65  (LPARAM)&context
66  );
67 }
68 
70  _In_ HWND ParentWindowHandle,
71  _In_ ULONG_PTR AffinityMask,
72  _Out_ PULONG_PTR NewAffinityMask
73  )
74 {
76 
77  context.ProcessItem = NULL;
78  context.ThreadItem = NULL;
79  context.AffinityMask = AffinityMask;
80 
81  if (DialogBoxParam(
83  MAKEINTRESOURCE(IDD_AFFINITY),
84  ParentWindowHandle,
86  (LPARAM)&context
87  ) == IDOK)
88  {
89  *NewAffinityMask = context.NewAffinityMask;
90 
91  return TRUE;
92  }
93  else
94  {
95  return FALSE;
96  }
97 }
98 
99 static INT_PTR CALLBACK PhpProcessAffinityDlgProc(
100  _In_ HWND hwndDlg,
101  _In_ UINT uMsg,
102  _In_ WPARAM wParam,
103  _In_ LPARAM lParam
104  )
105 {
106  switch (uMsg)
107  {
108  case WM_INITDIALOG:
109  {
110  NTSTATUS status;
112  SYSTEM_BASIC_INFORMATION systemBasicInfo;
113  ULONG_PTR systemAffinityMask;
114  ULONG_PTR affinityMask;
115  ULONG i;
116 
117  SetProp(hwndDlg, PhMakeContextAtom(), (HANDLE)context);
118  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
119 
120  systemAffinityMask = 0;
121 
122  if (context->ProcessItem)
123  {
124  HANDLE processHandle;
125  PROCESS_BASIC_INFORMATION basicInfo;
126 
127  if (NT_SUCCESS(status = PhOpenProcess(
128  &processHandle,
130  context->ProcessItem->ProcessId
131  )))
132  {
133  status = PhGetProcessBasicInformation(processHandle, &basicInfo);
134 
135  if (NT_SUCCESS(status))
136  affinityMask = basicInfo.AffinityMask;
137 
138  NtClose(processHandle);
139  }
140  }
141  else if (context->ThreadItem)
142  {
143  HANDLE threadHandle;
144  THREAD_BASIC_INFORMATION basicInfo;
145  HANDLE processHandle;
146  PROCESS_BASIC_INFORMATION processBasicInfo;
147 
148  if (NT_SUCCESS(status = PhOpenThread(
149  &threadHandle,
151  context->ThreadItem->ThreadId
152  )))
153  {
154  status = PhGetThreadBasicInformation(threadHandle, &basicInfo);
155 
156  if (NT_SUCCESS(status))
157  {
158  affinityMask = basicInfo.AffinityMask;
159 
160  // A thread's affinity mask is restricted by the process affinity mask,
161  // so use that as the system affinity mask.
162 
164  &processHandle,
166  basicInfo.ClientId.UniqueProcess
167  )))
168  {
169  if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &processBasicInfo)))
170  systemAffinityMask = processBasicInfo.AffinityMask;
171 
172  NtClose(processHandle);
173  }
174  }
175 
176  NtClose(threadHandle);
177  }
178  }
179  else
180  {
181  affinityMask = context->AffinityMask;
182  status = STATUS_SUCCESS;
183  }
184 
185  if (NT_SUCCESS(status) && systemAffinityMask == 0)
186  {
187  status = NtQuerySystemInformation(
189  &systemBasicInfo,
190  sizeof(SYSTEM_BASIC_INFORMATION),
191  NULL
192  );
193 
194  if (NT_SUCCESS(status))
195  systemAffinityMask = systemBasicInfo.ActiveProcessorsAffinityMask;
196  }
197 
198  if (!NT_SUCCESS(status))
199  {
200  PhShowStatus(hwndDlg, L"Unable to retrieve the affinity", status, 0);
201  EndDialog(hwndDlg, IDCANCEL);
202  break;
203  }
204 
205  // Disable the CPU checkboxes which aren't part of the system affinity mask,
206  // and check the CPU checkboxes which are part of the affinity mask.
207 
208  for (i = 0; i < 8 * 8; i++)
209  {
210  if ((i < sizeof(ULONG_PTR) * 8) && ((systemAffinityMask >> i) & 0x1))
211  {
212  if ((affinityMask >> i) & 0x1)
213  {
214  Button_SetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i), BST_CHECKED);
215  }
216  }
217  else
218  {
219  EnableWindow(GetDlgItem(hwndDlg, IDC_CPU0 + i), FALSE);
220  }
221  }
222  }
223  break;
224  case WM_DESTROY:
225  {
226  RemoveProp(hwndDlg, PhMakeContextAtom());
227  }
228  break;
229  case WM_COMMAND:
230  {
231  switch (LOWORD(wParam))
232  {
233  case IDCANCEL:
234  EndDialog(hwndDlg, IDCANCEL);
235  break;
236  case IDOK:
237  {
238  NTSTATUS status;
240  ULONG i;
241  ULONG_PTR affinityMask;
242 
243  // Work out the affinity mask.
244 
245  affinityMask = 0;
246 
247  for (i = 0; i < sizeof(ULONG_PTR) * 8; i++)
248  {
249  if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_CPU0 + i)) == BST_CHECKED)
250  affinityMask |= (ULONG_PTR)1 << i;
251  }
252 
253  if (context->ProcessItem)
254  {
255  HANDLE processHandle;
256 
257  if (NT_SUCCESS(status = PhOpenProcess(
258  &processHandle,
260  context->ProcessItem->ProcessId
261  )))
262  {
263  status = PhSetProcessAffinityMask(processHandle, affinityMask);
264  NtClose(processHandle);
265  }
266  }
267  else if (context->ThreadItem)
268  {
269  HANDLE threadHandle;
270 
271  if (NT_SUCCESS(status = PhOpenThread(
272  &threadHandle,
274  context->ThreadItem->ThreadId
275  )))
276  {
277  status = PhSetThreadAffinityMask(threadHandle, affinityMask);
278  NtClose(threadHandle);
279  }
280  }
281  else
282  {
283  context->NewAffinityMask = affinityMask;
284  status = STATUS_SUCCESS;
285  }
286 
287  if (NT_SUCCESS(status))
288  EndDialog(hwndDlg, IDOK);
289  else
290  PhShowStatus(hwndDlg, L"Unable to set the affinity", status, 0);
291  }
292  break;
293  case IDC_SELECTALL:
294  case IDC_DESELECTALL:
295  {
296  ULONG i;
297 
298  for (i = 0; i < sizeof(ULONG_PTR) * 8; i++)
299  {
300  HWND checkBox = GetDlgItem(hwndDlg, IDC_CPU0 + i);
301 
302  if (IsWindowEnabled(checkBox))
303  Button_SetCheck(checkBox, LOWORD(wParam) == IDC_SELECTALL ? BST_CHECKED : BST_UNCHECKED);
304  }
305  }
306  break;
307  }
308  }
309  break;
310  }
311 
312  return FALSE;
313 }