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
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
;
53 snapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
54 if (snapshot
== INVALID_HANDLE_VALUE
)
57 process_list
= malloc(alloc_count
* sizeof(*process_list
));
60 CloseHandle(snapshot
);
64 process_list
[0].dwSize
= sizeof(*process_list
);
65 if (!Process32FirstW(snapshot
, &process_list
[0]))
67 CloseHandle(snapshot
);
75 if (*process_count
== alloc_count
)
78 realloc_list
= realloc(process_list
, alloc_count
* sizeof(*process_list
));
81 CloseHandle(snapshot
);
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
);
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
;
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
)))
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
;
123 static void tasklist_get_header(const struct tasklist_options
*options
,
124 struct tasklist_process_info
*header
)
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
;
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());
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
);
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
);
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
;
195 left_string_operand
= NULL
;
196 left_dword_operand
= 0;
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
)
218 if (swscanf(filter
->value
, L
"%lu", &right_dword_operand
) != 1)
220 WINE_ERR("Invalid filter operand %s.\n", wine_dbgstr_w(filter
->value
));
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
;
241 filter
= filter
->next
;
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
)
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
))
274 if (!tasklist_check_filters(options
->filters
, &info
))
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
)
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
);
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
;
306 for (i
= 0; i
< argc
; i
++)
307 WINE_TRACE("%s ", wine_dbgstr_w(argv
[i
]));
310 for (i
= 1; i
< argc
; i
++)
312 if (!wcscmp(argv
[i
], L
"/?"))
314 tasklist_message(STRING_USAGE
);
317 else if (!wcsicmp(argv
[i
], L
"/nh"))
319 options
.no_header
= TRUE
;
321 else if (!wcsicmp(argv
[i
], L
"/fo"))
325 tasklist_error(STRING_INVALID_SYNTAX
);
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
;
343 tasklist_error(STRING_INVALID_SYNTAX
);
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
);
357 filter
= calloc(1, sizeof(*filter
));
360 WINE_ERR("Out of memory.\n");
365 if (!wcsicmp(filter_name
, L
"IMAGENAME"))
366 filter
->name
= IMAGENAME
;
367 else if (!wcsicmp(filter_name
, L
"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
;
377 WINE_WARN("Ignoring filter %s.\n", wine_dbgstr_w(filter_name
));
382 filter_op
= wcstok(NULL
, L
" ", &buffer
);
385 tasklist_error(STRING_FILTER_NOT_RECOGNIZED
);
391 if (!wcsicmp(filter_op
, L
"EQ"))
393 else if (!wcsicmp(filter_op
, L
"NE"))
395 else if (!wcsicmp(filter_op
, L
"GT"))
397 else if (!wcsicmp(filter_op
, L
"LT"))
399 else if (!wcsicmp(filter_op
, L
"GE"))
401 else if (!wcsicmp(filter_op
, L
"LE"))
405 tasklist_error(STRING_FILTER_NOT_RECOGNIZED
);
411 if (filter
->op
>= GT
&& filter
->name
!= PID
&& filter
->name
!= SESSION
&& filter
->name
!= MEMUSAGE
)
413 tasklist_error(STRING_FILTER_NOT_RECOGNIZED
);
419 filter
->value
= wcstok(NULL
, L
" ", &buffer
);
422 tasklist_error(STRING_FILTER_NOT_RECOGNIZED
);
428 *filter_ptr
= filter
;
429 filter_ptr
= &filter
->next
;
433 WINE_WARN("Ignoring option %s\n", wine_dbgstr_w(argv
[i
]));
437 tasklist_print(&options
);
440 next
= options
.filters
;