mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / kernel32 / tests / toolhelp.c
blob3bc07ec84f340190e8442f0a63e760d827d7535a
1 /*
2 * Toolhelp
4 * Copyright 2005 Eric Pouech
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.
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.
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
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <stdio.h>
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"
34 static char selfname[MAX_PATH];
36 /* Some functions are only in later versions of kernel32.dll */
37 static HANDLE (WINAPI *pCreateToolhelp32Snapshot)(DWORD, DWORD);
38 static BOOL (WINAPI *pModule32First)(HANDLE, LPMODULEENTRY32);
39 static BOOL (WINAPI *pModule32Next)(HANDLE, LPMODULEENTRY32);
40 static BOOL (WINAPI *pProcess32First)(HANDLE, LPPROCESSENTRY32);
41 static BOOL (WINAPI *pProcess32Next)(HANDLE, LPPROCESSENTRY32);
42 static BOOL (WINAPI *pThread32First)(HANDLE, LPTHREADENTRY32);
43 static BOOL (WINAPI *pThread32Next)(HANDLE, LPTHREADENTRY32);
44 static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, void *, ULONG, ULONG *);
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
51 static DWORD WINAPI sub_thread(void* pmt)
53 DWORD w = WaitForSingleObject(pmt, WAIT_TIME);
54 return w;
57 /******************************************************************
58 * init
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
67 static int init(void)
69 int argc;
70 char** argv;
71 HANDLE ev1, ev2, ev3, hThread;
72 DWORD w, tid;
74 argc = winetest_get_mainargs( &argv );
75 strcpy(selfname, argv[0]);
77 switch (argc)
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);
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);
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;
107 static void test_process(DWORD curr_pid, DWORD sub_pcs_pid)
109 HANDLE hSnapshot;
110 PROCESSENTRY32 pe;
111 MODULEENTRY32 me;
112 unsigned found = 0;
113 int num = 0;
114 int childpos = -1;
116 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
117 ok(hSnapshot != NULL, "Cannot create snapshot\n");
119 /* check that this current process is enumerated */
120 pe.dwSize = sizeof(pe);
121 if (pProcess32First( hSnapshot, &pe ))
125 if (pe.th32ProcessID == curr_pid) found++;
126 if (pe.th32ProcessID == sub_pcs_pid) { childpos = num; found++; }
127 trace("PID=%x %s\n", pe.th32ProcessID, pe.szExeFile);
128 num++;
129 } while (pProcess32Next( hSnapshot, &pe ));
131 ok(found == 2, "couldn't find self and/or sub-process in process list\n");
133 /* check that first really resets the enumeration */
134 found = 0;
135 if (pProcess32First( hSnapshot, &pe ))
139 if (pe.th32ProcessID == curr_pid) found++;
140 if (pe.th32ProcessID == sub_pcs_pid) found++;
141 trace("PID=%x %s\n", pe.th32ProcessID, pe.szExeFile);
142 num--;
143 } while (pProcess32Next( hSnapshot, &pe ));
145 ok(found == 2, "couldn't find self and/or sub-process in process list\n");
146 ok(!num, "mismatch in counting\n");
148 /* one broken program does Process32First() and does not expect anything
149 * interesting to be there, especially not the just forked off child */
150 ok (childpos !=0, "child is not expected to be at position 0.\n");
152 me.dwSize = sizeof(me);
153 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
155 CloseHandle(hSnapshot);
156 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n");
159 static DWORD WINAPI get_id_thread(void* curr_pid)
161 HANDLE hSnapshot;
162 THREADENTRY32 te;
163 HANDLE ev, threads[NUM_THREADS];
164 DWORD thread_ids[NUM_THREADS];
165 DWORD thread_traversed[NUM_THREADS];
166 DWORD tid, first_tid = 0;
167 BOOL found = FALSE;
168 int i, matched_idx = -1;
169 ULONG buf_size = 0;
170 NTSTATUS status;
171 BYTE* pcs_buffer = NULL;
172 DWORD pcs_offset = 0;
173 SYSTEM_PROCESS_INFORMATION* spi = NULL;
175 ev = CreateEventW(NULL, TRUE, FALSE, NULL);
176 ok(ev != NULL, "Cannot create event\n");
178 for (i = 0; i < NUM_THREADS; i++)
180 threads[i] = CreateThread(NULL, 0, sub_thread, ev, 0, &tid);
181 ok(threads[i] != NULL, "Cannot create thread\n");
182 thread_ids[i] = tid;
185 hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
186 ok(hSnapshot != NULL, "Cannot create snapshot\n");
188 /* Check that this current process is enumerated */
189 te.dwSize = sizeof(te);
190 ok(pThread32First(hSnapshot, &te), "Thread cannot traverse\n");
193 if (found)
195 if (te.th32OwnerProcessID != PtrToUlong(curr_pid)) break;
197 if (matched_idx >= 0)
199 thread_traversed[matched_idx++] = te.th32ThreadID;
200 if (matched_idx >= NUM_THREADS) break;
202 else if (thread_ids[0] == te.th32ThreadID)
204 matched_idx = 0;
205 thread_traversed[matched_idx++] = te.th32ThreadID;
208 else if (te.th32OwnerProcessID == PtrToUlong(curr_pid))
210 found = TRUE;
211 first_tid = te.th32ThreadID;
214 while (pThread32Next(hSnapshot, &te));
216 ok(found, "Couldn't find self and/or sub-process in process list\n");
218 /* Check if the thread order is strictly consistent */
219 found = FALSE;
220 for (i = 0; i < NUM_THREADS; i++)
222 if (thread_traversed[i] != thread_ids[i])
224 found = TRUE;
225 break;
227 /* Reset data */
228 thread_traversed[i] = 0;
230 ok(found == FALSE, "The thread order is not strictly consistent\n");
232 /* Determine the order by NtQuerySystemInformation function */
234 while ((status = NtQuerySystemInformation(SystemProcessInformation,
235 pcs_buffer, buf_size, &buf_size)) == STATUS_INFO_LENGTH_MISMATCH)
237 free(pcs_buffer);
238 pcs_buffer = malloc(buf_size);
240 ok(status == STATUS_SUCCESS, "got %#x\n", status);
241 found = FALSE;
242 matched_idx = -1;
246 spi = (SYSTEM_PROCESS_INFORMATION*)&pcs_buffer[pcs_offset];
247 if (spi->UniqueProcessId == curr_pid)
249 found = TRUE;
250 break;
252 pcs_offset += spi->NextEntryOffset;
253 } while (spi->NextEntryOffset != 0);
255 ok(found && spi, "No process found\n");
256 for (i = 0; i < spi->dwThreadCount; i++)
258 tid = HandleToULong(spi->ti[i].ClientId.UniqueThread);
259 if (matched_idx > 0)
261 thread_traversed[matched_idx++] = tid;
262 if (matched_idx >= NUM_THREADS) break;
264 else if (tid == thread_ids[0])
266 matched_idx = 0;
267 thread_traversed[matched_idx++] = tid;
270 free(pcs_buffer);
272 ok(matched_idx > 0, "No thread id match found\n");
274 found = FALSE;
275 for (i = 0; i < NUM_THREADS; i++)
277 if (thread_traversed[i] != thread_ids[i])
279 found = TRUE;
280 break;
283 ok(found == FALSE, "wrong order in NtQuerySystemInformation function\n");
285 SetEvent(ev);
286 WaitForMultipleObjects( NUM_THREADS, threads, TRUE, WAIT_TIME );
287 for (i = 0; i < NUM_THREADS; i++)
288 CloseHandle(threads[i]);
289 CloseHandle(ev);
290 CloseHandle(hSnapshot);
292 return first_tid;
295 static void test_main_thread(DWORD curr_pid, DWORD main_tid)
297 HANDLE thread;
298 DWORD tid = 0;
299 int error;
301 /* Check that the main thread id is first one in this thread. */
302 tid = get_id_thread(ULongToPtr(curr_pid));
303 ok(tid == main_tid, "The first thread id returned is not the main thread id\n");
305 /* Check that the main thread id is first one in other thread. */
306 thread = CreateThread(NULL, 0, get_id_thread, ULongToPtr(curr_pid), 0, NULL);
307 error = WaitForSingleObject(thread, WAIT_TIME);
308 ok(error == WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
310 ok(GetExitCodeThread(thread, &tid), "Could not retrieve exit code\n");
311 ok(tid == main_tid, "The first thread id returned is not the main thread id\n");
314 static void test_thread(DWORD curr_pid, DWORD sub_pcs_pid)
316 HANDLE hSnapshot;
317 THREADENTRY32 te;
318 MODULEENTRY32 me;
319 int num = 0;
320 unsigned curr_found = 0;
321 unsigned sub_found = 0;
323 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
324 ok(hSnapshot != NULL, "Cannot create snapshot\n");
326 /* check that this current process is enumerated */
327 te.dwSize = sizeof(te);
328 if (pThread32First( hSnapshot, &te ))
332 if (te.th32OwnerProcessID == curr_pid) curr_found++;
333 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++;
334 if (winetest_debug > 1)
335 trace("PID=%x TID=%x %d\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri);
336 num++;
337 } while (pThread32Next( hSnapshot, &te ));
339 ok(curr_found, "couldn't find self in thread list\n");
340 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
342 /* check that first really resets enumeration */
343 curr_found = 0;
344 sub_found = 0;
345 if (pThread32First( hSnapshot, &te ))
349 if (te.th32OwnerProcessID == curr_pid) curr_found++;
350 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++;
351 if (winetest_debug > 1)
352 trace("PID=%x TID=%x %d\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri);
353 num--;
354 } while (pThread32Next( hSnapshot, &te ));
356 ok(curr_found, "couldn't find self in thread list\n");
357 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n");
359 me.dwSize = sizeof(me);
360 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
362 CloseHandle(hSnapshot);
363 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n");
366 static const char* curr_expected_modules[] =
368 "kernel32_test.exe",
369 "kernel32.dll",
370 "ntdll.dll"
373 static const char* sub_expected_modules[] =
375 "kernel32_test.exe",
376 "kernel32.dll",
377 "shell32.dll",
378 "ntdll.dll"
381 static void test_module(DWORD pid, const char* expected[], unsigned num_expected)
383 HANDLE hSnapshot;
384 PROCESSENTRY32 pe;
385 THREADENTRY32 te;
386 MODULEENTRY32 me;
387 unsigned found[32];
388 unsigned i;
389 int num = 0;
391 ok(ARRAY_SIZE(found) >= num_expected, "Internal: bump found[] size\n");
393 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
394 ok(hSnapshot != NULL, "Cannot create snapshot\n");
396 for (i = 0; i < num_expected; i++) found[i] = 0;
397 me.dwSize = sizeof(me);
398 if (pModule32First( hSnapshot, &me ))
402 trace("PID=%x base=%p size=%x %s %s\n",
403 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule);
404 ok(me.th32ProcessID == pid, "wrong returned process id\n");
405 for (i = 0; i < num_expected; i++)
406 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++;
407 num++;
408 } while (pModule32Next( hSnapshot, &me ));
410 for (i = 0; i < num_expected; i++)
411 ok(found[i] == 1, "Module %s is %s\n",
412 expected[i], found[i] ? "listed more than once" : "not listed");
414 /* check that first really resets the enumeration */
415 for (i = 0; i < num_expected; i++) found[i] = 0;
416 me.dwSize = sizeof(me);
417 if (pModule32First( hSnapshot, &me ))
421 trace("PID=%x base=%p size=%x %s %s\n",
422 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule);
423 for (i = 0; i < num_expected; i++)
424 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++;
425 num--;
426 } while (pModule32Next( hSnapshot, &me ));
428 for (i = 0; i < num_expected; i++)
429 ok(found[i] == 1, "Module %s is %s\n",
430 expected[i], found[i] ? "listed more than once" : "not listed");
431 ok(!num, "mismatch in counting\n");
433 pe.dwSize = sizeof(pe);
434 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n");
436 te.dwSize = sizeof(te);
437 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n");
439 CloseHandle(hSnapshot);
440 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n");
443 START_TEST(toolhelp)
445 DWORD pid = GetCurrentProcessId();
446 int r;
447 char *p, module[MAX_PATH];
448 char buffer[MAX_PATH + 21];
449 SECURITY_ATTRIBUTES sa;
450 PROCESS_INFORMATION info;
451 STARTUPINFOA startup;
452 HANDLE ev1, ev2;
453 DWORD w;
454 HANDLE hkernel32 = GetModuleHandleA("kernel32");
455 HANDLE hntdll = GetModuleHandleA("ntdll.dll");
457 pCreateToolhelp32Snapshot = (VOID *) GetProcAddress(hkernel32, "CreateToolhelp32Snapshot");
458 pModule32First = (VOID *) GetProcAddress(hkernel32, "Module32First");
459 pModule32Next = (VOID *) GetProcAddress(hkernel32, "Module32Next");
460 pProcess32First = (VOID *) GetProcAddress(hkernel32, "Process32First");
461 pProcess32Next = (VOID *) GetProcAddress(hkernel32, "Process32Next");
462 pThread32First = (VOID *) GetProcAddress(hkernel32, "Thread32First");
463 pThread32Next = (VOID *) GetProcAddress(hkernel32, "Thread32Next");
464 pNtQuerySystemInformation = (VOID *) GetProcAddress(hntdll, "NtQuerySystemInformation");
466 if (!pCreateToolhelp32Snapshot ||
467 !pModule32First || !pModule32Next ||
468 !pProcess32First || !pProcess32Next ||
469 !pThread32First || !pThread32Next ||
470 !pNtQuerySystemInformation)
472 win_skip("Needed functions are not available, most likely running on Windows NT\n");
473 return;
476 r = init();
477 ok(r == 0, "Basic init of sub-process test\n");
478 if (r != 0) return;
480 sa.nLength = sizeof(sa);
481 sa.lpSecurityDescriptor = NULL;
482 sa.bInheritHandle = TRUE;
484 ev1 = CreateEventW(&sa, FALSE, FALSE, NULL);
485 ev2 = CreateEventW(&sa, FALSE, FALSE, NULL);
486 ok (ev1 != NULL && ev2 != NULL, "Couldn't create events\n");
487 memset(&startup, 0, sizeof(startup));
488 startup.cb = sizeof(startup);
489 startup.dwFlags = STARTF_USESHOWWINDOW;
490 startup.wShowWindow = SW_SHOWNORMAL;
492 sprintf(buffer, "%s toolhelp %lu %lu", selfname, (DWORD_PTR)ev1, (DWORD_PTR)ev2);
493 ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
494 /* wait for child to be initialized */
495 w = WaitForSingleObject(ev1, WAIT_TIME);
496 ok(w == WAIT_OBJECT_0, "Failed to wait on sub-process startup\n");
498 GetModuleFileNameA( 0, module, sizeof(module) );
499 if (!(p = strrchr( module, '\\' ))) p = module;
500 else p++;
501 curr_expected_modules[0] = p;
502 sub_expected_modules[0] = p;
504 test_process(pid, info.dwProcessId);
505 test_thread(pid, info.dwProcessId);
506 test_main_thread(pid, GetCurrentThreadId());
507 test_module(pid, curr_expected_modules, ARRAY_SIZE(curr_expected_modules));
508 test_module(info.dwProcessId, sub_expected_modules, ARRAY_SIZE(sub_expected_modules));
510 SetEvent(ev2);
511 wait_child_process( info.hProcess );