Reactos
1/*
2 * Toolhelp
3 *
4 * Copyright 2005 Eric Pouech
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21#include <stdarg.h>
22#include <stdlib.h>
23#include <stdio.h>
24
25#include "ntstatus.h"
26#define WIN32_NO_STATUS
27#include "windef.h"
28#include "winbase.h"
29#include "tlhelp32.h"
30#include "wine/test.h"
31#include "winuser.h"
32#include "winternl.h"
33
34static char selfname[MAX_PATH];
35
36/* Some functions are only in later versions of kernel32.dll */
37static HANDLE (WINAPI *pCreateToolhelp32Snapshot)(DWORD, DWORD);
38static BOOL (WINAPI *pModule32First)(HANDLE, LPMODULEENTRY32);
39static BOOL (WINAPI *pModule32Next)(HANDLE, LPMODULEENTRY32);
40static BOOL (WINAPI *pProcess32First)(HANDLE, LPPROCESSENTRY32);
41static BOOL (WINAPI *pProcess32Next)(HANDLE, LPPROCESSENTRY32);
42static BOOL (WINAPI *pThread32First)(HANDLE, LPTHREADENTRY32);
43static BOOL (WINAPI *pThread32Next)(HANDLE, LPTHREADENTRY32);
44static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, void *, ULONG, ULONG *);
45
46/* 1 minute should be more than enough */
47#define WAIT_TIME (60 * 1000)
48/* Specify the number of simultaneous threads to test */
49#define NUM_THREADS 4
50
51static DWORD WINAPI sub_thread(void* pmt)
52{
53 DWORD w = WaitForSingleObject(pmt, WAIT_TIME);
54 return w;
55}
56
57/******************************************************************
58 * init
59 *
60 * generates basic information like:
61 * selfname: the way to reinvoke ourselves
62 * returns:
63 * -1 on error
64 * 0 if parent
65 * doesn't return if child
66 */
67static int init(void)
68{
69 int argc;
70 char** argv;
71 HANDLE ev1, ev2, ev3, hThread;
72 DWORD w, tid;
73
74 argc = winetest_get_mainargs( &argv );
75 strcpy(selfname, argv[0]);
76
77 switch (argc)
78 {
79 case 2: /* the test program */
80 return 0;
81 case 4: /* the sub-process */
82 ev1 = (HANDLE)(INT_PTR)atoi(argv[2]);
83 ev2 = (HANDLE)(INT_PTR)atoi(argv[3]);
84 ev3 = CreateEventW(NULL, FALSE, FALSE, NULL);
85
86 if (ev3 == NULL) ExitProcess(WAIT_ABANDONED);
87 hThread = CreateThread(NULL, 0, sub_thread, ev3, 0, &tid);
88 if (hThread == NULL) ExitProcess(WAIT_ABANDONED);
89 if (!LoadLibraryA("shell32.dll")) ExitProcess(WAIT_ABANDONED);
90
91 /* signal init of sub-process is done */
92 SetEvent(ev1);
93 /* wait for parent to have done all its queries */
94 w = WaitForSingleObject(ev2, WAIT_TIME);
95 if (w != WAIT_OBJECT_0) ExitProcess(w);
96 /* signal sub-thread to terminate */
97 SetEvent(ev3);
98 w = WaitForSingleObject(hThread, WAIT_TIME);
99 if (w != WAIT_OBJECT_0) ExitProcess(w);
100 GetExitCodeThread(hThread, &w);
101 ExitProcess(w);
102 default:
103 return -1;
104 }
105}
106
107static void test_process(DWORD curr_pid, DWORD sub_pcs_pid)
108{
109 HANDLE hSnapshot;
110 PROCESSENTRY32 pe;
111 MODULEENTRY32 me;
112 unsigned found = 0;
113 int num = 0;
114 int childpos = -1;
115
116 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
117 ok(hSnapshot != NULL, "Cannot create snapshot\n");
118
119 /* check that this current process is enumerated */
120 pe.dwSize = sizeof(pe);
121 if (pProcess32First( hSnapshot, &pe ))
122 {
123 do
124 {
125 if (pe.th32ProcessID == curr_pid) found++;
126 if (pe.th32ProcessID == sub_pcs_pid) { childpos = num; found++; }
127 trace("PID=%lx %s\n", pe.th32ProcessID, pe.szExeFile);
128 num++;
129 } while (pProcess32Next( hSnapshot, &pe ));
130 }
131#if defined(__REACTOS__) && defined(_WIN64)
132 ok(found == 2 || broken(found == 0) /* x86_64 */, "couldn't find self and/or sub-process in process list\n");
133#else
134 ok(found == 2, "couldn't find self and/or sub-process in process list\n");
135#endif
136
137 /* check that first really resets the enumeration */
138 found = 0;
139 if (pProcess32First( hSnapshot, &pe ))
140 {
141 do
142 {
143 if (pe.th32ProcessID == curr_pid) found++;
144 if (pe.th32ProcessID == sub_pcs_pid) found++;
145 trace("PID=%lx %s\n", pe.th32ProcessID, pe.szExeFile);
146 num--;
147 } while (pProcess32Next( hSnapshot, &pe ));
148 }
149#if defined(__REACTOS__) && defined(_WIN64)
150 ok(found == 2 || broken(found == 0) /* x86_64 */, "couldn't find self and/or sub-process in process list\n");
151#else
152 ok(found == 2, "couldn't find self and/or sub-process in process list\n");
153#endif
154 ok(!num, "mismatch in counting\n");
155
156 /* one broken program does Process32First() and does not expect anything
157 * interesting to be there, especially not the just forked off child */
158 ok (childpos !=0, "child is not expected to be at position 0.\n");
159
160 me.dwSize = sizeof(me);
161 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
162
163 CloseHandle(hSnapshot);
164 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n");
165}
166
167static DWORD WINAPI get_id_thread(void* curr_pid)
168{
169 HANDLE hSnapshot;
170 THREADENTRY32 te;
171 HANDLE ev, threads[NUM_THREADS];
172 DWORD thread_ids[NUM_THREADS];
173 DWORD thread_traversed[NUM_THREADS];
174 DWORD tid, first_tid = 0;
175 BOOL found = FALSE;
176 int i, matched_idx = -1;
177 ULONG buf_size = 0;
178 NTSTATUS status;
179 BYTE* pcs_buffer = NULL;
180 DWORD pcs_offset = 0;
181 SYSTEM_PROCESS_INFORMATION* spi = NULL;
182
183 ev = CreateEventW(NULL, TRUE, FALSE, NULL);
184 ok(ev != NULL, "Cannot create event\n");
185
186 for (i = 0; i < NUM_THREADS; i++)
187 {
188 threads[i] = CreateThread(NULL, 0, sub_thread, ev, 0, &tid);
189 ok(threads[i] != NULL, "Cannot create thread\n");
190 thread_ids[i] = tid;
191 }
192
193 hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
194 ok(hSnapshot != NULL, "Cannot create snapshot\n");
195
196 /* Check that this current process is enumerated */
197 te.dwSize = sizeof(te);
198 ok(pThread32First(hSnapshot, &te), "Thread cannot traverse\n");
199 do
200 {
201 if (found)
202 {
203 if (te.th32OwnerProcessID != PtrToUlong(curr_pid)) break;
204
205 if (matched_idx >= 0)
206 {
207 thread_traversed[matched_idx++] = te.th32ThreadID;
208 if (matched_idx >= NUM_THREADS) break;
209 }
210 else if (thread_ids[0] == te.th32ThreadID)
211 {
212 matched_idx = 0;
213 thread_traversed[matched_idx++] = te.th32ThreadID;
214 }
215 }
216 else if (te.th32OwnerProcessID == PtrToUlong(curr_pid))
217 {
218 found = TRUE;
219 first_tid = te.th32ThreadID;
220 }
221 }
222 while (pThread32Next(hSnapshot, &te));
223
224 ok(found, "Couldn't find self and/or sub-process in process list\n");
225
226 /* Check if the thread order is strictly consistent */
227 found = FALSE;
228 for (i = 0; i < NUM_THREADS; i++)
229 {
230 if (thread_traversed[i] != thread_ids[i])
231 {
232 found = TRUE;
233 break;
234 }
235 /* Reset data */
236 thread_traversed[i] = 0;
237 }
238 ok(found == FALSE, "The thread order is not strictly consistent\n");
239
240 /* Determine the order by NtQuerySystemInformation function */
241
242 while ((status = NtQuerySystemInformation(SystemProcessInformation,
243 pcs_buffer, buf_size, &buf_size)) == STATUS_INFO_LENGTH_MISMATCH)
244 {
245 free(pcs_buffer);
246 pcs_buffer = malloc(buf_size);
247 }
248 ok(status == STATUS_SUCCESS, "got %#lx\n", status);
249 found = FALSE;
250 matched_idx = -1;
251
252 do
253 {
254 spi = (SYSTEM_PROCESS_INFORMATION*)&pcs_buffer[pcs_offset];
255 if (spi->UniqueProcessId == curr_pid)
256 {
257 found = TRUE;
258 break;
259 }
260 pcs_offset += spi->NextEntryOffset;
261 } while (spi->NextEntryOffset != 0);
262
263 ok(found && spi, "No process found\n");
264 for (i = 0; i < spi->dwThreadCount; i++)
265 {
266 tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
267 if (matched_idx > 0)
268 {
269 thread_traversed[matched_idx++] = tid;
270 if (matched_idx >= NUM_THREADS) break;
271 }
272 else if (tid == thread_ids[0])
273 {
274 matched_idx = 0;
275 thread_traversed[matched_idx++] = tid;
276 }
277 }
278 free(pcs_buffer);
279
280 ok(matched_idx > 0, "No thread id match found\n");
281
282 found = FALSE;
283 for (i = 0; i < NUM_THREADS; i++)
284 {
285 if (thread_traversed[i] != thread_ids[i])
286 {
287 found = TRUE;
288 break;
289 }
290 }
291 ok(found == FALSE, "wrong order in NtQuerySystemInformation function\n");
292
293 SetEvent(ev);
294 WaitForMultipleObjects( NUM_THREADS, threads, TRUE, WAIT_TIME );
295 for (i = 0; i < NUM_THREADS; i++)
296 CloseHandle(threads[i]);
297 CloseHandle(ev);
298 CloseHandle(hSnapshot);
299
300 return first_tid;
301}
302
303static void test_main_thread(DWORD curr_pid, DWORD main_tid)
304{
305 HANDLE thread;
306 DWORD tid = 0;
307 int error;
308
309 /* Check that the main thread id is first one in this thread. */
310 tid = get_id_thread(ULongToPtr(curr_pid));
311 ok(tid == main_tid, "The first thread id returned is not the main thread id\n");
312
313 /* Check that the main thread id is first one in other thread. */
314 thread = CreateThread(NULL, 0, get_id_thread, ULongToPtr(curr_pid), 0, NULL);
315 error = WaitForSingleObject(thread, WAIT_TIME);
316 ok(error == WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
317
318 ok(GetExitCodeThread(thread, &tid), "Could not retrieve exit code\n");
319 ok(tid == main_tid, "The first thread id returned is not the main thread id\n");
320}
321
322static void test_thread(DWORD curr_pid, DWORD sub_pcs_pid)
323{
324 HANDLE hSnapshot;
325 THREADENTRY32 te;
326 MODULEENTRY32 me;
327 unsigned curr_found = 0;
328 unsigned sub_found = 0;
329
330 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
331 ok(hSnapshot != NULL, "Cannot create snapshot\n");
332
333 /* check that this current process is enumerated */
334 te.dwSize = sizeof(te);
335 if (pThread32First( hSnapshot, &te ))
336 {
337 do
338 {
339 if (te.th32OwnerProcessID == curr_pid) curr_found++;
340 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++;
341 if (winetest_debug > 1)
342 trace("PID=%lx TID=%lx %ld\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri);
343 } while (pThread32Next( hSnapshot, &te ));
344 }
345 ok(curr_found, "couldn't find self in thread list\n");
346 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
347
348 /* check that first really resets enumeration */
349 curr_found = 0;
350 sub_found = 0;
351 if (pThread32First( hSnapshot, &te ))
352 {
353 do
354 {
355 if (te.th32OwnerProcessID == curr_pid) curr_found++;
356 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++;
357 if (winetest_debug > 1)
358 trace("PID=%lx TID=%lx %ld\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri);
359 } while (pThread32Next( hSnapshot, &te ));
360 }
361 ok(curr_found, "couldn't find self in thread list\n");
362 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
363
364 me.dwSize = sizeof(me);
365 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
366
367 CloseHandle(hSnapshot);
368 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n");
369}
370
371static const char* curr_expected_modules[] =
372{
373 "kernel32_test.exe",
374 "kernel32.dll",
375 "ntdll.dll"
376};
377
378static const char* sub_expected_modules[] =
379{
380 "kernel32_test.exe",
381 "kernel32.dll",
382 "shell32.dll",
383 "ntdll.dll"
384};
385
386static void test_module(DWORD pid, const char* expected[], unsigned num_expected)
387{
388 HANDLE hSnapshot;
389 PROCESSENTRY32 pe;
390 THREADENTRY32 te;
391 MODULEENTRY32 me;
392 unsigned found[32];
393 unsigned i;
394 int num = 0;
395
396 ok(ARRAY_SIZE(found) >= num_expected, "Internal: bump found[] size\n");
397
398 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
399 ok(hSnapshot != NULL, "Cannot create snapshot\n");
400
401 for (i = 0; i < num_expected; i++) found[i] = 0;
402 me.dwSize = sizeof(me);
403 if (pModule32First( hSnapshot, &me ))
404 {
405 do
406 {
407 trace("PID=%lx base=%p size=%lx %s %s\n",
408 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule);
409 ok(me.th32ProcessID == pid, "wrong returned process id\n");
410 for (i = 0; i < num_expected; i++)
411 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++;
412 num++;
413 } while (pModule32Next( hSnapshot, &me ));
414 }
415 for (i = 0; i < num_expected; i++)
416 ok(found[i] == 1, "Module %s is %s\n",
417 expected[i], found[i] ? "listed more than once" : "not listed");
418
419 /* check that first really resets the enumeration */
420 for (i = 0; i < num_expected; i++) found[i] = 0;
421 me.dwSize = sizeof(me);
422 if (pModule32First( hSnapshot, &me ))
423 {
424 do
425 {
426 trace("PID=%lx base=%p size=%lx %s %s\n",
427 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule);
428 for (i = 0; i < num_expected; i++)
429 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++;
430 num--;
431 } while (pModule32Next( hSnapshot, &me ));
432 }
433 for (i = 0; i < num_expected; i++)
434 ok(found[i] == 1, "Module %s is %s\n",
435 expected[i], found[i] ? "listed more than once" : "not listed");
436 ok(!num, "mismatch in counting\n");
437
438 pe.dwSize = sizeof(pe);
439 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n");
440
441 te.dwSize = sizeof(te);
442 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n");
443
444 CloseHandle(hSnapshot);
445 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
446}
447
448START_TEST(toolhelp)
449{
450 DWORD pid = GetCurrentProcessId();
451 int r;
452 char *p, module[MAX_PATH];
453 char buffer[MAX_PATH + 21];
454 SECURITY_ATTRIBUTES sa;
455 PROCESS_INFORMATION info;
456 STARTUPINFOA startup;
457 HANDLE ev1, ev2;
458 DWORD w;
459 HANDLE hkernel32 = GetModuleHandleA("kernel32");
460 HANDLE hntdll = GetModuleHandleA("ntdll.dll");
461
462 pCreateToolhelp32Snapshot = (VOID *) GetProcAddress(hkernel32, "CreateToolhelp32Snapshot");
463 pModule32First = (VOID *) GetProcAddress(hkernel32, "Module32First");
464 pModule32Next = (VOID *) GetProcAddress(hkernel32, "Module32Next");
465 pProcess32First = (VOID *) GetProcAddress(hkernel32, "Process32First");
466 pProcess32Next = (VOID *) GetProcAddress(hkernel32, "Process32Next");
467 pThread32First = (VOID *) GetProcAddress(hkernel32, "Thread32First");
468 pThread32Next = (VOID *) GetProcAddress(hkernel32, "Thread32Next");
469 pNtQuerySystemInformation = (VOID *) GetProcAddress(hntdll, "NtQuerySystemInformation");
470
471 if (!pCreateToolhelp32Snapshot ||
472 !pModule32First || !pModule32Next ||
473 !pProcess32First || !pProcess32Next ||
474 !pThread32First || !pThread32Next ||
475 !pNtQuerySystemInformation)
476 {
477 win_skip("Needed functions are not available, most likely running on Windows NT\n");
478 return;
479 }
480
481 r = init();
482 ok(r == 0, "Basic init of sub-process test\n");
483 if (r != 0) return;
484
485 sa.nLength = sizeof(sa);
486 sa.lpSecurityDescriptor = NULL;
487 sa.bInheritHandle = TRUE;
488
489 ev1 = CreateEventW(&sa, FALSE, FALSE, NULL);
490 ev2 = CreateEventW(&sa, FALSE, FALSE, NULL);
491 ok (ev1 != NULL && ev2 != NULL, "Couldn't create events\n");
492 memset(&startup, 0, sizeof(startup));
493 startup.cb = sizeof(startup);
494 startup.dwFlags = STARTF_USESHOWWINDOW;
495 startup.wShowWindow = SW_SHOWNORMAL;
496
497 sprintf(buffer, "%s toolhelp %Iu %Iu", selfname, (DWORD_PTR)ev1, (DWORD_PTR)ev2);
498 ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
499 /* wait for child to be initialized */
500 w = WaitForSingleObject(ev1, WAIT_TIME);
501 ok(w == WAIT_OBJECT_0, "Failed to wait on sub-process startup\n");
502
503 GetModuleFileNameA( 0, module, sizeof(module) );
504 if (!(p = strrchr( module, '\\' ))) p = module;
505 else p++;
506 curr_expected_modules[0] = p;
507 sub_expected_modules[0] = p;
508
509 test_process(pid, info.dwProcessId);
510 test_thread(pid, info.dwProcessId);
511 test_main_thread(pid, GetCurrentThreadId());
512 test_module(pid, curr_expected_modules, ARRAY_SIZE(curr_expected_modules));
513 test_module(info.dwProcessId, sub_expected_modules, ARRAY_SIZE(sub_expected_modules));
514
515 SetEvent(ev2);
516 wait_child_process( info.hProcess );
517}