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 "kernel_private.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(toolhelp
);
60 static WCHAR
*fetch_string( HANDLE hProcess
, UNICODE_STRING
* us
)
64 local
= HeapAlloc( GetProcessHeap(), 0, us
->Length
);
67 if (!ReadProcessMemory( hProcess
, us
->Buffer
, local
, us
->Length
, NULL
))
69 HeapFree( GetProcessHeap(), 0, local
);
77 static BOOL
fetch_module( DWORD process
, DWORD flags
, LDR_DATA_TABLE_ENTRY
**ldr_mod
, ULONG
*num
)
80 PROCESS_BASIC_INFORMATION pbi
;
81 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 if (set_ntstatus( NtQueryInformationProcess( hProcess
, ProcessBasicInformation
,
98 &pbi
, sizeof(pbi
), NULL
)))
100 if (ReadProcessMemory( hProcess
, &pbi
.PebBaseAddress
->LdrData
,
101 &pLdrData
, sizeof(pLdrData
), NULL
) &&
102 ReadProcessMemory( hProcess
,
103 &pLdrData
->InLoadOrderModuleList
.Flink
,
104 &curr
, sizeof(curr
), NULL
))
106 head
= &pLdrData
->InLoadOrderModuleList
;
111 *ldr_mod
= HeapAlloc( GetProcessHeap(), 0, sizeof(LDR_DATA_TABLE_ENTRY
) );
113 *ldr_mod
= HeapReAlloc( GetProcessHeap(), 0, *ldr_mod
,
114 (*num
+ 1) * sizeof(LDR_DATA_TABLE_ENTRY
) );
115 if (!*ldr_mod
) break;
116 if (!ReadProcessMemory( hProcess
,
117 CONTAINING_RECORD(curr
, LDR_DATA_TABLE_ENTRY
,
120 sizeof(LDR_DATA_TABLE_ENTRY
), NULL
))
122 curr
= (*ldr_mod
)[*num
].InLoadOrderLinks
.Flink
;
123 /* if we cannot fetch the strings, then just ignore this LDR_DATA_TABLE_ENTRY
124 * and continue loading the other ones in the list
126 if (!fetch_string( hProcess
, &(*ldr_mod
)[*num
].BaseDllName
)) continue;
127 if (fetch_string( hProcess
, &(*ldr_mod
)[*num
].FullDllName
))
130 HeapFree( GetProcessHeap(), 0, (*ldr_mod
)[*num
].BaseDllName
.Buffer
);
136 if (process
) CloseHandle( hProcess
);
140 static void fill_module( struct snapshot
* snap
, ULONG
* offset
, ULONG process
,
141 LDR_DATA_TABLE_ENTRY
* ldr_mod
, ULONG num
)
147 snap
->module_count
= num
;
148 snap
->module_pos
= 0;
150 snap
->module_offset
= *offset
;
152 mod
= (MODULEENTRY32W
*)&snap
->data
[*offset
];
154 for (i
= 0; i
< num
; i
++)
156 mod
->dwSize
= sizeof(MODULEENTRY32W
);
157 mod
->th32ModuleID
= 1; /* toolhelp internal id, never used */
158 mod
->th32ProcessID
= process
? process
: GetCurrentProcessId();
159 mod
->GlblcntUsage
= 0xFFFF; /* FIXME */
160 mod
->ProccntUsage
= 0xFFFF; /* FIXME */
161 mod
->modBaseAddr
= ldr_mod
[i
].DllBase
;
162 mod
->modBaseSize
= ldr_mod
[i
].SizeOfImage
;
163 mod
->hModule
= ldr_mod
[i
].DllBase
;
165 l
= min(ldr_mod
[i
].BaseDllName
.Length
, sizeof(mod
->szModule
) - sizeof(WCHAR
));
166 memcpy(mod
->szModule
, ldr_mod
[i
].BaseDllName
.Buffer
, l
);
167 mod
->szModule
[l
/ sizeof(WCHAR
)] = '\0';
168 l
= min(ldr_mod
[i
].FullDllName
.Length
, sizeof(mod
->szExePath
) - sizeof(WCHAR
));
169 memcpy(mod
->szExePath
, ldr_mod
[i
].FullDllName
.Buffer
, l
);
170 mod
->szExePath
[l
/ sizeof(WCHAR
)] = '\0';
175 *offset
+= num
* sizeof(MODULEENTRY32W
);
178 static BOOL
fetch_process_thread( DWORD flags
, SYSTEM_PROCESS_INFORMATION
** pspi
,
179 ULONG
* num_pcs
, ULONG
* num_thd
)
183 PSYSTEM_PROCESS_INFORMATION spi
;
185 *num_pcs
= *num_thd
= 0;
186 if (!(flags
& (TH32CS_SNAPPROCESS
| TH32CS_SNAPTHREAD
))) return TRUE
;
188 *pspi
= HeapAlloc( GetProcessHeap(), 0, size
= 4096 );
191 status
= NtQuerySystemInformation( SystemProcessInformation
, *pspi
,
196 *num_pcs
= *num_thd
= offset
= 0;
200 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ offset
);
201 if (flags
& TH32CS_SNAPPROCESS
) (*num_pcs
)++;
202 if (flags
& TH32CS_SNAPTHREAD
) *num_thd
+= spi
->dwThreadCount
;
203 } while ((offset
= spi
->NextEntryOffset
));
205 case STATUS_INFO_LENGTH_MISMATCH
:
206 *pspi
= HeapReAlloc( GetProcessHeap(), 0, *pspi
, size
*= 2 );
209 SetLastError( RtlNtStatusToDosError( status
) );
215 static void fill_process( struct snapshot
* snap
, ULONG
* offset
,
216 SYSTEM_PROCESS_INFORMATION
* spi
, ULONG num
)
218 PROCESSENTRY32W
* pcs_entry
;
222 snap
->process_count
= num
;
223 snap
->process_pos
= 0;
225 snap
->process_offset
= *offset
;
227 pcs_entry
= (PROCESSENTRY32W
*)&snap
->data
[*offset
];
231 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
233 pcs_entry
->dwSize
= sizeof(PROCESSENTRY32W
);
234 pcs_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
235 pcs_entry
->th32ProcessID
= HandleToUlong(spi
->UniqueProcessId
);
236 pcs_entry
->th32DefaultHeapID
= 0; /* MSDN says no longer used, always 0 */
237 pcs_entry
->th32ModuleID
= 0; /* MSDN says no longer used, always 0 */
238 pcs_entry
->cntThreads
= spi
->dwThreadCount
;
239 pcs_entry
->th32ParentProcessID
= HandleToUlong(spi
->ParentProcessId
);
240 pcs_entry
->pcPriClassBase
= spi
->dwBasePriority
;
241 pcs_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0 */
242 l
= min(spi
->ProcessName
.Length
, sizeof(pcs_entry
->szExeFile
) - sizeof(WCHAR
));
243 memcpy(pcs_entry
->szExeFile
, spi
->ProcessName
.Buffer
, l
);
244 pcs_entry
->szExeFile
[l
/ sizeof(WCHAR
)] = '\0';
246 } while ((poff
= spi
->NextEntryOffset
));
248 *offset
+= num
* sizeof(PROCESSENTRY32W
);
251 static void fill_thread( struct snapshot
* snap
, ULONG
* offset
, LPVOID info
, ULONG num
)
253 THREADENTRY32
* thd_entry
;
254 SYSTEM_PROCESS_INFORMATION
* spi
;
255 SYSTEM_THREAD_INFORMATION
* sti
;
258 snap
->thread_count
= num
;
259 snap
->thread_pos
= 0;
261 snap
->thread_offset
= *offset
;
263 thd_entry
= (THREADENTRY32
*)&snap
->data
[*offset
];
268 spi
= (SYSTEM_PROCESS_INFORMATION
*)((char*)spi
+ poff
);
271 for (i
= 0; i
< spi
->dwThreadCount
; i
++)
273 thd_entry
->dwSize
= sizeof(THREADENTRY32
);
274 thd_entry
->cntUsage
= 0; /* MSDN says no longer used, always 0 */
275 thd_entry
->th32ThreadID
= HandleToUlong(sti
->ClientId
.UniqueThread
);
276 thd_entry
->th32OwnerProcessID
= HandleToUlong(sti
->ClientId
.UniqueProcess
);
277 thd_entry
->tpBasePri
= sti
->dwBasePriority
;
278 thd_entry
->tpDeltaPri
= 0; /* MSDN says no longer used, always 0 */
279 thd_entry
->dwFlags
= 0; /* MSDN says no longer used, always 0" */
284 } while ((poff
= spi
->NextEntryOffset
));
285 *offset
+= num
* sizeof(THREADENTRY32
);
288 /***********************************************************************
289 * CreateToolhelp32Snapshot (KERNEL32.@)
291 HANDLE WINAPI
CreateToolhelp32Snapshot( DWORD flags
, DWORD process
)
293 SYSTEM_PROCESS_INFORMATION
* spi
= NULL
;
294 LDR_DATA_TABLE_ENTRY
*mod
= NULL
;
295 ULONG num_pcs
, num_thd
, num_mod
;
296 HANDLE hSnapShot
= 0;
298 TRACE("%x,%x\n", flags
, process
);
299 if (!(flags
& (TH32CS_SNAPPROCESS
|TH32CS_SNAPTHREAD
|TH32CS_SNAPMODULE
)))
301 FIXME("flags %x not implemented\n", flags
);
302 SetLastError( ERROR_CALL_NOT_IMPLEMENTED
);
303 return INVALID_HANDLE_VALUE
;
306 if (fetch_module( process
, flags
, &mod
, &num_mod
) &&
307 fetch_process_thread( flags
, &spi
, &num_pcs
, &num_thd
))
310 struct snapshot
*snap
;
311 SECURITY_ATTRIBUTES sa
;
313 /* create & fill the snapshot section */
314 sect_size
= sizeof(struct snapshot
) - 1; /* for last data[1] */
315 if (flags
& TH32CS_SNAPMODULE
) sect_size
+= num_mod
* sizeof(MODULEENTRY32W
);
316 if (flags
& TH32CS_SNAPPROCESS
) sect_size
+= num_pcs
* sizeof(PROCESSENTRY32W
);
317 if (flags
& TH32CS_SNAPTHREAD
) sect_size
+= num_thd
* sizeof(THREADENTRY32
);
318 if (flags
& TH32CS_SNAPHEAPLIST
)FIXME("Unimplemented: heap list snapshot\n");
320 sa
.bInheritHandle
= (flags
& TH32CS_INHERIT
) != 0;
321 sa
.lpSecurityDescriptor
= NULL
;
323 hSnapShot
= CreateFileMappingW( INVALID_HANDLE_VALUE
, &sa
,
324 SEC_COMMIT
| PAGE_READWRITE
,
325 0, sect_size
, NULL
);
326 if (hSnapShot
&& (snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
330 fill_module( snap
, &offset
, process
, mod
, num_mod
);
331 fill_process( snap
, &offset
, spi
, num_pcs
);
332 fill_thread( snap
, &offset
, spi
, num_thd
);
333 UnmapViewOfFile( snap
);
339 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].BaseDllName
.Buffer
);
340 HeapFree( GetProcessHeap(), 0, mod
[num_mod
].FullDllName
.Buffer
);
342 HeapFree( GetProcessHeap(), 0, mod
);
343 HeapFree( GetProcessHeap(), 0, spi
);
345 if (!hSnapShot
) return INVALID_HANDLE_VALUE
;
349 static BOOL
next_thread( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
, BOOL first
)
351 struct snapshot
* snap
;
354 if (lpte
->dwSize
< sizeof(THREADENTRY32
))
356 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
357 WARN("Result buffer too small (%d)\n", lpte
->dwSize
);
360 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
362 if (first
) snap
->thread_pos
= 0;
363 if (snap
->thread_pos
< snap
->thread_count
)
365 LPTHREADENTRY32 te
= (THREADENTRY32
*)&snap
->data
[snap
->thread_offset
];
366 *lpte
= te
[snap
->thread_pos
++];
369 else SetLastError( ERROR_NO_MORE_FILES
);
370 UnmapViewOfFile( snap
);
375 /***********************************************************************
376 * Thread32First (KERNEL32.@)
378 * Return info about the first thread in a toolhelp32 snapshot
380 BOOL WINAPI
Thread32First( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
382 return next_thread( hSnapShot
, lpte
, TRUE
);
385 /***********************************************************************
386 * Thread32Next (KERNEL32.@)
388 * Return info about the first thread in a toolhelp32 snapshot
390 BOOL WINAPI
Thread32Next( HANDLE hSnapShot
, LPTHREADENTRY32 lpte
)
392 return next_thread( hSnapShot
, lpte
, FALSE
);
395 /***********************************************************************
398 * Implementation of Process32First/Next. Note that the ANSI / Unicode
399 * version check is a bit of a hack as it relies on the fact that only
400 * the last field is actually different.
402 static BOOL
process_next( HANDLE hSnapShot
, LPPROCESSENTRY32W lppe
,
403 BOOL first
, BOOL unicode
)
405 struct snapshot
* snap
;
407 DWORD sz
= unicode
? sizeof(PROCESSENTRY32W
) : sizeof(PROCESSENTRY32
);
409 if (lppe
->dwSize
< sz
)
411 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
412 WARN("Result buffer too small (%d)\n", lppe
->dwSize
);
415 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
417 if (first
) snap
->process_pos
= 0;
418 if (snap
->process_pos
< snap
->process_count
)
420 LPPROCESSENTRY32W pe
= (PROCESSENTRY32W
*)&snap
->data
[snap
->process_offset
];
422 *lppe
= pe
[snap
->process_pos
];
425 lppe
->cntUsage
= pe
[snap
->process_pos
].cntUsage
;
426 lppe
->th32ProcessID
= pe
[snap
->process_pos
].th32ProcessID
;
427 lppe
->th32DefaultHeapID
= pe
[snap
->process_pos
].th32DefaultHeapID
;
428 lppe
->th32ModuleID
= pe
[snap
->process_pos
].th32ModuleID
;
429 lppe
->cntThreads
= pe
[snap
->process_pos
].cntThreads
;
430 lppe
->th32ParentProcessID
= pe
[snap
->process_pos
].th32ParentProcessID
;
431 lppe
->pcPriClassBase
= pe
[snap
->process_pos
].pcPriClassBase
;
432 lppe
->dwFlags
= pe
[snap
->process_pos
].dwFlags
;
434 WideCharToMultiByte( CP_ACP
, 0, pe
[snap
->process_pos
].szExeFile
, -1,
435 (char*)lppe
->szExeFile
, sizeof(lppe
->szExeFile
),
441 else SetLastError( ERROR_NO_MORE_FILES
);
442 UnmapViewOfFile( snap
);
449 /***********************************************************************
450 * Process32First (KERNEL32.@)
452 * Return info about the first process in a toolhelp32 snapshot
454 BOOL WINAPI
Process32First(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
456 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, TRUE
, FALSE
/* ANSI */ );
459 /***********************************************************************
460 * Process32Next (KERNEL32.@)
462 * Return info about the "next" process in a toolhelp32 snapshot
464 BOOL WINAPI
Process32Next(HANDLE hSnapshot
, LPPROCESSENTRY32 lppe
)
466 return process_next( hSnapshot
, (PROCESSENTRY32W
*)lppe
, FALSE
, FALSE
/* ANSI */ );
469 /***********************************************************************
470 * Process32FirstW (KERNEL32.@)
472 * Return info about the first process in a toolhelp32 snapshot
474 BOOL WINAPI
Process32FirstW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
476 return process_next( hSnapshot
, lppe
, TRUE
, TRUE
/* Unicode */ );
479 /***********************************************************************
480 * Process32NextW (KERNEL32.@)
482 * Return info about the "next" process in a toolhelp32 snapshot
484 BOOL WINAPI
Process32NextW(HANDLE hSnapshot
, LPPROCESSENTRY32W lppe
)
486 return process_next( hSnapshot
, lppe
, FALSE
, TRUE
/* Unicode */ );
489 /***********************************************************************
492 * Implementation of Module32{First|Next}W
494 static BOOL
module_nextW( HANDLE hSnapShot
, LPMODULEENTRY32W lpme
, BOOL first
)
496 struct snapshot
* snap
;
499 if (lpme
->dwSize
< sizeof (MODULEENTRY32W
))
501 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
502 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
505 if ((snap
= MapViewOfFile( hSnapShot
, FILE_MAP_ALL_ACCESS
, 0, 0, 0 )))
507 if (first
) snap
->module_pos
= 0;
508 if (snap
->module_pos
< snap
->module_count
)
510 LPMODULEENTRY32W pe
= (MODULEENTRY32W
*)&snap
->data
[snap
->module_offset
];
511 *lpme
= pe
[snap
->module_pos
++];
514 else SetLastError( ERROR_NO_MORE_FILES
);
515 UnmapViewOfFile( snap
);
521 /***********************************************************************
522 * Module32FirstW (KERNEL32.@)
524 * Return info about the "first" module in a toolhelp32 snapshot
526 BOOL WINAPI
Module32FirstW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
528 return module_nextW( hSnapshot
, lpme
, TRUE
);
531 /***********************************************************************
532 * Module32NextW (KERNEL32.@)
534 * Return info about the "next" module in a toolhelp32 snapshot
536 BOOL WINAPI
Module32NextW(HANDLE hSnapshot
, LPMODULEENTRY32W lpme
)
538 return module_nextW( hSnapshot
, lpme
, FALSE
);
541 /***********************************************************************
544 * Implementation of Module32{First|Next}A
546 static BOOL
module_nextA( HANDLE handle
, LPMODULEENTRY32 lpme
, BOOL first
)
551 if (lpme
->dwSize
< sizeof(MODULEENTRY32
))
553 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
554 WARN("Result buffer too small (was: %d)\n", lpme
->dwSize
);
558 mew
.dwSize
= sizeof(mew
);
559 if ((ret
= module_nextW( handle
, &mew
, first
)))
561 lpme
->th32ModuleID
= mew
.th32ModuleID
;
562 lpme
->th32ProcessID
= mew
.th32ProcessID
;
563 lpme
->GlblcntUsage
= mew
.GlblcntUsage
;
564 lpme
->ProccntUsage
= mew
.ProccntUsage
;
565 lpme
->modBaseAddr
= mew
.modBaseAddr
;
566 lpme
->modBaseSize
= mew
.modBaseSize
;
567 lpme
->hModule
= mew
.hModule
;
568 WideCharToMultiByte( CP_ACP
, 0, mew
.szModule
, -1, lpme
->szModule
, sizeof(lpme
->szModule
), NULL
, NULL
);
569 WideCharToMultiByte( CP_ACP
, 0, mew
.szExePath
, -1, lpme
->szExePath
, sizeof(lpme
->szExePath
), NULL
, NULL
);
574 /***********************************************************************
575 * Module32First (KERNEL32.@)
577 * Return info about the "first" module in a toolhelp32 snapshot
579 BOOL WINAPI
Module32First(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
581 return module_nextA( hSnapshot
, lpme
, TRUE
);
584 /***********************************************************************
585 * Module32Next (KERNEL32.@)
587 * Return info about the "next" module in a toolhelp32 snapshot
589 BOOL WINAPI
Module32Next(HANDLE hSnapshot
, LPMODULEENTRY32 lpme
)
591 return module_nextA( hSnapshot
, lpme
, FALSE
);
594 /************************************************************************
595 * Heap32ListFirst (KERNEL32.@)
598 BOOL WINAPI
Heap32ListFirst(HANDLE hSnapshot
, LPHEAPLIST32 lphl
)
604 /******************************************************************
605 * Toolhelp32ReadProcessMemory (KERNEL32.@)
609 BOOL WINAPI
Toolhelp32ReadProcessMemory(DWORD pid
, const void* base
,
610 void* buf
, SIZE_T len
, SIZE_T
* r
)
615 h
= (pid
) ? OpenProcess(PROCESS_VM_READ
, FALSE
, pid
) : GetCurrentProcess();
618 ret
= ReadProcessMemory(h
, base
, buf
, len
, r
);
619 if (pid
) CloseHandle(h
);