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
26 #define WIN32_NO_STATUS
30 #include "wine/test.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 */
51 static DWORD WINAPI
sub_thread(void* pmt
)
53 DWORD w
= WaitForSingleObject(pmt
, WAIT_TIME
);
57 /******************************************************************
60 * generates basic information like:
61 * selfname: the way to reinvoke ourselves
65 * doesn't return if child
71 HANDLE ev1
, ev2
, ev3
, hThread
;
74 argc
= winetest_get_mainargs( &argv
);
75 strcpy(selfname
, argv
[0]);
79 case 2: /* the test program */
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 */
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 */
98 w
= WaitForSingleObject(hThread
, WAIT_TIME
);
99 if (w
!= WAIT_OBJECT_0
) ExitProcess(w
);
100 GetExitCodeThread(hThread
, &w
);
107 static void test_process(DWORD curr_pid
, DWORD sub_pcs_pid
)
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
);
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 */
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
);
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
)
163 HANDLE ev
, threads
[NUM_THREADS
];
164 DWORD thread_ids
[NUM_THREADS
];
165 DWORD thread_traversed
[NUM_THREADS
];
166 DWORD tid
, first_tid
= 0;
168 int i
, matched_idx
= -1;
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");
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");
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
)
205 thread_traversed
[matched_idx
++] = te
.th32ThreadID
;
208 else if (te
.th32OwnerProcessID
== PtrToUlong(curr_pid
))
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 */
220 for (i
= 0; i
< NUM_THREADS
; i
++)
222 if (thread_traversed
[i
] != thread_ids
[i
])
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
)
238 pcs_buffer
= malloc(buf_size
);
240 ok(status
== STATUS_SUCCESS
, "got %#x\n", status
);
246 spi
= (SYSTEM_PROCESS_INFORMATION
*)&pcs_buffer
[pcs_offset
];
247 if (spi
->UniqueProcessId
== curr_pid
)
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
);
261 thread_traversed
[matched_idx
++] = tid
;
262 if (matched_idx
>= NUM_THREADS
) break;
264 else if (tid
== thread_ids
[0])
267 thread_traversed
[matched_idx
++] = tid
;
272 ok(matched_idx
> 0, "No thread id match found\n");
275 for (i
= 0; i
< NUM_THREADS
; i
++)
277 if (thread_traversed
[i
] != thread_ids
[i
])
283 ok(found
== FALSE
, "wrong order in NtQuerySystemInformation function\n");
286 WaitForMultipleObjects( NUM_THREADS
, threads
, TRUE
, WAIT_TIME
);
287 for (i
= 0; i
< NUM_THREADS
; i
++)
288 CloseHandle(threads
[i
]);
290 CloseHandle(hSnapshot
);
295 static void test_main_thread(DWORD curr_pid
, DWORD main_tid
)
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
)
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
);
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 */
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
);
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
[] =
373 static const char* sub_expected_modules
[] =
381 static void test_module(DWORD pid
, const char* expected
[], unsigned num_expected
)
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
]++;
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
]++;
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");
445 DWORD pid
= GetCurrentProcessId();
447 char *p
, module
[MAX_PATH
];
448 char buffer
[MAX_PATH
+ 21];
449 SECURITY_ATTRIBUTES sa
;
450 PROCESS_INFORMATION info
;
451 STARTUPINFOA startup
;
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");
477 ok(r
== 0, "Basic init of sub-process test\n");
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
;
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
));
511 wait_child_process( info
.hProcess
);