--- q_a/samples/inherit/inherit.c 2018/08/09 18:29:41 1.1.1.2 +++ q_a/samples/inherit/inherit.c 2018/08/09 18:30:09 1.1.1.3 @@ -1,9 +1,28 @@ + +/******************************************************************************\ +* This is a part of the Microsoft Source Code Samples. +* Copyright (C) 1993 Microsoft Corporation. +* All rights reserved. +* This source code is only intended as a supplement to +* Microsoft Development Tools and/or WinHelp documentation. +* See these sources for detailed information regarding the +* Microsoft samples programs. +\******************************************************************************/ + /******************************************************************** * This program is to demonstrate the use of anonymous pipes used as * -* stdout and stderr replacements. * +* stdout and stderr replacements. One of two techniques can be * +* chose to do this: the SetStdHandle technique or the * +* STARTF_USESTDHANDLES technique. The SetStdHandle technique sets * +* the standard output handles to the pipe that we will read from, * +* which the child will inherit. The STARTF_USESTDHANDLES technique * +* passes the pipe handles to the child as standard handles via the * +* STARTUPINFO structure. The STARTF_USESTDHANDLES technique *must* * +* be used for "console-less" processes such as GUI applications or * +* detached processes. * * * * This program demonstrates the use of the following Win32 APIs: * -* CreatePipe, SetStdHandle. * +* CreatePipe, CreateProcess. * * * * This program also uses the following Win32 APIs: * * GetLastError, CreateFile, CloseHandle, CreateProcess, ReadFile, * @@ -16,254 +35,241 @@ * trace file is the name of the file where the stdout * * and stderr of command will be redirected * * * -* command to execute is command line image of the function * -* you wish to perform. * +* command to execute can be either an external executable or an * +* internal cmd.exe command. * * * * Examples: * * * -* inherit trace.txt cl386 foo * -* inherit trace.txt nmake foo * +* inherit chkdsk.dat chkdsk d: * +* inherit nmake.txt nmake /f foo.mak * +* * ********************************************************************/ -/* Microsoft Developer Support - Copyright (c) 1992 Microsoft Corporation */ - #include #include #include #include -#define HSTDOUT 1 /* system handle for stderr */ -#define HSTDERR 2 /* read pipe handle */ +/* define USESTDHANDLES to use the new technique of passing the +standard handles to the child process via the STARTUPINFO structure. +This technique must be used for "console-less" parents such as GUI +applications or detached applications. */ + +#define USESTDHANDLES /* Standard error macro for reporting API errors */ -#define PERR(api) printf("%s: Error %d from %s on line %d\n", \ - __FILE__, GetLastError(), api, __LINE__); +#define PERR(bSuccess, api) {if (!(bSuccess)) printf("%s: Error %d from %s \ + on line %d\n", __FILE__, GetLastError(), api, __LINE__);} int main(int argc, char *argv[]) { - char bReadBuffer[64]; /* pipe read buffer */ - BOOL fSuccess; /* BOOL return code for APIs */ + char chReadBuffer[64]; /* pipe read buffer */ + BOOL bSuccess; /* BOOL return code for APIs */ int j; HANDLE hOutFile; /* handle to log file */ - HANDLE hReadPipe, hWritePipe; /* handles to the anonymous pipe */ - char bArgBuffer[256]; /* child process argument buffer */ - DWORD cbReadBuffer; /* number of bytes read or to be written */ + /* handles to the anonymous pipe */ + HANDLE hReadPipe, hWritePipe, hWritePipe2; + char szArgs[256]; /* child process argument buffer */ + char *p; /* temporary pointer into szArgs */ + DWORD cchReadBuffer; /* number of bytes read or to be written */ STARTUPINFO si; /* for CreateProcess call */ PROCESS_INFORMATION pi; /* for CreateProcess call */ - char *tpARG; SECURITY_ATTRIBUTES saPipe; /* security for anonymous pipe */ + /* Check to make sure we are running on Windows NT */ + if( GetVersion() & 0x80000000 ) + { + MessageBox(NULL, "Sorry, this application requires Windows NT.\n" + "This application will now terminate.", + "Error: Windows NT Required to Run", MB_OK ); + return(1); + } if (argc < 3) { puts("format: inherit "); puts("trace file is the name of the file where the stdout"); puts("and stderr of command will be redirected\n"); - puts("command to execute is command line image of the function"); + puts("command to execute is command line of the function"); puts("you wish to perform.\n"); puts("Examples:\n"); - puts(" inherit trace.txt cl386 foo"); - puts(" inherit trace.txt nmake foo"); + puts(" inherit trace.txt chkdsk d:"); + puts(" inherit trace.txt nmake /f foo.mak"); return(1); } + /* create the log file where we will save all output from child */ hOutFile = CreateFile(argv[1], /* file to open */ GENERIC_WRITE, /* access mode */ FILE_SHARE_READ, /* share mode */ NULL, /* security attributes */ - CREATE_ALWAYS, /* creation flags */ + CREATE_ALWAYS, /* creation flags - trash existing file */ FILE_ATTRIBUTE_NORMAL, /* file attributes */ NULL); - if (hOutFile == (HANDLE) -1) - PERR("CreateFile"); - - /****************************************************************** - * DosMakePipe(&hReadPipe, * - * &hWritePipe, * - * 0); * - * * - * The OS/2 DosMakePipe call will be replaced by the Win32 * - * CreatePipe call. One important difference between these two * - * calls is the addition of the security attribute with * - * CreatePipe. In order for the child process to be able to write * - * to the anonymous pipe, the handle must be marked as inheritable * - * by child processes by setting the * - * SECURITY_ATTRIBUTES.bInheritHandle flag to TRUE. * - ******************************************************************/ + PERR(hOutFile != INVALID_HANDLE_VALUE, "CreateFile"); + /* set up the security attributes for the anonymous pipe */ saPipe.nLength = sizeof(SECURITY_ATTRIBUTES); saPipe.lpSecurityDescriptor = NULL; + /* In order for the child to be able to write to the pipe, the handle */ + /* must be marked as inheritable by setting this flag: */ saPipe.bInheritHandle = TRUE; - fSuccess = CreatePipe(&hReadPipe, /* read handle */ - &hWritePipe, /* write handle */ + + /* create the anonymous pipe */ + bSuccess = CreatePipe(&hReadPipe, /* read handle */ + &hWritePipe, /* write handle, used as stdout by child */ &saPipe, /* security descriptor */ - 0); /* use default pipe buffer size */ - if (!fSuccess) - PERR("CreatePipe"); - - /******************************************************************* - * hOldHandle = HSTDOUT; * - * DosDupHandle(hWritePipe, * - * &hOldHandle); * - * hOldHandle = HSTDERR; * - * DosDupHandle(hWritePipe, * - * &hOldHandle); * - * DosClose(hWritePipe); * - * * - * Now we must set standard out and standard error to the write end * - * of the pipe. Rather than using the OS/2 method of duplicating * - * the standard output handle onto the pipe handle, in Win32 all we * - * need to do is to set the standard handle to the pipe via * - * SetStdHandle. Once standard out and standard error are set to * - * the pipe handle, we must close the pipe handle so that when the * - * child process dies, the write end of the pipe will close, * - * setting an EOF condition on the pipe. * - *******************************************************************/ - - fSuccess = SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe); - if (!fSuccess) - PERR("SetStdHandle"); - - fSuccess = SetStdHandle(STD_ERROR_HANDLE, hWritePipe); - if (!fSuccess) - PERR("SetStdHandle"); - - /* start the child process; set up the command-line buffer */ - memset(bArgBuffer, 0, sizeof(bArgBuffer)); - strcpy(bArgBuffer, argv[2]); - if (strchr(bArgBuffer, '.') == NULL) /* does it have a '.'? */ - strcat(bArgBuffer, ".exe"); /* if not, assume it's an .exe */ - - /***************************************************************** - * tpARG = strchr(bArgBuffer, 0) + 1; * - * * - * This will point one past the terminating null in the buffer. * - * * - * In the OS/2 version, the first parameter of the command line, * - * the program name, must be separated from the rest of the * - * parameters by a null. The rest of the parameters would be * - * separated by spaces. In the Win32 version, the program name is * - * separated from the parameters by a *space*, not a null. * - *****************************************************************/ + 0); /* pipe buffer size */ + PERR(bSuccess, "CreatePipe"); - strcat(bArgBuffer, " "); - tpARG = strchr(bArgBuffer, 0); + /* Now we need to change the inheritable property for the readable + end of the pipe so that the child will not inherit that handle as + a "garbage" handle. This will keep us from having extra, + unclosable handles to the pipe. Alternatively, we could have + opened the pipe with saPipe.bInheritHandle = FALSE and changed the + inherit property on the *write* handle of the pipe to TRUE. */ + + bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */ + hReadPipe, /* handle to duplicate */ + GetCurrentProcess(), /* destination process */ + NULL, /* new handle - don't want one, change original handle */ + 0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */ + FALSE, /* make it *not* inheritable */ + DUPLICATE_SAME_ACCESS); + PERR(bSuccess, "DuplicateHandle"); + + /* I most cases you can get away with using the same anonymous + pipe write handle for both the child's standard output and + standard error, but this may cause problems if the child app + explicitly closes one of its standard output or error handles. If + that happens, the anonymous pipe will close, since the child's + standard output and error handles are really the same handle. The + child won't be able to write to the other write handle since the + pipe is now gone, and parent reads from the pipe will return + ERROR_BROKEN_PIPE and child output will be lost. To solve this + problem, simply duplicate the write end of the pipe to create + another distinct, separate handle to the write end of the pipe. + One pipe write handle will serve as standard out, the other as + standard error. Now *both* write handles must be closed before the + write end of the pipe actually closes. */ + + bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */ + hWritePipe, /* handle to duplicate */ + GetCurrentProcess(), /* destination process */ + &hWritePipe2, /* new handle, used as stderr by child */ + 0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */ + TRUE, /* it's inheritable */ + DUPLICATE_SAME_ACCESS); + PERR(bSuccess, "DuplicateHandle"); + + /* Set up the STARTUPINFO structure for the CreateProcess() call */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + /* Set up the command-line buffer for the child for CreateProcess() */ + memset(szArgs, 0, sizeof(szArgs)); + strcpy(szArgs, argv[2]); + if (strchr(szArgs, '.') == NULL) /* does it have a '.'? */ + strcat(szArgs, ".exe"); /* if not, assume it's an .exe */ + strcat(szArgs, " "); + p = strchr(szArgs, 0); /* point to the terminating null */ for (j = 3; j < argc; j++) { - strcat(tpARG, argv[j]); - strcat(tpARG, " "); + strcat(p, argv[j]); + /* the program and parameters are delimited by spaces */ + strcat(p, " "); } - /****************************************************************** - * DosExecPgm(bObjectBuffer, * - * sizeof(bObjectBuffer), * - * 1, * - * bArgBuffer, * - * NULL, * - * &bResultCodes, * - * bPath); * - * DosClose(HSTDOUT); * - * DosClose(HSTDERR); * - * * - * Replace the OS/2 DosExecPgm call with Win32 CreateProcess. Be * - * sure to set the fInheritHandles parameter to TRUE so that open * - * file handles will be inheritied. We can close the child process * - * and thread handles as we won't be needing them. The child * - * process will not die until these handles are closed. Also note * - * that we don't need to close the standard handles in the Win32 * - * version since we did not duplicate the handles but rather set * - * the standard handles directly without duplicating them. * - ******************************************************************/ +#ifdef USESTDHANDLES + /* If using the STARTUPINFO STARTF_USESTDHANDLES flag, be sure to + set the CreateProcess fInheritHandles parameter too TRUE so that + the file handles specified in the STARTUPINFO structure will be + inheritied by the child. Note that we don't specify a standard + input handle; the child will not inherit a valid input handle, so + if it reads from stdin, it will encounter errors. */ + + si.hStdOutput = hWritePipe; /* write end of the pipe */ + si.hStdError = hWritePipe2; /* duplicate of write end of the pipe */ + si.dwFlags = STARTF_USESTDHANDLES; +#else + /* If we're not using the STARTF_USESTDHANDLES flag, set the + standard output and error handles to the end of the pipe we want + the child to inherit with SetStdHandle(). For this program, we + don't want standard input inherited so we'll also change the + handle inheritance property of standard input so that it is not + inherited */ + + bSuccess = SetStdHandle(STD_OUTPUT_HANDLE, hWritePipe); + PERR(bSuccess, "SetStdHandle"); + bSuccess = SetStdHandle(STD_ERROR_HANDLE, hWritePipe2); + PERR(bSuccess, "SetStdHandle"); + bSuccess = DuplicateHandle(GetCurrentProcess(), /* source process */ + GetStdHandle(STD_INPUT_HANDLE), /* handle to duplicate */ + GetCurrentProcess(), /* destination process */ + NULL, /* new handle - don't want one, change original handle */ + 0, /* new access flags - ignored since DUPLICATE_SAME_ACCESS */ + FALSE, /* it's *not* inheritable */ + DUPLICATE_SAME_ACCESS); + PERR(bSuccess, "DuplicateHandle"); +#endif - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - fSuccess = CreateProcess(NULL, /* filename */ - bArgBuffer, /* full command line for child */ + /* Now create the child process, inheriting handles */ + + bSuccess = CreateProcess(NULL, /* filename */ + szArgs, /* full command line for child */ NULL, /* process security descriptor */ NULL, /* thread security descriptor */ - TRUE, /* inherit handles? */ + TRUE, /* inherit handles? Also use if STARTF_USESTDHANDLES */ 0, /* creation flags */ NULL, /* inherited environment address */ NULL, /* startup dir; NULL = start in current */ &si, /* pointer to startup info (input) */ &pi); /* pointer to process info (output) */ - if (!fSuccess) - PERR("CreateProcess"); + PERR(bSuccess, "CreateProcess"); + + /* We can close the returned child process handle and thread + handle as we won't be needing them; you could, however, wait on + the process handle to wait until the child process terminates. */ + CloseHandle(pi.hThread); CloseHandle(pi.hProcess); - /* We need to close our instance of the inherited pipe write - handle now that it's been inherited so that it will actually - close when the child process ends. This will put an EOF - condition on the pipe which we can then detect. */ - fSuccess = CloseHandle(hWritePipe); - if (!fSuccess) - PERR("CloseHandle"); - - /* read from the pipe until EOF condition reached*/ - do { - - /************************************************************ - * DosRead(hReadPipe, * - * bReadBuffer, * - * sizeof(bReadBuffer), * - * &cbReadBuffer); * - * * - * Replace the OS/2 DosRead calls with ReadFile calls. These * - * calls are very similar. * - ************************************************************/ - - fSuccess = ReadFile(hReadPipe, /* read handle */ - bReadBuffer, /* buffer for incoming data */ - sizeof(bReadBuffer), /* number of bytes to read */ - &cbReadBuffer, /* number of bytes actually read */ + + /* We need to close our instances of the inheritable pipe write + handle now that it's been inherited so that all open handles to + the pipe are closed when the child process ends and closes its + handles to the pipe. */ + + bSuccess = CloseHandle(hWritePipe); + PERR(bSuccess, "CloseHandle"); + bSuccess = CloseHandle(hWritePipe2); + PERR(bSuccess, "CloseHandle"); + + /* read from the pipe until we get an ERROR_BROKEN_PIPE */ + for (;;) + { + bSuccess = ReadFile(hReadPipe, /* read handle */ + chReadBuffer, /* buffer for incoming data */ + sizeof(chReadBuffer), /* number of bytes to read */ + &cchReadBuffer, /* number of bytes actually read */ NULL); /* no overlapped reading */ - if (!fSuccess) - if (GetLastError() == ERROR_BROKEN_PIPE) - break; /* child has died */ - else - PERR("ReadFile"); - if (fSuccess && cbReadBuffer) + if (!bSuccess && (GetLastError() == ERROR_BROKEN_PIPE)) + break; /* child has died */ + PERR(bSuccess, "ReadFile"); + if (bSuccess && cchReadBuffer) { - - /*************************************************** - * DosWrite(hOutFile, * - * bReadBuffer, * - * cbReadBuffer, * - * &cbReadBuffer); * - * * - * Replace the DosWrite calls with WriteFile calls. * - ***************************************************/ - - fSuccess = WriteFile(hOutFile, /* write handle */ - bReadBuffer, /* buffer to write */ - cbReadBuffer, /* number of bytes to write */ - &cbReadBuffer, /* number of bytes actually written */ + /* write the data from the child to the file */ + bSuccess = WriteFile(hOutFile, /* write handle */ + chReadBuffer, /* buffer to write */ + cchReadBuffer, /* number of bytes to write */ + &cchReadBuffer, /* number of bytes actually written */ NULL); /* no overlapped writing */ - if (!fSuccess) - PERR("WriteFile"); - - /*************************************************************** - * VioWrtTTY(bReadBuffer, * - * cbReadBuffer, * - * 0); * - * * - * Since we didn't do any duplication of standard handles in * - * the Win32 version, but rather simply set the standard output * - * handle for the child process to the pipe, we can still use * - * the standard handles in the parent process, unlike the OS/2 * - * version, which must use VioWrtTTY to output to the screen. * - ***************************************************************/ + PERR(bSuccess, "WriteFile"); /* write buffer (of specified length) to console */ - printf("%.*s", cbReadBuffer, bReadBuffer); + printf("%.*s", cchReadBuffer, chReadBuffer); } - } while (fSuccess && cbReadBuffer); + } /* close the trace file, pipe handles */ CloseHandle(hOutFile); CloseHandle(hReadPipe); return(0); } - - -