Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"CreateToolhelp32Snapshot" problem

129 views
Skip to first unread message

Larry

unread,
Jan 6, 2006, 1:50:04 PM1/6/06
to
Hi there,

I have a function that enumerates all processes on the system and for each
process encountered, it then enumerates each process' modules using the
following function call:

HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessID)

This function fails however when I encounter the process ID for the "System"
process. "GetLastError()" then returns ERROR_INVALID_HANDLE (6) on Win2000
Pro SP4, ERROR_NOT_ENOUGH_MEMORY (8) on WinXP SP2, and ERROR_PARTIAL_COPY
(299) on Win2003 Server SP1. How do I therefore avoid this? I can bypass the
"System" process entirely but how do I officially identify it. I just can't
compare the process name with "System" presumably since it may differ on
different language versions of Windows (or can I). What is the "System"
process anyway and are there any others I need to know about (besides the
"System Idle Process" - can you confirm the process ID for this is always 0
BTW). Thanks.

Ivan Brugiolo [MSFT]

unread,
Jan 6, 2006, 2:17:27 PM1/6/06
to
In the current implementations, you can skip the process IDs of `0` and `4`

--
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"Larry" <La...@nospam.nospam> wrote in message
news:E9F97499-DE20-44E2...@microsoft.com...

Larry

unread,
Jan 6, 2006, 2:31:04 PM1/6/06
to
Thanks for the quick response. I did notice that PID 4 was in use for the
"System" process on WinXP SP2 and Win2003 SP1 but on Win2000 Pro SP4 it's 8.
Is the latter value hardcoded as well? Moreover, I assume this is stable
across all versions of Windows regardless of language (Win2000 SP4 and later
that is). We have a large project used on different versions world-wide and
stability is very important. Please advise (and again, I assume there are no
other special PIDs I need to worry about). Thanks.

Arkady Frenkel

unread,
Jan 7, 2006, 11:04:22 AM1/7/06
to
System has value of 2 in original W2K e.g.
Arkady

"Larry" <La...@nospam.nospam> wrote in message

news:F5E038BF-701F-4D3B...@microsoft.com...

Skywing

unread,
Jan 7, 2006, 11:19:36 AM1/7/06
to
I would recommend just dealing with the error in a graceful fashion. There
are likely to be other situations besides the system process where you are
going to be unable to open a process, e.g. don't have sufficient security
rights to do it.

"Larry" <La...@nospam.nospam> wrote in message

news:F5E038BF-701F-4D3B...@microsoft.com...

Larry

unread,
Jan 7, 2006, 12:47:01 PM1/7/06
to
It's showing as 8 in the latest version (SP4). If MSFT could clarify the
situation for all versions mentioned below (regardless of SP or language) it
would be appreciated :)

Larry

Larry

unread,
Jan 7, 2006, 1:46:02 PM1/7/06
to
> I would recommend just dealing with the error in a graceful fashion. There
> are likely to be other situations besides the system process where you are
> going to be unable to open a process, e.g. don't have sufficient security
> rights to do it.

Thanks for the feedback. Security isn't an issue however since that's been
taken care of (we enable the SE_DEBUG_NAME privilege when available and
properly handle the situation otherwise). As for gracefully handling other
errors, that's what we're trying to do but we lack sufficient information.
That is, if "CreateToolhelp32Snapshot()" is always going to fail when dealing
with the "System" process then we need to bypass that process but don't know
how to officially recognize it. This would apply to any other esoteric
(system-level) process that's going to fail as well (for reasons that aren't
readily explained anywhere). Otherwise, if we didn't bypass this process,
then what do we do when we encounter it. "CreateToolhelp32Snapshot()" is
always going to fail but now we would need to distinguish between a real
error (one that requires us to back out of what we're doing) versus this
"pseudo-error" (where we can just move onto the next process). We can't
distinguish between the two errors however without knowing how to identify
the "System" process when we encounter it (and others like it if any).

Skywing

unread,
Jan 7, 2006, 2:35:18 PM1/7/06
to
One thing you might try is trying to open the process yourself and querying
information about it.

You can't open a process with a pid of 0, so you can safely always exclude
that process (the idle process).
With administrative rights or the debug privilege enabled, you will still be
able to open other system processes (such as the "main" system process)
using OpenProcess. However, you can't do much to that process as the user
mode data structures that things like
CreateToolhelp32Snapshot(...TH32CS_SNAPMODULE...) expect to read out of the
process's address space aren't there. This is why creating a loaded module
snapshot won't work for a system process and will fail with strange errors.

One thing you can do however is use NtQueryInformationProcess for
ProcessBasicInformation and inspect the returned PEB address - a system
process will always have a null PEB pointer (indeed, as I recall, this is
equivalent to the same test the kernel uses internally to determine if a
process is a system process or not).

Some caveats: This only applies to NT-based systems, and it's only partially
documented. It is going to be more reliable than hardcoding process ids
though, especially since there are drivers out there which create their own
system (kernel only) processes. You are also going to either require
administrative rights / SeDebugPrivilege in order to open a system process
for any access with the default security descriptor.

"Larry" <La...@nospam.nospam> wrote in message

news:4EDA7988-2EF3-449C...@microsoft.com...

Larry

unread,
Jan 7, 2006, 5:09:01 PM1/7/06
to
> One thing you might try is trying to open the process yourself and querying
> information about it.
>
> You can't open a process with a pid of 0, so you can safely always exclude
> that process (the idle process).
> With administrative rights or the debug privilege enabled, you will still be
> able to open other system processes (such as the "main" system process)
> using OpenProcess. However, you can't do much to that process as the user
> mode data structures that things like
> CreateToolhelp32Snapshot(...TH32CS_SNAPMODULE...) expect to read out of the
> process's address space aren't there. This is why creating a loaded module
> snapshot won't work for a system process and will fail with strange errors.
>
> One thing you can do however is use NtQueryInformationProcess for
> ProcessBasicInformation and inspect the returned PEB address - a system
> process will always have a null PEB pointer (indeed, as I recall, this is
> equivalent to the same test the kernel uses internally to determine if a
> process is a system process or not).
>
> Some caveats: This only applies to NT-based systems, and it's only partially
> documented. It is going to be more reliable than hardcoding process ids
> though, especially since there are drivers out there which create their own
> system (kernel only) processes. You are also going to either require
> administrative rights / SeDebugPrivilege in order to open a system process
> for any access with the default security descriptor.

Thanks for the info. I agree that checking for specific PIDs is hardly a
model approach and doesn't seem reliable unless MSFT says otherwise (still
waiting for that). We can't depend on "NtQueryInformationProcess()" however
due to the caveat seen at the top of the documenation. Even if we could I
would need MSFT to confirm what you said (appreciate the tip though). This
entire issue gets into uncharted waters and there seems to be no official
documentation I can find on the subject. It's a common enough scenario
however if you need to enumerate all modules on the system (in our case we're
trying to determine if a given DLL is loaded by any arbitrary process - we
pass the fully-qualified path in and then go hunting for it). We need to
handle all legitimate errors along the way without guesswork however and this
particular situation is making that difficult. Thanks again for your input.

Pavel Lebedinsky [MSFT]

unread,
Jan 8, 2006, 12:27:00 AM1/8/06
to
It is not possible to document all legitimate reasons for the module
snapshot to fail. On current operating systems it will fail for the
system process but on Vista for example you'll likely see more
processes for which it doesn't work, even if you enable
SeDebugPrivilege.

The best you can do is gracefully handle any errors you get.

Note that even if you successfully enumerate all user mode
processes and don't find a certain DLL in any of them, it doesn't
mean the DLL is not actually loaded, or locked in some other
way. A process could corrupt its PEB for example and report
bogus data to CreateToolhelp32Snapshot. Or it could open
the DLL exclusively as a file.

--
This posting is provided "AS IS" with no warranties, and confers no
rights.

Dave Brown

unread,
Jan 8, 2006, 3:48:24 PM1/8/06
to
> It is not possible to document all legitimate reasons for the module
> snapshot to fail.

If something *always* fails for a "legitimate" reason then it's not really a
failure but normal and expected behaviour. It should therefore be documented
as such. If failure occurs because there's a real problem however, then the
caller needs to know that so he can take appropriate action (which typically
means backing out of what you're doing). Moreover, the error codes in this
case are also misleading and inconsistent across different versions of
Windows. Why is ERROR_NOT_ENOUGH_MEMORY being returned when the "System"
process is encountered on WinXP for instance? (and yet different codes on
Win2000 and Win2003). This error has nothing to do with the problem so what
should the caller do. If it's returned for the "System" process which we
still don't know how to properly identify, then the caller doesn't know if a
real memory problem has been encountered or whether this is the normal and
expected return code. You also have to check the OS for XP which is an ugly
and needless step (similar situation occurs for the other OSs).

> The best you can do is gracefully handle any errors you get.

You can't do that however without being able to identify that a real error
has occurred.

> Note that even if you successfully enumerate all user mode
> processes and don't find a certain DLL in any of them, it doesn't
> mean the DLL is not actually loaded, or locked in some other
> way. A process could corrupt its PEB for example and report
> bogus data to CreateToolhelp32Snapshot. Or it could open
> the DLL exclusively as a file.

There's no legitimate reason that "CreateToolhelp32Snapshot()" should fail
just because a process corrupts itself. If the OS loses track of what
modules are loaded by that process because of the corruption then the OS
itself is defective. All bets are then off. As for opening a DLL as an
ordinary file, I agree with you on this one. Fortunately, it's a very rare
occurrence so won't happen that often in practice (not for most DLLs that
someone may be looking for anyway). Unfortunately, the last time I looked
anyway, if you simply want to find out which process has what files or any
other handles open for that matter, then there seems to be no easy way to do
this in Windows (not without relying on techniques that are sketchy,
frequently undocumented, and difficult to implement).


Pavel Lebedinsky [MSFT]

unread,
Jan 8, 2006, 6:54:23 PM1/8/06
to
"Dave Brown" wrote:

> If something *always* fails for a "legitimate" reason then it's not really
> a failure but normal and expected behaviour. It should therefore be
> documented as such.

Agreed. I'll ask the doc team to mention that module snapshot
doesn't work for the system process.

> Moreover, the error codes in this case are also misleading and
> inconsistent across different versions of Windows. Why is
> ERROR_NOT_ENOUGH_MEMORY being returned when the "System" process is
> encountered on WinXP for instance? (and yet different codes on Win2000 and
> Win2003). This error has nothing to do with the problem so what should the
> caller do.

I don't know. My guess is that different error codes are probably
side effects of changing the underlying implementation of toolhelp32,
in an effort to make it more robust. I agree that it would be better
if error codes were more relevant (and consistent) - I'll check what
we do in Vista in this case.

> If it's returned for the "System" process which we still don't know how
> to properly identify

I think you can identify it by looking at the parent process ID in
PROCESSENTRY32. For the system process it should be 0.
This again is not documented (probably should be). But at least
it's less likely to change than the process id itself.

> There's no legitimate reason that "CreateToolhelp32Snapshot()" should fail
> just because a process corrupts itself. If the OS loses track of what
> modules are loaded by that process because of the corruption then the OS
> itself is defective. All bets are then off.

The OS (the kernel) doesn't keep track of this data in the first place.
All loader structures are purely user mode. This is why toolhelp needs
to go looking for the PEB and then read stuff from the target process
address space. This simply cannot be made 100% reliable without
moving the data into the kernel.

Module enumeration in PSAPI has the same problem. It works most
of the time, and is useful for debugging/diagnostic purposes, but it
cannot be trusted in the same way as, say, the information about
what processes are currently running.

I'll see if I can get this documented.

0 new messages