dmband: Download segment tracks if performance auto-download is set.
[wine.git] / programs / tasklist / tasklist.c
blob4f866f6b7274ebf8932103b5239b0b2d5096ea36
1 /*
2 * Copyright 2013 Austin English
3 * Copyright 2023 Zhiyi Zhang for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <windows.h>
21 #include <psapi.h>
22 #include <tlhelp32.h>
23 #include "tasklist.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(tasklist);
28 static int tasklist_message(int msg)
30 WCHAR msg_buffer[MAXSTRING];
32 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
33 return wprintf(msg_buffer);
36 static int tasklist_error(int msg)
38 WCHAR msg_buffer[MAXSTRING];
40 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
41 return fwprintf(stderr, msg_buffer);
44 static PROCESSENTRY32W *enumerate_processes(DWORD *process_count)
46 unsigned int alloc_count = 128;
47 PROCESSENTRY32W *process_list;
48 void *realloc_list;
49 HANDLE snapshot;
51 *process_count = 0;
53 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
54 if (snapshot == INVALID_HANDLE_VALUE)
55 return NULL;
57 process_list = malloc(alloc_count * sizeof(*process_list));
58 if (!process_list)
60 CloseHandle(snapshot);
61 return NULL;
64 process_list[0].dwSize = sizeof(*process_list);
65 if (!Process32FirstW(snapshot, &process_list[0]))
67 CloseHandle(snapshot);
68 free(process_list);
69 return NULL;
74 (*process_count)++;
75 if (*process_count == alloc_count)
77 alloc_count *= 2;
78 realloc_list = realloc(process_list, alloc_count * sizeof(*process_list));
79 if (!realloc_list)
81 CloseHandle(snapshot);
82 free(process_list);
83 return NULL;
85 process_list = realloc_list;
87 process_list[*process_count].dwSize = sizeof(*process_list);
88 } while (Process32NextW(snapshot, &process_list[*process_count]));
89 CloseHandle(snapshot);
90 return process_list;
93 static NUMBERFMTW *tasklist_get_memory_format(void)
95 static NUMBERFMTW format = {0, 0, 0, NULL, NULL, 1};
96 static WCHAR grouping[3], decimal[2], thousand[2];
97 static BOOL initialized;
99 if (initialized)
100 return &format;
102 if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_ILZERO | LOCALE_RETURN_NUMBER,
103 (WCHAR *)&format.LeadingZero, 2))
104 format.LeadingZero = 0;
106 if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, ARRAY_SIZE(grouping)))
107 format.Grouping = 3;
108 else
109 format.Grouping = (grouping[2] == '2' ? 32 : grouping[0] - '0');
111 if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal, ARRAY_SIZE(decimal)))
112 wcscpy(decimal, L".");
114 if (!GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousand, ARRAY_SIZE(thousand)))
115 wcscpy(thousand, L",");
117 format.lpDecimalSep = decimal;
118 format.lpThousandSep = thousand;
119 initialized = TRUE;
120 return &format;
123 static void tasklist_get_header(const struct tasklist_options *options,
124 struct tasklist_process_info *header)
126 HMODULE module;
128 module = GetModuleHandleW(NULL);
129 LoadStringW(module, STRING_IMAGE_NAME, header->image_name, ARRAY_SIZE(header->image_name));
130 LoadStringW(module, STRING_PID, header->pid, ARRAY_SIZE(header->pid));
131 LoadStringW(module, STRING_SESSION_NAME, header->session_name, ARRAY_SIZE(header->session_name));
132 LoadStringW(module, STRING_SESSION_NUMBER, header->session_number, ARRAY_SIZE(header->session_number));
133 LoadStringW(module, STRING_MEM_USAGE, header->memory_usage, ARRAY_SIZE(header->memory_usage));
134 if (options->format == LIST)
136 wcscat(header->image_name, L":");
137 wcscat(header->pid, L":");
138 wcscat(header->session_name, L":");
139 wcscat(header->session_number, L":");
140 wcscat(header->memory_usage, L":");
144 static BOOL tasklist_get_process_info(const PROCESSENTRY32W *process_entry, struct tasklist_process_info *info)
146 PROCESS_MEMORY_COUNTERS memory_counters;
147 DWORD session_id;
148 WCHAR buffer[16];
149 HANDLE process;
151 memset(info, 0, sizeof(*info));
153 if (!ProcessIdToSessionId(process_entry->th32ProcessID, &session_id))
155 WINE_FIXME("Failed to get process session id, %lu.\n", GetLastError());
156 return FALSE;
159 process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_entry->th32ProcessID);
160 if (process && GetProcessMemoryInfo(process, &memory_counters, sizeof(memory_counters)))
162 swprintf(buffer, ARRAY_SIZE(buffer), L"%u", memory_counters.WorkingSetSize / 1024);
163 if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, buffer, tasklist_get_memory_format(),
164 info->memory_usage, ARRAY_SIZE(info->memory_usage)))
166 LoadStringW(GetModuleHandleW(NULL), STRING_K, buffer, ARRAY_SIZE(buffer));
167 wcscat(info->memory_usage, L" ");
168 wcscat(info->memory_usage, buffer);
171 if (process)
172 CloseHandle(process);
173 if (info->memory_usage[0] == '\0')
174 wcscpy(info->memory_usage, L"N/A");
176 info->pid_value = process_entry->th32ProcessID;
177 info->memory_usage_value = memory_counters.WorkingSetSize / 1024;
178 info->session_id_value = session_id;
179 wcscpy(info->image_name, process_entry->szExeFile);
180 swprintf(info->pid, ARRAY_SIZE(info->pid), L"%u", process_entry->th32ProcessID);
181 wcscpy(info->session_name, session_id == 0 ? L"Services" : L"Console");
182 swprintf(info->session_number, ARRAY_SIZE(info->session_number), L"%u", session_id);
183 return TRUE;
186 static BOOL tasklist_check_filters(const struct tasklist_filter *filter,
187 const struct tasklist_process_info *info)
189 DWORD left_dword_operand, right_dword_operand;
190 const WCHAR *left_string_operand = NULL;
191 BOOL eval;
193 while (filter)
195 left_string_operand = NULL;
196 left_dword_operand = 0;
197 eval = FALSE;
199 if (filter->name == IMAGENAME)
200 left_string_operand = info->image_name;
201 else if (filter->name == SESSIONNAME)
202 left_string_operand = info->session_name;
203 else if (filter->name == PID)
204 left_dword_operand = info->pid_value;
205 else if (filter->name == SESSION)
206 left_dword_operand = info->session_id_value;
207 else if (filter->name == MEMUSAGE)
208 left_dword_operand = info->memory_usage_value;
210 if (left_string_operand)
212 eval = wcsicmp(left_string_operand, filter->value);
213 if (filter->op == EQ)
214 eval = !eval;
216 else
218 if (swscanf(filter->value, L"%lu", &right_dword_operand) != 1)
220 WINE_ERR("Invalid filter operand %s.\n", wine_dbgstr_w(filter->value));
221 return FALSE;
224 if (filter->op == EQ)
225 eval = left_dword_operand == right_dword_operand;
226 else if (filter->op == NE)
227 eval = left_dword_operand != right_dword_operand;
228 else if (filter->op == GT)
229 eval = left_dword_operand > right_dword_operand;
230 else if (filter->op == LT)
231 eval = left_dword_operand < right_dword_operand;
232 else if (filter->op == GE)
233 eval = left_dword_operand >= right_dword_operand;
234 else if (filter->op == LE)
235 eval = left_dword_operand <= right_dword_operand;
238 if (!eval)
239 return FALSE;
241 filter = filter->next;
244 return TRUE;
247 static void tasklist_print(const struct tasklist_options *options)
249 struct tasklist_process_info header, info;
250 PROCESSENTRY32W *process_list;
251 DWORD process_count, i;
253 if (options->format == TABLE)
254 wprintf(L"\n");
256 tasklist_get_header(options, &header);
257 if (!options->no_header)
259 if (options->format == TABLE)
260 wprintf(L"%-25.25s %8.8s %-16.16s %11.11s %12.12s\n"
261 L"========================= ======== ================ =========== ============\n",
262 header.image_name, header.pid, header.session_name, header.session_number, header.memory_usage);
263 else if (options->format == CSV)
264 wprintf(L"\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
265 header.image_name, header.pid, header.session_name, header.session_number, header.memory_usage);
268 process_list = enumerate_processes(&process_count);
269 for (i = 0; i < process_count; ++i)
271 if (!tasklist_get_process_info(&process_list[i], &info))
272 continue;
274 if (!tasklist_check_filters(options->filters, &info))
275 continue;
277 if (options->format == TABLE)
278 wprintf(L"%-25.25s %8.8s %-16.16s %11.11s %12s\n",
279 info.image_name, info.pid, info.session_name, info.session_number, info.memory_usage);
280 else if (options->format == CSV)
281 wprintf(L"\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
282 info.image_name, info.pid, info.session_name, info.session_number, info.memory_usage);
283 else if (options->format == LIST)
284 wprintf(L"\n"
285 L"%-13.13s %s\n"
286 L"%-13.13s %s\n"
287 L"%-13.13s %s\n"
288 L"%-13.13s %s\n"
289 L"%-13.13s %s\n",
290 header.image_name, info.image_name,
291 header.pid, info.pid,
292 header.session_name, info.session_name,
293 header.session_number, info.session_number,
294 header.memory_usage, info.memory_usage);
296 free(process_list);
299 int __cdecl wmain(int argc, WCHAR *argv[])
301 struct tasklist_options options = {0};
302 struct tasklist_filter *filter, *next, **filter_ptr = &options.filters;
303 WCHAR *filter_name, *filter_op, *buffer;
304 int i, ret = 0;
306 for (i = 0; i < argc; i++)
307 WINE_TRACE("%s ", wine_dbgstr_w(argv[i]));
308 WINE_TRACE("\n");
310 for (i = 1; i < argc; i++)
312 if (!wcscmp(argv[i], L"/?"))
314 tasklist_message(STRING_USAGE);
315 goto done;
317 else if (!wcsicmp(argv[i], L"/nh"))
319 options.no_header = TRUE;
321 else if (!wcsicmp(argv[i], L"/fo"))
323 if (i + 1 >= argc)
325 tasklist_error(STRING_INVALID_SYNTAX);
326 ret = 1;
327 goto done;
329 else if (!wcsicmp(argv[i + 1], L"TABLE"))
331 options.format = TABLE;
333 else if (!wcsicmp(argv[i + 1], L"CSV"))
335 options.format = CSV;
337 else if (!wcsicmp(argv[i + 1], L"LIST"))
339 options.format = LIST;
341 else
343 tasklist_error(STRING_INVALID_SYNTAX);
344 ret = 1;
345 goto done;
348 else if (!wcsicmp(argv[i], L"/fi"))
350 if (i + 1 >= argc || !(filter_name = wcstok(argv[i + 1], L" ", &buffer)))
352 tasklist_error(STRING_INVALID_SYNTAX);
353 ret = 1;
354 goto done;
357 filter = calloc(1, sizeof(*filter));
358 if (!filter)
360 WINE_ERR("Out of memory.\n");
361 ret = 1;
362 goto done;
365 if (!wcsicmp(filter_name, L"IMAGENAME"))
366 filter->name = IMAGENAME;
367 else if (!wcsicmp(filter_name, L"PID"))
368 filter->name = PID;
369 else if (!wcsicmp(filter_name, L"SESSION"))
370 filter->name = SESSION;
371 else if (!wcsicmp(filter_name, L"SESSIONNAME"))
372 filter->name = SESSIONNAME;
373 else if (!wcsicmp(filter_name, L"MEMUSAGE"))
374 filter->name = MEMUSAGE;
375 else
377 WINE_WARN("Ignoring filter %s.\n", wine_dbgstr_w(filter_name));
378 free(filter);
379 continue;
382 filter_op = wcstok(NULL, L" ", &buffer);
383 if (!filter_op)
385 tasklist_error(STRING_FILTER_NOT_RECOGNIZED);
386 free(filter);
387 ret = 1;
388 goto done;
391 if (!wcsicmp(filter_op, L"EQ"))
392 filter->op = EQ;
393 else if (!wcsicmp(filter_op, L"NE"))
394 filter->op = NE;
395 else if (!wcsicmp(filter_op, L"GT"))
396 filter->op = GT;
397 else if (!wcsicmp(filter_op, L"LT"))
398 filter->op = LT;
399 else if (!wcsicmp(filter_op, L"GE"))
400 filter->op = GE;
401 else if (!wcsicmp(filter_op, L"LE"))
402 filter->op = LE;
403 else
405 tasklist_error(STRING_FILTER_NOT_RECOGNIZED);
406 free(filter);
407 ret = 1;
408 goto done;
411 if (filter->op >= GT && filter->name != PID && filter->name != SESSION && filter->name != MEMUSAGE)
413 tasklist_error(STRING_FILTER_NOT_RECOGNIZED);
414 free(filter);
415 ret = 1;
416 goto done;
419 filter->value = wcstok(NULL, L" ", &buffer);
420 if (!filter->value)
422 tasklist_error(STRING_FILTER_NOT_RECOGNIZED);
423 free(filter);
424 ret = 1;
425 goto done;
428 *filter_ptr = filter;
429 filter_ptr = &filter->next;
431 else
433 WINE_WARN("Ignoring option %s\n", wine_dbgstr_w(argv[i]));
437 tasklist_print(&options);
439 done:
440 next = options.filters;
441 while (next)
443 filter = next->next;
444 free(next);
445 next = filter;
447 return ret;