Reactos
at master 517 lines 18 kB view raw
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}