2 * Misc Toolhelp functions
4 * Copyright 1996 Marcus Meissner
5 * Copyright 2005 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #define WIN32_NO_STATUS
36 #include "kernel_private.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(toolhelp
);
55 static WCHAR
*fetch_string( HANDLE hProcess
, UNICODE_STRING
* us
)
59 local
= HeapAlloc( GetProcessHeap(), 0, us
->Length
);
62 if (!ReadProcessMemory( hProcess
, us
->Buffer
, local
, us
->Length
, NULL
))
64 HeapFree( GetProcessHeap(), 0, local
);
72 static BOOL
fetch_module( DWORD process
, DWORD flags
, LDR_DATA_TABLE_ENTRY
**ldr_mod
, ULONG
*num
)
75 PROCESS_BASIC_INFORMATION pbi
;
76 PPEB_LDR_DATA pLdrData
;
77 PLIST_ENTRY head
, curr
;
82 if (!(flags
& TH32CS_SNAPMODULE
)) return TRUE
;
86 hProcess
= OpenProcess( PROCESS_VM_READ
| PROCESS_QUERY_INFORMATION
, FALSE
, process
);
87 if (!hProcess
) return FALSE
;
90 hProcess
= GetCurrentProcess();
92 if (set_ntstatus( NtQueryInformationProcess( hProcess
, ProcessBasicInformation
,
93 &pbi
, sizeof(pbi
), NULL
)))
95 if (ReadProcessMemory( hProcess
, &pbi
.PebBaseAddress
->LdrData
,
96 &pLdrData
, sizeof(pLdrData
), NULL
) &&
97 ReadProcessMemory( hProcess
,
98 &pLdrData
->InLoadOrderModuleList
.Flink
,
99 &curr
, sizeof(curr
), NULL
))
101 head
= &pLdrData
->InLoadOrderModuleList
;
106 *ldr_mod
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LDR_DATA_TABLE_ENTRY
) );
108 *ldr_mod
= HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, *ldr_mod
,
109 (*num
+ 1) * sizeof(LDR_DATA_TABLE_ENTRY
) );
110 if (!*ldr_mod
) break;
111 if (!ReadProcessMemory( hProcess
,
112 CONTAINING_RECORD(curr
, LDR_DATA_TABLE_ENTRY
,
115 sizeof(LDR_DATA_TABLE_ENTRY
), NULL
))
117 curr
= (*ldr_mod
)[*num
].InLoadOrderLinks
.Flink
;
118 /* if we cannot fetch the strings, then just ignore this LDR_DATA_TABLE_ENTRY
119 * and continue loading the other ones in the list
121 if (!fetch_string( hProcess
, &(*ldr_mod
)[*num
].BaseDllName
)) continue;
122 if (fetch_string( hProcess
, &(*ldr_mod
)[*num
].FullDllName
))
125 HeapFree( GetProcessHeap(), 0, (*ldr_mod
)[*num
].BaseDllName
.Buffer
);
131 if (process
) CloseHandle( hProcess
);
135 static void fill_module( struct snapshot
* snap
, ULONG
* offset
, ULONG process
,
136 LDR_DATA_TABLE_ENTRY
* ldr_mod
, ULONG num
)
142 snap
->module_count
= num
;
143 snap
->module_pos
= 0;
145 snap
->module_offset
= *offset
;
147 mod
= (MODULEENTRY32W
*)&snap
->data
[*offset
];
149 for (i
= 0; i
< num
; i
++)
151 mod
->dwSize
= sizeof(MODULEENTRY32W
);
152 mod
->th32ModuleID
= 1; /* toolhelp internal id, never used */
153 mod
->th32ProcessID
= process
? process
: GetCurrentProcessId();
154 mod
->GlblcntUsage
= 0xFFFF; /* FIXME */
155 mod
->ProccntUsage
= 0xFFFF; /* FIXME */
156 mod
->modBaseAddr
= ldr_mod
[i
].DllBase
;
157 mod
->modBaseSize
= ldr_mod
[i
].SizeOfImage
;
158 mod
->hModule
= ldr_mod
[i
].DllBase
;
160 l
= min(ldr_mod
[i
].BaseDllName
.Length
, sizeof(mod
->szModule
) - sizeof(WCHAR
));
161 memcpy(mod
->szModule
, ldr_mod
[i
].BaseDllName
.Buffer
, l
);
162 mod
->szModule
[l
/ sizeof(WCHAR
)] = '\0';
163 l
= min(ldr_mod
[i
].FullDllName
.Length
, sizeof(mod
->szExePath
) - sizeof(WCHAR
));
164 memcpy(mod
->szExePath
, ldr_mod
[i
].FullDllName
.Buffer
, l
);
165 mod
->szExePath
[l
/ sizeof(WCHAR
)] = '\0';
170 *offset
+= num
* sizeof(MODULEENTRY32W
);
173 static BOOL
fetch_process_thread( DWORD flags
, SYSTEM_PROCESS_INFORMATION
** pspi
,
174 ULONG
* num_pcs
, ULONG
* num_thd
)
178 PSYSTEM_PROCESS_INFORMATION spi
;
180 *num_pcs
= *num_thd
= 0;
181 if (!(flags
& (TH32CS_SNAPPROCESS
| TH32CS_SNAPTHREAD
))) return TRUE
;
183 *pspi
= HeapAlloc( GetProcessHeap(), 0, size
= 4096 );
186 status
= NtQuerySystemInformation( SystemProcessInformation
, *pspi
,
191 *num_pcs
= *num_thd
= offset
= 0;
195 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ offset
);
196 if (flags
& TH32CS_SNAPPROCESS
) (*num_pcs
)++;
197 if (flags
& TH32CS_SNAPTHREAD
) *num_thd
+= spi
->dwThreadCount
;
198 } while ((offset
= spi
->NextEntryOffset
));
200 case STATUS_INFO_LENGTH_MISMATCH
:
201 *pspi
= HeapReAlloc( GetProcessHeap(), 0, *pspi
, size
*= 2 );
204 SetLastError( RtlNtStatusToDosError( status
) );
210 static void fill_process( struct snapshot
* snap
, ULONG
* offset
,
211 SYSTEM_PROCESS_INFORMATION
* spi
, ULONG num
)
213 PROCESSENTRY32W
* pcs_entry
;
217 snap
->process_count
= num
;
218 snap
->process_pos
= 0;
220 snap
->process_offset
= *offset
;
222 pcs_entry
= (PROCESSENTRY32W
*)&snap
->data
[*offset
];
226 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
228 pcs_entry
->dwSize
= sizeof(PROCESSENTRY32W
);
229 pcs_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
230 pcs_entry
->th32ProcessID
= HandleToUlong(spi
->UniqueProcessId
);
231 pcs_entry
->th32DefaultHeapID
= 0; /* MSDN says no longer used, always 0 */
232 pcs_entry
->th32ModuleID
= 0; /* MSDN says no longer used, always 0 */
233 pcs_entry
->cntThreads
= spi
->dwThreadCount
;
234 pcs_entry
->th32ParentProcessID
= HandleToUlong(spi
->ParentProcessId
);
235 pcs_entry
->pcPriClassBase
= spi
->dwBasePriority
;
236 pcs_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0 */
237 l
= min(spi
->ProcessName
.Length
, sizeof(pcs_entry
->szExeFile
) - sizeof(WCHAR
));
238 memcpy(pcs_entry
->szExeFile
, spi
->ProcessName
.Buffer
, l
);
239 pcs_entry
->szExeFile
[l
/ sizeof(WCHAR
)] = '\0';
241 } while ((poff
= spi
->NextEntryOffset
));
243 *offset
+= num
* sizeof(PROCESSENTRY32W
);
246 static void fill_thread( struct snapshot
* snap
, ULONG
* offset
, LPVOID info
, ULONG num
)
248 THREADENTRY32
* thd_entry
;
249 SYSTEM_PROCESS_INFORMATION
* spi
;
250 SYSTEM_THREAD_INFORMATION
* sti
;
253 snap
->thread_count
= num
;
254 snap
->thread_pos
= 0;
256 snap
->thread_offset
= *offset
;
258 thd_entry
= (THREADENTRY32
*)&snap
->data
[*offset
];
263 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
266 for (i
= 0; i
< spi
->dwThreadCount
; i
++)
268 thd_entry
->dwSize
= sizeof(THREADENTRY32
);
269 thd_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
270 thd_entry
->th32ThreadID
= HandleToUlong(sti
->ClientId
.UniqueThread
);
271 thd_entry
->th32OwnerProcessID
= HandleToUlong(sti
->ClientId
.UniqueProcess
);
272 thd_entry
->tpBasePri
= sti
->dwBasePriority
;
273 thd_entry
->tpDeltaPri
= 0; /* MSDN says no longer used, always 0 */
274 thd_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0" */
279 } while ((poff
= spi
->NextEntryOffset
));
280 *offset
+= num
* sizeof(THREADENTRY32
);
283 /***********************************************************************
284 * CreateToolhelp32Snapshot (KERNEL32.@)
286 HANDLE WINAPI
CreateToolhelp32Snapshot( DWORD flags
, DWORD process
)
288 SYSTEM_PROCESS_INFORMATION
* spi
= NULL
;
289 LDR_DATA_TABLE_ENTRY
*mod
= NULL
;
290 ULONG num_pcs
, num_thd
, num_mod
;
291 HANDLE hSnapShot
= 0;
293 TRACE("%x,%x\n", flags
, process
);
294 if (!(flags
& (TH32CS_SNAPPROCESS
|TH32CS_SNAPTHREAD
|TH32CS_SNAPMODULE
)))
296 FIXME("flags %x not implemented\n", flags
);
297 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
298 return INVALID_HANDLE_VALUE
;
301 if (fetch_module( process
, flags
, &mod
, &num_mod
) &&
302 fetch_process_thread( flags
, &spi
, &num_pcs
, &num_thd
))
305 struct snapshot
*snap
;
306 SECURITY_ATTRIBUTES sa
;
308 /* create & fill the snapshot section */
309 sect_size
= sizeof(struct snapshot
) - 1; /* for last data[1] */
310 if (flags
& TH32CS_SNAPMODULE
) sect_size
+= num_mod
* sizeof(MODULEENTRY32W
);
311 if (flags
& TH32CS_SNAPPROCESS
) sect_size
+= num_pcs
* sizeof(PROCESSENTRY32W
);
312 if (flags
& TH32CS_SNAPTHREAD
) sect_size
+= num_thd
* sizeof(THREADENTRY32
);
313 if (flags
& TH32CS_SNAPHEAPLIST
)FIXME("Unimplemented: heap list snapshot\n");
315 sa
.bInheritHandle
= (flags
& TH32CS_INHERIT
) != 0;
316 sa
.lpSecurityDescriptor
= NULL
;
318 hSnapShot
= CreateFileMappingW( INVALID_HANDLE_VALUE
, &sa
,
319 SEC_COMMIT
| PAGE_READWRITE
,
320 0, sect_size
, NULL
);
321 if (hSnapShot
&& (snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
325 fill_module( snap
, &offset
, process
, mod
, num_mod
);
326 fill_process( snap
, &offset
, spi
, num_pcs
);
327 fill_thread( snap
, &offset
, spi
, num_thd
);
328 UnmapViewOfFile( snap
);
334 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].BaseDllName
.Buffer
);
335 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].FullDllName
.Buffer
);
337 HeapFree( GetProcessHeap(), 0, mod
);
338 HeapFree( GetProcessHeap(), 0, spi
);
340 if (!hSnapShot
) return INVALID_HANDLE_VALUE
;
344 static BOOL
next_thread( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
, BOOL first
)
346 struct snapshot
* snap
;
349 if (lpte
->dwSize
< sizeof(THREADENTRY32
))
351 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
352 WARN("Result buffer too small (%d)\n", lpte
->dwSize
);
355 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
357 if (first
) snap
->thread_pos
= 0;
358 if (snap
->thread_pos
< snap
->thread_count
)
360 LPTHREADENTRY32 te
= (THREADENTRY32
*)&snap
->data
[snap
->thread_offset
];
361 *lpte
= te
[snap
->thread_pos
++];
364 else SetLastError( ERROR_NO_MORE_FILES
);
365 UnmapViewOfFile( snap
);
370 /***********************************************************************
371 * Thread32First (KERNEL32.@)
373 * Return info about the first thread in a toolhelp32 snapshot
375 BOOL WINAPI
Thread32First( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
377 return next_thread( hSnapShot
, lpte
, TRUE
);
380 /***********************************************************************
381 * Thread32Next (KERNEL32.@)
383 * Return info about the first thread in a toolhelp32 snapshot
385 BOOL WINAPI
Thread32Next( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
387 return next_thread( hSnapShot
, lpte
, FALSE
);
390 /***********************************************************************
393 * Implementation of Process32First/Next. Note that the ANSI / Unicode
394 * version check is a bit of a hack as it relies on the fact that only
395 * the last field is actually different.
397 static BOOL
process_next( HANDLE hSnapShot
, LPPROCESSENTRY32W lppe
,
398 BOOL first
, BOOL unicode
)
400 struct snapshot
* snap
;
402 DWORD sz
= unicode
? sizeof(PROCESSENTRY32W
) : sizeof(PROCESSENTRY32
);
404 if (lppe
->dwSize
< sz
)
406 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
407 WARN("Result buffer too small (%d)\n", lppe
->dwSize
);
410 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
412 if (first
) snap
->process_pos
= 0;
413 if (snap
->process_pos
< snap
->process_count
)
415 LPPROCESSENTRY32W pe
= (PROCESSENTRY32W
*)&snap
->data
[snap
->process_offset
];
417 *lppe
= pe
[snap
->process_pos
];
420 lppe
->cntUsage
= pe
[snap
->process_pos
].cntUsage
;
421 lppe
->th32ProcessID
= pe
[snap
->process_pos
].th32ProcessID
;
422 lppe
->th32DefaultHeapID
= pe
[snap
->process_pos
].th32DefaultHeapID
;
423 lppe
->th32ModuleID
= pe
[snap
->process_pos
].th32ModuleID
;
424 lppe
->cntThreads
= pe
[snap
->process_pos
].cntThreads
;
425 lppe
->th32ParentProcessID
= pe
[snap
->process_pos
].th32ParentProcessID
;
426 lppe
->pcPriClassBase
= pe
[snap
->process_pos
].pcPriClassBase
;
427 lppe
->dwFlags
= pe
[snap
->process_pos
].dwFlags
;
429 WideCharToMultiByte( CP_ACP
, 0, pe
[snap
->process_pos
].szExeFile
, -1,
430 (char*)lppe
->szExeFile
, sizeof(lppe
->szExeFile
),
436 else SetLastError( ERROR_NO_MORE_FILES
);
437 UnmapViewOfFile( snap
);
444 /***********************************************************************
445 * Process32First (KERNEL32.@)
447 * Return info about the first process in a toolhelp32 snapshot
449 BOOL WINAPI
Process32First(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
451 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, TRUE
, FALSE
/* ANSI */ );
454 /***********************************************************************
455 * Process32Next (KERNEL32.@)
457 * Return info about the "next" process in a toolhelp32 snapshot
459 BOOL WINAPI
Process32Next(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
461 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, FALSE
, FALSE
/* ANSI */ );
464 /***********************************************************************
465 * Process32FirstW (KERNEL32.@)
467 * Return info about the first process in a toolhelp32 snapshot
469 BOOL WINAPI
Process32FirstW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
471 return process_next( hSnapshot
, lppe
, TRUE
, TRUE
/* Unicode */ );
474 /***********************************************************************
475 * Process32NextW (KERNEL32.@)
477 * Return info about the "next" process in a toolhelp32 snapshot
479 BOOL WINAPI
Process32NextW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
481 return process_next( hSnapshot
, lppe
, FALSE
, TRUE
/* Unicode */ );
484 /***********************************************************************
487 * Implementation of Module32{First|Next}W
489 static BOOL
module_nextW( HANDLE hSnapShot
, LPMODULEENTRY32W lpme
, BOOL first
)
491 struct snapshot
* snap
;
494 if (lpme
->dwSize
< sizeof (MODULEENTRY32W
))
496 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
497 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
500 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
502 if (first
) snap
->module_pos
= 0;
503 if (snap
->module_pos
< snap
->module_count
)
505 LPMODULEENTRY32W pe
= (MODULEENTRY32W
*)&snap
->data
[snap
->module_offset
];
506 *lpme
= pe
[snap
->module_pos
++];
509 else SetLastError( ERROR_NO_MORE_FILES
);
510 UnmapViewOfFile( snap
);
516 /***********************************************************************
517 * Module32FirstW (KERNEL32.@)
519 * Return info about the "first" module in a toolhelp32 snapshot
521 BOOL WINAPI
Module32FirstW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
523 return module_nextW( hSnapshot
, lpme
, TRUE
);
526 /***********************************************************************
527 * Module32NextW (KERNEL32.@)
529 * Return info about the "next" module in a toolhelp32 snapshot
531 BOOL WINAPI
Module32NextW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
533 return module_nextW( hSnapshot
, lpme
, FALSE
);
536 /***********************************************************************
539 * Implementation of Module32{First|Next}A
541 static BOOL
module_nextA( HANDLE handle
, LPMODULEENTRY32 lpme
, BOOL first
)
546 if (lpme
->dwSize
< sizeof(MODULEENTRY32
))
548 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
549 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
553 mew
.dwSize
= sizeof(mew
);
554 if ((ret
= module_nextW( handle
, &mew
, first
)))
556 lpme
->th32ModuleID
= mew
.th32ModuleID
;
557 lpme
->th32ProcessID
= mew
.th32ProcessID
;
558 lpme
->GlblcntUsage
= mew
.GlblcntUsage
;
559 lpme
->ProccntUsage
= mew
.ProccntUsage
;
560 lpme
->modBaseAddr
= mew
.modBaseAddr
;
561 lpme
->modBaseSize
= mew
.modBaseSize
;
562 lpme
->hModule
= mew
.hModule
;
563 WideCharToMultiByte( CP_ACP
, 0, mew
.szModule
, -1, lpme
->szModule
, sizeof(lpme
->szModule
), NULL
, NULL
);
564 WideCharToMultiByte( CP_ACP
, 0, mew
.szExePath
, -1, lpme
->szExePath
, sizeof(lpme
->szExePath
), NULL
, NULL
);
569 /***********************************************************************
570 * Module32First (KERNEL32.@)
572 * Return info about the "first" module in a toolhelp32 snapshot
574 BOOL WINAPI
Module32First(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
576 return module_nextA( hSnapshot
, lpme
, TRUE
);
579 /***********************************************************************
580 * Module32Next (KERNEL32.@)
582 * Return info about the "next" module in a toolhelp32 snapshot
584 BOOL WINAPI
Module32Next(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
586 return module_nextA( hSnapshot
, lpme
, FALSE
);
589 /************************************************************************
590 * Heap32ListFirst (KERNEL32.@)
593 BOOL WINAPI
Heap32ListFirst(HANDLE hSnapshot
, LPHEAPLIST32 lphl
)
599 /******************************************************************
600 * Toolhelp32ReadProcessMemory (KERNEL32.@)
604 BOOL WINAPI
Toolhelp32ReadProcessMemory(DWORD pid
, const void* base
,
605 void* buf
, SIZE_T len
, SIZE_T
* r
)
610 h
= (pid
) ? OpenProcess(PROCESS_VM_READ
, FALSE
, pid
) : GetCurrentProcess();
613 ret
= ReadProcessMemory(h
, base
, buf
, len
, r
);
614 if (pid
) CloseHandle(h
);