I'm able to create a child process and pipe its stdin and stdout. It works fine when it is in text mode.
However, when I try to set the I/O in the child process to be in the binary format (i.e. no 0x0A to 0x0D 0x0A translation) the child process fails. _setmode returns -1 which has been documented to indicate failure. Why is that and how can it be fixed?
Parent code resembles the following:
const std::string path; // = "path_to.exe"
PROCESS_INFORMATION info;
SECURITY_ATTRIBUTES sec_attr;
//in-out from the CHILD process' perspective
HANDLE out_r = nullptr;
HANDLE out_w = nullptr;
HANDLE in_r = nullptr;
HANDLE in_w = nullptr;
sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sec_attr.lpSecurityDescriptor = NULL;
sec_attr.bInheritHandle = TRUE; //inherit by child processes
if (!CreatePipe(&out_r, &out_w, &sec_attr, 0))
throw std::exception();
if (!SetHandleInformation(out_r, HANDLE_FLAG_INHERIT, 0))
throw std::exception();
if (!CreatePipe(&in_r, &in_w, &sec_attr, 0))
throw std::exception();
if (!SetHandleInformation(in_r, HANDLE_FLAG_INHERIT, 0))
throw std::exception();
if (out_r && out_w && in_r && in_w)
{
startup_info.hStdError = out_w;
startup_info.hStdOutput = out_w;
startup_info.hStdInput = in_r;
startup_info.dwFlags = STARTF_USESTDHANDLES;
}
if (CreateProcessA(path.c_str(), (char*)cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &info)
!= TRUE)
{
DWORD error = GetLastError();
//error handling
}
// ... read data using ReadFile
Child code resembles the following:
int status = _setmode(_fileno(stdin), _O_BINARY);
if (status == -1)
throw std::exception();
status = _setmode(_fileno(stdout), _O_BINARY);
if (status == -1)
throw std::exception();
puts("hello from the child process");
I'm looking at the MSDN article Creating a Child Process with Redirected Input and Output that provides a reference implementation for nearly what you're trying to do here.
One subtle difference I see is, where in your parent code is written (the second call to SetHandleInformation()):
if (!SetHandleInformation(in_r, HANDLE_FLAG_INHERIT, 0))
the MSDN example (translated to use your variable names) would be written:
if (!SetHandleInformation(in_w, HANDLE_FLAG_INHERIT, 0))
Here are a couple more SO pages that would be worth looking at as well, if this small change doesn't help:
What is the simplest way to write to stdout in binary mode?
Win32 changing to binary mode child's Stdout (pipe)
Related
I am writing a WinDBG extension to debug a device driver, and need to call an external binary to debug the device's firmware. I would like to show the output of this binary in the WinDBG console.
My initial idea was to simply pipe the output of the binary to a buffer and print that buffer with ControlledOutput. However, I get a 'broken pipe' error when I try to read from the pipe in my extension.
Here is how I create the external process in my extension:
SECURITY_ATTRIBUTES sAttr;
HANDLE childOutRead = NULL;
HANDLE childOutWrite = NULL;
PROCESS_INFORMATION childProcInfo;
STARTUPINFO childStartInfo;
char buf[4096];
sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
CreatePipe(&childOutRead, &childOutWrite, &sAttr, 0);
// don't inherit read end
SetHandleInformation(childOutRead, HANDLE_FLAG_INHERIT, 0);
ZeroMemory(&childProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&childStartInfo, sizeof(STARTUPINFO));
childStartInfo.cb = sizeof(STARTUPINFO);
childStartInfo.hStdError = childOutWrite;
childStartInfo.hStdOut = childOutWrite;
childStartInfo.hStdIn = GetStdHandle(STD_INPUT_HANDLE);
childStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcessA(NULL, "myBinary.exe someArgs",
NULL, NULL, TRUE, 0, NULL, NULL,
&childStartInfo, &childProcInfo);
// close the handle not used in parent
CloseHandle(childOutWrite);
// read output
while (1) {
DWORD read;
BOOL r;
DWORD error;
r = ReadFile(childOutRead, buf, sizeof(buf), &read, NULL);
if (!r) {
error = GetLastError();
windbgPrintf("got error 0x%x\n", error);
break;
}
if (read == 0) break;
windbgPrint(buf, read);
}
ReadFile fails with error 0x6D, BROKEN_PIPE. This makes me suspect that the pipe is somehow not being inherited.
I have nearly identical code working in a test outside of WinDBG, so it must be doing something differently. How do I get pipes working in this way inside WinDBG?
I am working on my library which needs to capture and process the standard output (and err) of a child process as it runs. The problem arises when ReadFile is used to read the output, it does not return once the process ends (gets killed or exits).
It looks like ReadFile is not able to detect that the other end of the pipe (the write handle) is closed. According to the documentation it should return FALSE and set the last error to ERROR_BROKEN_PIPE:
If an anonymous pipe is being used and the write handle has been closed, when ReadFile attempts to read using the pipe's corresponding read handle, the function returns FALSE and GetLastError returns ERROR_BROKEN_PIPE.
Here is my code, I have stripped out the irrelevant bits: (NOTE: I have updated the allium_start to follow the suggested changes, I am keeping the original for reference, please use the newer function code to find flaws)
bool allium_start(struct TorInstance *instance, char *config, allium_pipe *output_pipes) {
// Prepare startup info with appropriate information
SecureZeroMemory(&instance->startup_info, sizeof instance->startup_info);
instance->startup_info.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES pipe_secu_attribs = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
HANDLE pipes[2];
if (output_pipes == NULL) {
CreatePipe(&pipes[0], &pipes[1], &pipe_secu_attribs, 0);
output_pipes = pipes;
}
instance->startup_info.hStdOutput = output_pipes[1];
instance->startup_info.hStdError = output_pipes[1];
instance->stdout_pipe = output_pipes[0]; // Stored for internal reference
// Create the process
bool success = CreateProcessA(
NULL,
cmd,
NULL,
NULL,
config ? true : false,
0,
NULL,
NULL,
&instance->startup_info,
SecureZeroMemory(&instance->process, sizeof instance->process)
);
// Return on failure
if (!success) return false;
}
char *allium_read_stdout_line(struct TorInstance *instance) {
char *buffer = instance->buffer.data;
// Process the input
unsigned int read_len = 0;
while (true) {
// Read data
unsigned long bytes_read;
if (ReadFile(instance->stdout_pipe, buffer, 1, &bytes_read, NULL) == false || bytes_read == 0) return NULL;
// Check if we have reached end of line
if (buffer[0] == '\n') break;
// Proceed to the next character
++buffer; ++read_len;
}
// Terminate the new line with null character and return
// Special handling for Windows, terminate at CR if present
buffer[read_len >= 2 && buffer[-1] == '\r' ? -1 : 0] = '\0';
return instance->buffer.data;
}
The allium_start creates the pipe for output redirection (it uses the same pipe for both stdout and stderr to get merged streams) and then creates the child process. The other allium_read_stdout_line function is responsible for reading the output from the pipe and returning it when it encounters a new line.
The issue occurs at the ReadFile function call, it never returns if there is nothing to read after the process exits, from my understanding all the handles of a process are closed by Windows when it ends, so it looks like ReadFile is not able to detect the fact that the pipe (write handle) at the other end has been closed.
How do I fix this? I have been searching for a solution but I have found none so far, one potential option is to use multi-threading and put ReadFile in a separate thread so that it doesn't block the whole program, by using that method I can check if the process still exists periodically while I wait for the reading to finish... or kill/stop the thread if the process is gone.
I do prefer fixing the issue instead of opting for a workaround, but I am open to any other solutions to make it work. Thanks in advance!
Edit: After reading #RemyLebeau's answer and #RbMm's comments in that answer, it is pretty clear that my understand of how handle inheritance works is fundamentally flawed. So I incorporated their suggestions (SetHandleInformation to disable inheritance of read handle and closing it after creating the child process) into my allium_start function:
bool allium_start(struct TorInstance *instance, char *config, allium_pipe *output_pipes) {
// Prepare startup info with appropriate information
SecureZeroMemory(&instance->startup_info, sizeof instance->startup_info);
instance->startup_info.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES pipe_secu_attribs = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
HANDLE pipes[2];
if (output_pipes == NULL) {
CreatePipe(&pipes[0], &pipes[1], &pipe_secu_attribs, 0);
output_pipes = pipes;
}
SetHandleInformation(output_pipes[0], HANDLE_FLAG_INHERIT, 0);
instance->startup_info.hStdOutput = output_pipes[1];
instance->startup_info.hStdError = output_pipes[1];
instance->stdout_pipe = output_pipes[0]; // Stored for internal reference
// Create the process
bool success = CreateProcessA(
NULL,
cmd,
NULL,
NULL,
config ? true : false,
0,
NULL,
NULL,
&instance->startup_info,
SecureZeroMemory(&instance->process, sizeof instance->process)
);
// Close the write end of our stdout handle
CloseHandle(output_pipes[1]);
// Return on failure
if (!success) return false;
}
(The below text was originally here before edit 2)
But sadly it still doesn't work :(
Edit 2 (after accepting answer): It does work! See my last comment on the accepted answer.
You are not managing your pipes correctly, or more specifically, you are not controlling the inheritance of your pipe handles. DO NOT let the child process inherit the reading handle of your pipe (output_pipes[0]), otherwise the pipe will not break correctly when the child process ends.
Read MSDN for more details:
Creating a Child Process with Redirected Input and Output
The case of the redirected standard handles that won’t close even though the child process has exited
Use SetHandleInformation() or PROC_THREAD_ATTRIBUTE_LIST to prevent CreateProcess() from passing output_pipes[0] to the child process as an inheritable handle. The child process does not need access to that handle, so there is no need to pass it over the process boundary anyway. It only needs access to the writing handle of your pipe (output_pipes[1]).
For anonymous pipelines, the read process and the write process will have the handler of hRead and hWrite, each of process has its own handler(copy after inheritance). So after your child process exit and close the handler in it, anther hWrite still in parent process. We must pay attention to close hRead in the write process, close hWrite in the read process.
I can reproduce this ReadFile issue, and if closing write handler after setting child's hStdOutput and hStdError, the ReadFile will return 0 after the child process exit.
Here is my code sample,
Parent.cpp:
#include <windows.h>
#include <iostream>
#include <stdio.h>
HANDLE childInRead = NULL;
HANDLE W1 = NULL;
HANDLE W2 = NULL;
HANDLE R2 = NULL;
HANDLE R1 = NULL;
#define BUFSIZE 4096
void CreateChildProcess() {
TCHAR applicationName[] = TEXT("kids.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL success = FALSE;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = W1;
si.hStdOutput = W1;
si.hStdInput = R2;
si.dwFlags |= STARTF_USESTDHANDLES;
success = CreateProcess(NULL, applicationName, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (!success) {
printf("Error creating child process \n");
}
else {
printf("Child process successfuly created \n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main()
{
printf("Parent process running.... \n");
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE] = { 0 };
BOOL bSuccess = FALSE;
SECURITY_ATTRIBUTES secAttr;
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
printf("Creating first pipe \n");
if (!CreatePipe(&R1, &W1, &secAttr, 0)) {
printf("\n error creating first pipe \n");
}
printf("Creating second pipe \n");
if (!CreatePipe(&R2, &W2, &secAttr, 0)) {
printf("\n error creating second pipe \n");
}
if (!SetHandleInformation(R1, HANDLE_FLAG_INHERIT, 0)) {
printf("\n R1 SetHandleInformation \n");
}
if (!SetHandleInformation(W2, HANDLE_FLAG_INHERIT, 0)) {
printf("\n W1 SetHandleInformation \n");
}
printf("\n Creating child process..... \n");
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
CreateChildProcess();
CloseHandle(W1);
CloseHandle(R2);
for (;;) {
printf("Inside for loop \n");
//1. read from stdin
printf("read from stdin:\n");
bSuccess = ReadFile(hStdIn, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//2. write to Pipe2
printf("write to Pipe2...\n");
bSuccess = WriteFile(W2, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//3. read from Pipe1
printf("read from Pipe1...\n");
bSuccess = ReadFile(R1, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess)
{
printf("error reading :%d \n", GetLastError());
break;
}
//4. write to stdout
printf("write to stdout:\n");
bSuccess = WriteFile(hStdOut, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
}
getchar();
return 0;
}
Kids.cpp:
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 4096
int main()
{
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE];
BOOL success = FALSE;
HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
printf("Child process running....");
if (stdIn == INVALID_HANDLE_VALUE || stdOut == INVALID_HANDLE_VALUE) {
ExitProcess(1);
}
//for (;;) {
success = ReadFile(stdIn, chBuf, BUFSIZE, &dRead, NULL);
//if (!success || dRead == 0) break;
success = WriteFile(stdOut, chBuf, dRead, &dWritten, NULL);
//if (!success) break;
//}
return 0;
}
I'm desperately trying to create a child process and redirect its output to new pipes and read from those pipes, but I just can't get it to work. I am very new the Win32API, please be nice to me. :)
After having failed on using the Win32API "normally", I created wrappers to focus on finding an error in the logic and/or order of API calls. You can find the interface for the wrappers below. Since most of the methods directly translate to Win32API calls, it should (hopefully) not be an obstacle to answering this question.
I get the same behaviour with using the wrapper classes as I have experienced originally.
I've read a lot of online resources about this topic and one says something different than the other. The one that has been most useful until now was https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx, especially this information (emphasis mine):
The parent process uses the opposite ends of these two pipes to write to the child process's input and read from the child process's output. As specified in the STARTUPINFO structure, these handles are also inheritable. However, these handles must not be inherited. Therefore, before creating the child process, the parent process uses the SetHandleInformation function to ensure that the write handle for the child process's standard input and the read handle for the child process's standard input cannot be inherited. For more information, see Pipes.
Before I found this topic and closed the ends that I'm not using from the parent process side, I head ReadFile() blocking forever on the standard output read handle of the child process. Now, it always immediately returns that the pipe is broken.
This is how I create the Pipes and Process:
Popen(const String& command, const String& args,
Bool use_current_pipes = false, Bool merge_stderr = true)
{
Bool ok = true;
_error = 0;
ZeroMemory(&_pi, sizeof(_pi));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
if (!use_current_pipes) {
// Create pipes for standard input, output and error.
_stdin = Pipe(true);
_stdout = Pipe(true);
if (_stdout && merge_stderr)
_stderr = _stdout.Duplicate();
else
_stderr = Pipe(true);
if (_stdin && _stdout && _stderr) {
_stdin.w.SetInheritable(false);
_stderr.r.SetInheritable(false);
_stdout.r.SetInheritable(false);
si.hStdInput = _stdin.r.Get();
si.hStdOutput = _stdout.w.Get();
si.hStdError = _stderr.w.Get();
si.dwFlags |= STARTF_USESTDHANDLES;
}
else {
ok = false;
}
}
else {
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
}
// Create the process. Enclose the actual command in quotes.
ok = ok && CreateProcess(
nullptr, // command might contain whitespace, pass it quoted in arg 2 instead.
AutoString("\"" + command + "\" " + args),
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
true, // handles are inherited
0, // No creation flags
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO
&_pi); // Pointer to PROCESS_INFORMATION
// Something went wrong? Well, bad.
if (!ok) {
_error = GetLastError();
}
// Close the handles that have been inherited by the child process
// and to which we don't need access to, otherwise they will not
// close when the child exits.
_stdin.r.Close();
_stdout.w.Close();
_stderr.w.Close();
}
And this is how I read from the standard output (_stdout.r):
UInt Read(UInt num_bytes, char* buffer) {
if (!_stdout.r) return 0;
DWORD bytes_read = 0;
if (!ReadFile(_stdout.r.Get(), buffer, num_bytes - 1, &bytes_read, nullptr)) {
_error = GetLastError();
ConsoleOut("[ERROR]: ReadFile() : " + String::IntToString((Int32) _error));
if (_error == ERROR_BROKEN_PIPE) {
ConsoleOut("No Wait, the Pipe is just broken.");
_error = 0; // that's fine
}
return 0;
}
buffer[bytes_read] = '\0';
return bytes_read;
}
When I comment out the last lines of the Popen constructor (closing the pipe handles that are not used from the parent process) ReadFile() blocks forever. With these lines enabled, the Pipe is always immediately broken (the child process exits pretty quickly).
Question
Can someone see what is wrong in my code/logic?
If not, I would already appreciate if there is a complete working example of opening a child process and reading its output
Wrapper Interface
struct Handle {
HANDLE h;
explicit Handle();
explicit Handle(HANDLE h);
Handle(Handle&& other);
Handle& operator = (Handle&& other);
~Handle();
void Close();
HANDLE Get();
HANDLE Release();
Handle Duplicate(DWORD options = DUPLICATE_SAME_ACCESS, HANDLE src_proc = nullptr, HANDLE dst_proc = nullptr) const;
DWORD GetInfo() const; // uses GetHandleInformation
void SetInheritable(bool inheritable) const; // uses SetHandleInformation
bool GetInheritable() const;
operator bool() const;
explicit Handle(const Handle&) = delete;
Handle* operator = (const Handle&) = delete;
};
struct Pipe {
Handle r, w;
DWORD error;
explicit Pipe();
explicit Pipe(bool inheritable);
Pipe(Pipe&& other);
Pipe& operator = (Pipe&& other);
~Pipe();
void Close();
Pipe Duplicate(DWORD options = DUPLICATE_SAME_ACCESS, HANDLE src_proc = nullptr, HANDLE dst_proc = nullptr) const;
operator bool() const;
explicit Pipe(const Pipe&) = delete;
Pipe* operator = (const Pipe&) = delete;
};
Without using either threads or overlapped I/O, you risk deadlock. The child process could be trying to read from its stdin or waiting for space in its stdout buffer so it can write, you cannot tell which, and when you choose wrong, you get the observed behavior. The blocking read on the child's output means you guessed wrong, and it is actually waiting for input.
Read Raymond Chen's blog article Be careful when redirecting both a process's stdin and stdout to pipes, for you can easily deadlock which I also linked in your earlier question today. It specifically calls out the horrible brokenness in the very same sample you linked in your question.
I 'm using the CreatePipe to redirect stdin/out from a process to my process.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365152(v=vs.85).aspx
This works ok so far. The problem is when I want to terminate the thread that waits for the client process to write something.
I can use CancelIoEx() but this only works in Vista+, and I also want an XP solution. Without CancelIoEx(), ReadFile() in the other thread never returns.
I cannot also use OVERLAPPED ReadFile, for pipes created with CreatePipe do not support it.
Any options?
Save a handle to the write end of the stdout pipe when creating the child process. You can then write a character to this to unblock the thread that has called ReadFile (that is reading from the read end of the stdout pipe). In order not to interpret this as data, create an Event (CreateEvent) that is set (SetEvent) in the thread that writes the dummy character, and is checked after ReadFile returns. A bit messy, but seems to work.
/* Init */
stdout_closed_event = CreateEvent(NULL, TRUE, FALSE, NULL);
/* Read thread */
read_result = ReadFile(stdout_read, data, buf_len, &bytes_read, NULL);
if (!read_result)
ret = -1;
else
ret = bytes_read;
if ((bytes_read > 0) && (WAIT_OBJECT_0 == WaitForSingleObject(stdout_closed_event, 0))) {
if (data[bytes_read-1] == eot) {
if (bytes_read > 1) {
/* Discard eot character, but return the rest of the read data that should be valid. */
ret--;
} else {
/* No data. */
ret = -1;
}
}
}
/* Cancel thread */
HMODULE mod = LoadLibrary (L"Kernel32.dll");
BOOL WINAPI (*cancel_io_ex) (HANDLE, LPOVERLAPPED) = NULL;
if (mod != NULL) {
cancel_io_ex = (BOOL WINAPI (*) (HANDLE, LPOVERLAPPED)) GetProcAddress (mod, "CancelIoEx");
}
if (cancel_io_ex != NULL) {
cancel_io_ex(stdout_write_pipe, NULL);
} else {
SetEvent(stdout_closed_event);
WriteFile(stdout_write_pipe, &eot, 1, &written, NULL);
}
Attempting to implement a poor man's test of whether a process is still running or not (essentially an equivalent of the trivial kill(pid, 0).)
Hoped to be able to simply call OpenProcess with some minimal desired access then test for either GetLastError() == ERROR_INVALID_PARAMETER or GetExitCodeProcess(...) != STILL_ACTIVE.
Nice try... Running on Windows XP, as administrator:
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
DWORD dwLastError = GetLastError();
}
...fails miserably with dwLastError == ERROR_ACCESS_DENIED when pid is owned by a different (not SYSTEM) user. Moreover, if pid was originally owned by a different user but has since terminated, OpenProcess also fails with ERROR_ACCESS_DENIED (not ERROR_INVALID_PARAMETER.)
Do I have to use Process32First/Process32Next or EnumProcesses?
I absolutely do not want to use SeDebugPrivilege.
Thanks,
V
If you have a process ID:
// this should succeed even when a medium integrity process
// requests access to a high integrity process
if (HANDLE h = OpenProcess(SYNCHRONIZE, FALSE, pid))
{
// do a wait, if the handle is signaled: not running
DWORD wait = WaitForSingleObject(h, 0);
if (wait == WAIT_OBJECT_0) return FALSE;
}
// cannot get a handle to the process:
// probably running at system integrity level
// I'm not sure how reliable this check is, but it seems to work:
// if access is denied: running
// if invalid parameter: not running
else if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
If you have a window handle that should be valid for as long as the process is running, this is a good alternative:
if (hWnd && !IsWindow(hWnd)) return FALSE;
static BOOL
isProcessAlive(DWORD th32ProcessID) {
BOOL bSuccess = FALSE;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 pe32 = { sizeof(pe32), 0 };
if (Process32First(hSnap, &pe32)) {
while (pe32.th32ProcessID != pid && Process32Next(hSnap, &pe32));
_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_NO_MORE_FILES);
bSuccess = (pe32.th32ProcessID == th32ProcessID);
}
CloseHandle(hSnap);
}
return bSuccess;
}