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 "precomp.h" 22 23 static char selfname[MAX_PATH]; 24 25 /* Some functions are only in later versions of kernel32.dll */ 26 static HANDLE (WINAPI *pCreateToolhelp32Snapshot)(DWORD, DWORD); 27 static BOOL (WINAPI *pModule32First)(HANDLE, LPMODULEENTRY32); 28 static BOOL (WINAPI *pModule32Next)(HANDLE, LPMODULEENTRY32); 29 static BOOL (WINAPI *pProcess32First)(HANDLE, LPPROCESSENTRY32); 30 static BOOL (WINAPI *pProcess32Next)(HANDLE, LPPROCESSENTRY32); 31 static BOOL (WINAPI *pThread32First)(HANDLE, LPTHREADENTRY32); 32 static BOOL (WINAPI *pThread32Next)(HANDLE, LPTHREADENTRY32); 33 34 /* 1 minute should be more than enough */ 35 #define WAIT_TIME (60 * 1000) 36 37 static DWORD WINAPI sub_thread(void* pmt) 38 { 39 DWORD w = WaitForSingleObject(pmt, WAIT_TIME); 40 return w; 41 } 42 43 /****************************************************************** 44 * init 45 * 46 * generates basic information like: 47 * selfname: the way to reinvoke ourselves 48 * returns: 49 * -1 on error 50 * 0 if parent 51 * doesn't return if child 52 */ 53 static int init(void) 54 { 55 int argc; 56 char** argv; 57 HANDLE ev1, ev2, ev3, hThread; 58 DWORD w, tid; 59 60 argc = winetest_get_mainargs( &argv ); 61 strcpy(selfname, argv[0]); 62 63 switch (argc) 64 { 65 case 2: /* the test program */ 66 return 0; 67 case 4: /* the sub-process */ 68 ev1 = (HANDLE)(INT_PTR)atoi(argv[2]); 69 ev2 = (HANDLE)(INT_PTR)atoi(argv[3]); 70 ev3 = CreateEventW(NULL, FALSE, FALSE, NULL); 71 72 if (ev3 == NULL) ExitProcess(WAIT_ABANDONED); 73 hThread = CreateThread(NULL, 0, sub_thread, ev3, 0, &tid); 74 if (hThread == NULL) ExitProcess(WAIT_ABANDONED); 75 if (!LoadLibraryA("shell32.dll")) ExitProcess(WAIT_ABANDONED); 76 77 /* signal init of sub-process is done */ 78 SetEvent(ev1); 79 /* wait for parent to have done all its queries */ 80 w = WaitForSingleObject(ev2, WAIT_TIME); 81 if (w != WAIT_OBJECT_0) ExitProcess(w); 82 /* signal sub-thread to terminate */ 83 SetEvent(ev3); 84 w = WaitForSingleObject(hThread, WAIT_TIME); 85 if (w != WAIT_OBJECT_0) ExitProcess(w); 86 GetExitCodeThread(hThread, &w); 87 ExitProcess(w); 88 default: 89 return -1; 90 } 91 } 92 93 static void test_process(DWORD curr_pid, DWORD sub_pcs_pid) 94 { 95 HANDLE hSnapshot; 96 PROCESSENTRY32 pe; 97 MODULEENTRY32 me; 98 unsigned found = 0; 99 int num = 0; 100 int childpos = -1; 101 102 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); 103 ok(hSnapshot != NULL, "Cannot create snapshot\n"); 104 105 /* check that this current process is enumerated */ 106 pe.dwSize = sizeof(pe); 107 if (pProcess32First( hSnapshot, &pe )) 108 { 109 do 110 { 111 if (pe.th32ProcessID == curr_pid) found++; 112 if (pe.th32ProcessID == sub_pcs_pid) { childpos = num; found++; } 113 trace("PID=%x %s\n", pe.th32ProcessID, pe.szExeFile); 114 num++; 115 } while (pProcess32Next( hSnapshot, &pe )); 116 } 117 ok(found == 2, "couldn't find self and/or sub-process in process list\n"); 118 119 /* check that first really resets the enumeration */ 120 found = 0; 121 if (pProcess32First( hSnapshot, &pe )) 122 { 123 do 124 { 125 if (pe.th32ProcessID == curr_pid) found++; 126 if (pe.th32ProcessID == sub_pcs_pid) found++; 127 trace("PID=%x %s\n", pe.th32ProcessID, pe.szExeFile); 128 num--; 129 } while (pProcess32Next( hSnapshot, &pe )); 130 } 131 ok(found == 2, "couldn't find self and/or sub-process in process list\n"); 132 ok(!num, "mismatch in counting\n"); 133 134 /* one broken program does Process32First() and does not expect anything 135 * interesting to be there, especially not the just forked off child */ 136 ok (childpos !=0, "child is not expected to be at position 0.\n"); 137 138 me.dwSize = sizeof(me); 139 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n"); 140 141 CloseHandle(hSnapshot); 142 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n"); 143 } 144 145 static void test_thread(DWORD curr_pid, DWORD sub_pcs_pid) 146 { 147 HANDLE hSnapshot; 148 THREADENTRY32 te; 149 MODULEENTRY32 me; 150 int num = 0; 151 unsigned curr_found = 0; 152 unsigned sub_found = 0; 153 154 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 155 ok(hSnapshot != NULL, "Cannot create snapshot\n"); 156 157 /* check that this current process is enumerated */ 158 te.dwSize = sizeof(te); 159 if (pThread32First( hSnapshot, &te )) 160 { 161 do 162 { 163 if (te.th32OwnerProcessID == curr_pid) curr_found++; 164 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++; 165 if (winetest_debug > 1) 166 trace("PID=%x TID=%x %d\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri); 167 num++; 168 } while (pThread32Next( hSnapshot, &te )); 169 } 170 ok(curr_found, "couldn't find self in thread list\n"); 171 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n"); 172 173 /* check that first really resets enumeration */ 174 curr_found = 0; 175 sub_found = 0; 176 if (pThread32First( hSnapshot, &te )) 177 { 178 do 179 { 180 if (te.th32OwnerProcessID == curr_pid) curr_found++; 181 if (te.th32OwnerProcessID == sub_pcs_pid) sub_found++; 182 if (winetest_debug > 1) 183 trace("PID=%x TID=%x %d\n", te.th32OwnerProcessID, te.th32ThreadID, te.tpBasePri); 184 num--; 185 } while (pThread32Next( hSnapshot, &te )); 186 } 187 ok(curr_found, "couldn't find self in thread list\n"); 188 ok(sub_found >= 2, "couldn't find sub-process threads in thread list\n"); 189 190 me.dwSize = sizeof(me); 191 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n"); 192 193 CloseHandle(hSnapshot); 194 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n"); 195 } 196 197 static const char* curr_expected_modules[] = 198 { 199 "kernel32_test.exe", 200 "kernel32.dll", 201 "ntdll.dll" 202 }; 203 204 static const char* sub_expected_modules[] = 205 { 206 "kernel32_test.exe", 207 "kernel32.dll", 208 "shell32.dll", 209 "ntdll.dll" 210 }; 211 212 #define NUM_OF(x) (sizeof(x) / sizeof(x[0])) 213 214 static void test_module(DWORD pid, const char* expected[], unsigned num_expected) 215 { 216 HANDLE hSnapshot; 217 PROCESSENTRY32 pe; 218 THREADENTRY32 te; 219 MODULEENTRY32 me; 220 unsigned found[32]; 221 unsigned i; 222 int num = 0; 223 224 ok(NUM_OF(found) >= num_expected, "Internal: bump found[] size\n"); 225 226 hSnapshot = pCreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid ); 227 ok(hSnapshot != NULL, "Cannot create snapshot\n"); 228 229 for (i = 0; i < num_expected; i++) found[i] = 0; 230 me.dwSize = sizeof(me); 231 if (pModule32First( hSnapshot, &me )) 232 { 233 do 234 { 235 trace("PID=%x base=%p size=%x %s %s\n", 236 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule); 237 ok(me.th32ProcessID == pid, "wrong returned process id\n"); 238 for (i = 0; i < num_expected; i++) 239 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++; 240 num++; 241 } while (pModule32Next( hSnapshot, &me )); 242 } 243 for (i = 0; i < num_expected; i++) 244 ok(found[i] == 1, "Module %s is %s\n", 245 expected[i], found[i] ? "listed more than once" : "not listed"); 246 247 /* check that first really resets the enumeration */ 248 for (i = 0; i < num_expected; i++) found[i] = 0; 249 me.dwSize = sizeof(me); 250 if (pModule32First( hSnapshot, &me )) 251 { 252 do 253 { 254 trace("PID=%x base=%p size=%x %s %s\n", 255 me.th32ProcessID, me.modBaseAddr, me.modBaseSize, me.szExePath, me.szModule); 256 for (i = 0; i < num_expected; i++) 257 if (!lstrcmpiA(expected[i], me.szModule)) found[i]++; 258 num--; 259 } while (pModule32Next( hSnapshot, &me )); 260 } 261 for (i = 0; i < num_expected; i++) 262 ok(found[i] == 1, "Module %s is %s\n", 263 expected[i], found[i] ? "listed more than once" : "not listed"); 264 ok(!num, "mismatch in counting\n"); 265 266 pe.dwSize = sizeof(pe); 267 ok(!pProcess32First( hSnapshot, &pe ), "shouldn't return a process\n"); 268 269 te.dwSize = sizeof(te); 270 ok(!pThread32First( hSnapshot, &te ), "shouldn't return a thread\n"); 271 272 CloseHandle(hSnapshot); 273 ok(!pModule32First( hSnapshot, &me ), "shouldn't return a module\n"); 274 } 275 276 START_TEST(toolhelp) 277 { 278 DWORD pid = GetCurrentProcessId(); 279 int r; 280 char *p, module[MAX_PATH]; 281 char buffer[MAX_PATH]; 282 SECURITY_ATTRIBUTES sa; 283 PROCESS_INFORMATION info; 284 STARTUPINFOA startup; 285 HANDLE ev1, ev2; 286 DWORD w; 287 HANDLE hkernel32 = GetModuleHandleA("kernel32"); 288 289 pCreateToolhelp32Snapshot = (VOID *) GetProcAddress(hkernel32, "CreateToolhelp32Snapshot"); 290 pModule32First = (VOID *) GetProcAddress(hkernel32, "Module32First"); 291 pModule32Next = (VOID *) GetProcAddress(hkernel32, "Module32Next"); 292 pProcess32First = (VOID *) GetProcAddress(hkernel32, "Process32First"); 293 pProcess32Next = (VOID *) GetProcAddress(hkernel32, "Process32Next"); 294 pThread32First = (VOID *) GetProcAddress(hkernel32, "Thread32First"); 295 pThread32Next = (VOID *) GetProcAddress(hkernel32, "Thread32Next"); 296 297 if (!pCreateToolhelp32Snapshot || 298 !pModule32First || !pModule32Next || 299 !pProcess32First || !pProcess32Next || 300 !pThread32First || !pThread32Next) 301 { 302 win_skip("Needed functions are not available, most likely running on Windows NT\n"); 303 return; 304 } 305 306 r = init(); 307 ok(r == 0, "Basic init of sub-process test\n"); 308 if (r != 0) return; 309 310 sa.nLength = sizeof(sa); 311 sa.lpSecurityDescriptor = NULL; 312 sa.bInheritHandle = TRUE; 313 314 ev1 = CreateEventW(&sa, FALSE, FALSE, NULL); 315 ev2 = CreateEventW(&sa, FALSE, FALSE, NULL); 316 ok (ev1 != NULL && ev2 != NULL, "Couldn't create events\n"); 317 memset(&startup, 0, sizeof(startup)); 318 startup.cb = sizeof(startup); 319 startup.dwFlags = STARTF_USESHOWWINDOW; 320 startup.wShowWindow = SW_SHOWNORMAL; 321 322 sprintf(buffer, "%s tests/toolhelp.c %lu %lu", selfname, (DWORD_PTR)ev1, (DWORD_PTR)ev2); 323 ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n"); 324 /* wait for child to be initialized */ 325 w = WaitForSingleObject(ev1, WAIT_TIME); 326 ok(w == WAIT_OBJECT_0, "Failed to wait on sub-process startup\n"); 327 328 GetModuleFileNameA( 0, module, sizeof(module) ); 329 if (!(p = strrchr( module, '\\' ))) p = module; 330 else p++; 331 curr_expected_modules[0] = p; 332 sub_expected_modules[0] = p; 333 334 test_process(pid, info.dwProcessId); 335 test_thread(pid, info.dwProcessId); 336 test_module(pid, curr_expected_modules, NUM_OF(curr_expected_modules)); 337 test_module(info.dwProcessId, sub_expected_modules, NUM_OF(sub_expected_modules)); 338 339 SetEvent(ev2); 340 winetest_wait_child_process( info.hProcess ); 341 } 342