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
33 #define WIN32_NO_STATUS
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(toolhelp
);
59 static WCHAR
*fetch_string( HANDLE hProcess
, UNICODE_STRING
* us
)
63 local
= HeapAlloc( GetProcessHeap(), 0, us
->Length
);
66 if (!ReadProcessMemory( hProcess
, us
->Buffer
, local
, us
->Length
, NULL
))
68 HeapFree( GetProcessHeap(), 0, local
);
76 static BOOL
fetch_module( DWORD process
, DWORD flags
, LDR_MODULE
** ldr_mod
, ULONG
* num
)
79 PROCESS_BASIC_INFORMATION pbi
;
80 PPEB_LDR_DATA pLdrData
;
82 PLIST_ENTRY head
, curr
;
87 if (!(flags
& TH32CS_SNAPMODULE
)) return TRUE
;
91 hProcess
= OpenProcess( PROCESS_VM_READ
| PROCESS_QUERY_INFORMATION
, FALSE
, process
);
92 if (!hProcess
) return FALSE
;
95 hProcess
= GetCurrentProcess();
97 status
= NtQueryInformationProcess( hProcess
, ProcessBasicInformation
,
98 &pbi
, sizeof(pbi
), NULL
);
101 if (ReadProcessMemory( hProcess
, &pbi
.PebBaseAddress
->LdrData
,
102 &pLdrData
, sizeof(pLdrData
), NULL
) &&
103 ReadProcessMemory( hProcess
,
104 &pLdrData
->InLoadOrderModuleList
.Flink
,
105 &curr
, sizeof(curr
), NULL
))
107 head
= &pLdrData
->InLoadOrderModuleList
;
112 *ldr_mod
= HeapAlloc( GetProcessHeap(), 0, sizeof(LDR_MODULE
) );
114 *ldr_mod
= HeapReAlloc( GetProcessHeap(), 0, *ldr_mod
,
115 (*num
+ 1) * sizeof(LDR_MODULE
) );
116 if (!*ldr_mod
) break;
117 if (!ReadProcessMemory( hProcess
,
118 CONTAINING_RECORD(curr
, LDR_MODULE
,
119 InLoadOrderModuleList
),
121 sizeof(LDR_MODULE
), NULL
))
123 curr
= (*ldr_mod
)[*num
].InLoadOrderModuleList
.Flink
;
124 /* if we cannot fetch the strings, then just ignore this LDR_MODULE
125 * and continue loading the other ones in the list
127 if (!fetch_string( hProcess
, &(*ldr_mod
)[*num
].BaseDllName
)) continue;
128 if (fetch_string( hProcess
, &(*ldr_mod
)[*num
].FullDllName
))
131 HeapFree( GetProcessHeap(), 0, (*ldr_mod
)[*num
].BaseDllName
.Buffer
);
136 else SetLastError( RtlNtStatusToDosError( status
) );
138 if (process
) CloseHandle( hProcess
);
142 static void fill_module( struct snapshot
* snap
, ULONG
* offset
, ULONG process
,
143 LDR_MODULE
* ldr_mod
, ULONG num
)
149 snap
->module_count
= num
;
150 snap
->module_pos
= 0;
152 snap
->module_offset
= *offset
;
154 mod
= (MODULEENTRY32W
*)&snap
->data
[*offset
];
156 for (i
= 0; i
< num
; i
++)
158 mod
->dwSize
= sizeof(MODULEENTRY32W
);
159 mod
->th32ModuleID
= 1; /* toolhelp internal id, never used */
160 mod
->th32ProcessID
= process
? process
: GetCurrentProcessId();
161 mod
->GlblcntUsage
= 0xFFFF; /* FIXME */
162 mod
->ProccntUsage
= 0xFFFF; /* FIXME */
163 mod
->modBaseAddr
= ldr_mod
[i
].BaseAddress
;
164 mod
->modBaseSize
= ldr_mod
[i
].SizeOfImage
;
165 mod
->hModule
= ldr_mod
[i
].BaseAddress
;
167 l
= min(ldr_mod
[i
].BaseDllName
.Length
, sizeof(mod
->szModule
) - sizeof(WCHAR
));
168 memcpy(mod
->szModule
, ldr_mod
[i
].BaseDllName
.Buffer
, l
);
169 mod
->szModule
[l
/ sizeof(WCHAR
)] = '\0';
170 l
= min(ldr_mod
[i
].FullDllName
.Length
, sizeof(mod
->szExePath
) - sizeof(WCHAR
));
171 memcpy(mod
->szExePath
, ldr_mod
[i
].FullDllName
.Buffer
, l
);
172 mod
->szExePath
[l
/ sizeof(WCHAR
)] = '\0';
177 *offset
+= num
* sizeof(MODULEENTRY32W
);
180 static BOOL
fetch_process_thread( DWORD flags
, SYSTEM_PROCESS_INFORMATION
** pspi
,
181 ULONG
* num_pcs
, ULONG
* num_thd
)
185 PSYSTEM_PROCESS_INFORMATION spi
;
187 *num_pcs
= *num_thd
= 0;
188 if (!(flags
& (TH32CS_SNAPPROCESS
| TH32CS_SNAPTHREAD
))) return TRUE
;
190 *pspi
= HeapAlloc( GetProcessHeap(), 0, size
= 4096 );
193 status
= NtQuerySystemInformation( SystemProcessInformation
, *pspi
,
198 *num_pcs
= *num_thd
= offset
= 0;
202 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ offset
);
203 if (flags
& TH32CS_SNAPPROCESS
) (*num_pcs
)++;
204 if (flags
& TH32CS_SNAPTHREAD
) *num_thd
+= spi
->dwThreadCount
;
205 } while ((offset
= spi
->NextEntryOffset
));
207 case STATUS_INFO_LENGTH_MISMATCH
:
208 *pspi
= HeapReAlloc( GetProcessHeap(), 0, *pspi
, size
*= 2 );
211 SetLastError( RtlNtStatusToDosError( status
) );
217 static void fill_process( struct snapshot
* snap
, ULONG
* offset
,
218 SYSTEM_PROCESS_INFORMATION
* spi
, ULONG num
)
220 PROCESSENTRY32W
* pcs_entry
;
224 snap
->process_count
= num
;
225 snap
->process_pos
= 0;
227 snap
->process_offset
= *offset
;
229 pcs_entry
= (PROCESSENTRY32W
*)&snap
->data
[*offset
];
233 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
235 pcs_entry
->dwSize
= sizeof(PROCESSENTRY32W
);
236 pcs_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
237 pcs_entry
->th32ProcessID
= HandleToUlong(spi
->UniqueProcessId
);
238 pcs_entry
->th32DefaultHeapID
= 0; /* MSDN says no longer used, always 0 */
239 pcs_entry
->th32ModuleID
= 0; /* MSDN says no longer used, always 0 */
240 pcs_entry
->cntThreads
= spi
->dwThreadCount
;
241 pcs_entry
->th32ParentProcessID
= HandleToUlong(spi
->ParentProcessId
);
242 pcs_entry
->pcPriClassBase
= spi
->dwBasePriority
;
243 pcs_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0 */
244 l
= min(spi
->ProcessName
.Length
, sizeof(pcs_entry
->szExeFile
) - sizeof(WCHAR
));
245 memcpy(pcs_entry
->szExeFile
, spi
->ProcessName
.Buffer
, l
);
246 pcs_entry
->szExeFile
[l
/ sizeof(WCHAR
)] = '\0';
248 } while ((poff
= spi
->NextEntryOffset
));
250 *offset
+= num
* sizeof(PROCESSENTRY32W
);
253 static void fill_thread( struct snapshot
* snap
, ULONG
* offset
, LPVOID info
, ULONG num
)
255 THREADENTRY32
* thd_entry
;
256 SYSTEM_PROCESS_INFORMATION
* spi
;
257 SYSTEM_THREAD_INFORMATION
* sti
;
260 snap
->thread_count
= num
;
261 snap
->thread_pos
= 0;
263 snap
->thread_offset
= *offset
;
265 thd_entry
= (THREADENTRY32
*)&snap
->data
[*offset
];
270 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
273 for (i
= 0; i
< spi
->dwThreadCount
; i
++)
275 thd_entry
->dwSize
= sizeof(THREADENTRY32
);
276 thd_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
277 thd_entry
->th32ThreadID
= HandleToUlong(sti
->ClientId
.UniqueThread
);
278 thd_entry
->th32OwnerProcessID
= HandleToUlong(sti
->ClientId
.UniqueProcess
);
279 thd_entry
->tpBasePri
= sti
->dwBasePriority
;
280 thd_entry
->tpDeltaPri
= 0; /* MSDN says no longer used, always 0 */
281 thd_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0" */
286 } while ((poff
= spi
->NextEntryOffset
));
287 *offset
+= num
* sizeof(THREADENTRY32
);
290 /***********************************************************************
291 * CreateToolhelp32Snapshot (KERNEL32.@)
293 HANDLE WINAPI
CreateToolhelp32Snapshot( DWORD flags
, DWORD process
)
295 SYSTEM_PROCESS_INFORMATION
* spi
= NULL
;
296 LDR_MODULE
* mod
= NULL
;
297 ULONG num_pcs
, num_thd
, num_mod
;
298 HANDLE hSnapShot
= 0;
300 TRACE("%x,%x\n", flags
, process
);
301 if (!(flags
& (TH32CS_SNAPPROCESS
|TH32CS_SNAPTHREAD
|TH32CS_SNAPMODULE
)))
303 FIXME("flags %x not implemented\n", flags
);
304 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
305 return INVALID_HANDLE_VALUE
;
308 if (fetch_module( process
, flags
, &mod
, &num_mod
) &&
309 fetch_process_thread( flags
, &spi
, &num_pcs
, &num_thd
))
312 struct snapshot
*snap
;
313 SECURITY_ATTRIBUTES sa
;
315 /* create & fill the snapshot section */
316 sect_size
= sizeof(struct snapshot
) - 1; /* for last data[1] */
317 if (flags
& TH32CS_SNAPMODULE
) sect_size
+= num_mod
* sizeof(MODULEENTRY32W
);
318 if (flags
& TH32CS_SNAPPROCESS
) sect_size
+= num_pcs
* sizeof(PROCESSENTRY32W
);
319 if (flags
& TH32CS_SNAPTHREAD
) sect_size
+= num_thd
* sizeof(THREADENTRY32
);
320 if (flags
& TH32CS_SNAPHEAPLIST
)FIXME("Unimplemented: heap list snapshot\n");
322 sa
.bInheritHandle
= (flags
& TH32CS_INHERIT
) ? TRUE
: FALSE
;
323 sa
.lpSecurityDescriptor
= NULL
;
325 hSnapShot
= CreateFileMappingW( INVALID_HANDLE_VALUE
, &sa
,
326 SEC_COMMIT
| PAGE_READWRITE
,
327 0, sect_size
, NULL
);
328 if (hSnapShot
&& (snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
332 fill_module( snap
, &offset
, process
, mod
, num_mod
);
333 fill_process( snap
, &offset
, spi
, num_pcs
);
334 fill_thread( snap
, &offset
, spi
, num_thd
);
335 UnmapViewOfFile( snap
);
341 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].BaseDllName
.Buffer
);
342 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].FullDllName
.Buffer
);
344 HeapFree( GetProcessHeap(), 0, mod
);
345 HeapFree( GetProcessHeap(), 0, spi
);
347 if (!hSnapShot
) return INVALID_HANDLE_VALUE
;
351 static BOOL
next_thread( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
, BOOL first
)
353 struct snapshot
* snap
;
356 if (lpte
->dwSize
< sizeof(THREADENTRY32
))
358 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
359 WARN("Result buffer too small (%d)\n", lpte
->dwSize
);
362 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
364 if (first
) snap
->thread_pos
= 0;
365 if (snap
->thread_pos
< snap
->thread_count
)
367 LPTHREADENTRY32 te
= (THREADENTRY32
*)&snap
->data
[snap
->thread_offset
];
368 *lpte
= te
[snap
->thread_pos
++];
371 else SetLastError( ERROR_NO_MORE_FILES
);
372 UnmapViewOfFile( snap
);
377 /***********************************************************************
378 * Thread32First (KERNEL32.@)
380 * Return info about the first thread in a toolhelp32 snapshot
382 BOOL WINAPI
Thread32First( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
384 return next_thread( hSnapShot
, lpte
, TRUE
);
387 /***********************************************************************
388 * Thread32Next (KERNEL32.@)
390 * Return info about the first thread in a toolhelp32 snapshot
392 BOOL WINAPI
Thread32Next( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
394 return next_thread( hSnapShot
, lpte
, FALSE
);
397 /***********************************************************************
400 * Implementation of Process32First/Next. Note that the ANSI / Unicode
401 * version check is a bit of a hack as it relies on the fact that only
402 * the last field is actually different.
404 static BOOL
process_next( HANDLE hSnapShot
, LPPROCESSENTRY32W lppe
,
405 BOOL first
, BOOL unicode
)
407 struct snapshot
* snap
;
409 DWORD sz
= unicode
? sizeof(PROCESSENTRY32W
) : sizeof(PROCESSENTRY32
);
411 if (lppe
->dwSize
< sz
)
413 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
414 WARN("Result buffer too small (%d)\n", lppe
->dwSize
);
417 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
419 if (first
) snap
->process_pos
= 0;
420 if (snap
->process_pos
< snap
->process_count
)
422 LPPROCESSENTRY32W pe
= (PROCESSENTRY32W
*)&snap
->data
[snap
->process_offset
];
424 *lppe
= pe
[snap
->process_pos
];
427 lppe
->cntUsage
= pe
[snap
->process_pos
].cntUsage
;
428 lppe
->th32ProcessID
= pe
[snap
->process_pos
].th32ProcessID
;
429 lppe
->th32DefaultHeapID
= pe
[snap
->process_pos
].th32DefaultHeapID
;
430 lppe
->th32ModuleID
= pe
[snap
->process_pos
].th32ModuleID
;
431 lppe
->cntThreads
= pe
[snap
->process_pos
].cntThreads
;
432 lppe
->th32ParentProcessID
= pe
[snap
->process_pos
].th32ParentProcessID
;
433 lppe
->pcPriClassBase
= pe
[snap
->process_pos
].pcPriClassBase
;
434 lppe
->dwFlags
= pe
[snap
->process_pos
].dwFlags
;
436 WideCharToMultiByte( CP_ACP
, 0, pe
[snap
->process_pos
].szExeFile
, -1,
437 (char*)lppe
->szExeFile
, sizeof(lppe
->szExeFile
),
443 else SetLastError( ERROR_NO_MORE_FILES
);
444 UnmapViewOfFile( snap
);
451 /***********************************************************************
452 * Process32First (KERNEL32.@)
454 * Return info about the first process in a toolhelp32 snapshot
456 BOOL WINAPI
Process32First(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
458 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, TRUE
, FALSE
/* ANSI */ );
461 /***********************************************************************
462 * Process32Next (KERNEL32.@)
464 * Return info about the "next" process in a toolhelp32 snapshot
466 BOOL WINAPI
Process32Next(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
468 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, FALSE
, FALSE
/* ANSI */ );
471 /***********************************************************************
472 * Process32FirstW (KERNEL32.@)
474 * Return info about the first process in a toolhelp32 snapshot
476 BOOL WINAPI
Process32FirstW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
478 return process_next( hSnapshot
, lppe
, TRUE
, TRUE
/* Unicode */ );
481 /***********************************************************************
482 * Process32NextW (KERNEL32.@)
484 * Return info about the "next" process in a toolhelp32 snapshot
486 BOOL WINAPI
Process32NextW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
488 return process_next( hSnapshot
, lppe
, FALSE
, TRUE
/* Unicode */ );
491 /***********************************************************************
494 * Implementation of Module32{First|Next}W
496 static BOOL
module_nextW( HANDLE hSnapShot
, LPMODULEENTRY32W lpme
, BOOL first
)
498 struct snapshot
* snap
;
501 if (lpme
->dwSize
< sizeof (MODULEENTRY32W
))
503 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
504 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
507 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
509 if (first
) snap
->module_pos
= 0;
510 if (snap
->module_pos
< snap
->module_count
)
512 LPMODULEENTRY32W pe
= (MODULEENTRY32W
*)&snap
->data
[snap
->module_offset
];
513 *lpme
= pe
[snap
->module_pos
++];
516 else SetLastError( ERROR_NO_MORE_FILES
);
517 UnmapViewOfFile( snap
);
523 /***********************************************************************
524 * Module32FirstW (KERNEL32.@)
526 * Return info about the "first" module in a toolhelp32 snapshot
528 BOOL WINAPI
Module32FirstW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
530 return module_nextW( hSnapshot
, lpme
, TRUE
);
533 /***********************************************************************
534 * Module32NextW (KERNEL32.@)
536 * Return info about the "next" module in a toolhelp32 snapshot
538 BOOL WINAPI
Module32NextW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
540 return module_nextW( hSnapshot
, lpme
, FALSE
);
543 /***********************************************************************
546 * Implementation of Module32{First|Next}A
548 static BOOL
module_nextA( HANDLE handle
, LPMODULEENTRY32 lpme
, BOOL first
)
553 if (lpme
->dwSize
< sizeof(MODULEENTRY32
))
555 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
556 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
560 mew
.dwSize
= sizeof(mew
);
561 if ((ret
= module_nextW( handle
, &mew
, first
)))
563 lpme
->th32ModuleID
= mew
.th32ModuleID
;
564 lpme
->th32ProcessID
= mew
.th32ProcessID
;
565 lpme
->GlblcntUsage
= mew
.GlblcntUsage
;
566 lpme
->ProccntUsage
= mew
.ProccntUsage
;
567 lpme
->modBaseAddr
= mew
.modBaseAddr
;
568 lpme
->modBaseSize
= mew
.modBaseSize
;
569 lpme
->hModule
= mew
.hModule
;
570 WideCharToMultiByte( CP_ACP
, 0, mew
.szModule
, -1, lpme
->szModule
, sizeof(lpme
->szModule
), NULL
, NULL
);
571 WideCharToMultiByte( CP_ACP
, 0, mew
.szExePath
, -1, lpme
->szExePath
, sizeof(lpme
->szExePath
), NULL
, NULL
);
576 /***********************************************************************
577 * Module32First (KERNEL32.@)
579 * Return info about the "first" module in a toolhelp32 snapshot
581 BOOL WINAPI
Module32First(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
583 return module_nextA( hSnapshot
, lpme
, TRUE
);
586 /***********************************************************************
587 * Module32Next (KERNEL32.@)
589 * Return info about the "next" module in a toolhelp32 snapshot
591 BOOL WINAPI
Module32Next(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
593 return module_nextA( hSnapshot
, lpme
, FALSE
);
596 /************************************************************************
597 * Heap32ListFirst (KERNEL32.@)
600 BOOL WINAPI
Heap32ListFirst(HANDLE hSnapshot
, LPHEAPLIST32 lphl
)
606 /******************************************************************
607 * Toolhelp32ReadProcessMemory (KERNEL32.@)
611 BOOL WINAPI
Toolhelp32ReadProcessMemory(DWORD pid
, const void* base
,
612 void* buf
, SIZE_T len
, SIZE_T
* r
)
617 h
= (pid
) ? OpenProcess(PROCESS_VM_READ
, FALSE
, pid
) : GetCurrentProcess();
620 ret
= ReadProcessMemory(h
, base
, buf
, len
, r
);
621 if (pid
) CloseHandle(h
);