Under Windows 98, the CreateProcess command starts every new process. The old WinExec and LoadModule commands still exist for backward compatibility, but internally both now call CreateProcess.
BOOL CreateProcess(
LPCTSTR lpszImageName, // image file (.EXE) name
LPCTSTR lpszCmdLine, // command line for new process
LPSECURITY_ATTRIBUTES lpsaProcess, // how process will be shared
LPSECURITY_ATTRIBUTES lpsaThread, // how new thread will be shared
BOOL bInheritHandles, // TRUE to inherit handles
DWORD fdwCreate, // creation flags
LPVOID lpvEnvironment, // new environment (default = NULL)
LPTSTR lpszCurrentDir, // name of new current directory
LPSTARTUPINFO lpStartupInfo, // gives info about new process
LPPROCESS_INFORMATION lpProcInfo ) // receives info about new process
The CreateProcess function’s parameters permit control over the new process’s starting conditions. First, every process requires code to execute. Together, the first two parameters create a complete command line naming a program file and passing it arguments.
The first parameter, lpszImageName, must point only to the name of the executable file (for example, Child). Do not include any arguments. In locating a file named in this parameter; the system does not search the PATH directories. The program must be in the current directory, or the string must contain a full path name.
The second parameter, lpszCmdLine, may be NULL if you have no arguments to pass. If you do pass arguments, the first item in the lpszCmdLine string must be what C programmers call the argv[0] value.
The first item is typically, but not necessarily, the name of the program, even if you have already given it in lpszImageName. You can omit the path and extension if they appear in the lpszImageName, but some item must precede the first argument.
To make CreateProcess search along the environment PATH for the file, leave the first parameter blank and pass the complete command line in the second parameter. Table 15.2 lists some examples of using the first and second CreateProcess parameters.
Table 15.2: Using CreateProcess Parameters
First Parameter | Second Parameter | Result |
d:\dev\bin\qgrep | NULL | Runs qgrep without arguments only if qgrep is found in the \dev\bin directory. |
qgrep.exe | qgrep -L-y “ERROR_” *.h | Runs qgrep with arguments only if qgrep is in the current directory. |
NULL | qgrep -L-y “ERROR_” *.h | Runs qgrep with arguments if it can be found anywhere on the path. |
Security Attributes The next two parameters in CreateProcess provide security attributes for both the new process and its primary thread. (Every process receives an initial thread; otherwise, nothing would ever execute.) You saw the same SECURITY_ATTRIBUTES structure in the discussion of creating new threads in Chapter 14, and you will see it often in commands that create new objects. The information in this structure controls what another process may do with the object if it opens a duplicate handle. It also controls whether child processes may inherit a handle to the new object. By default, other processes receive full access to the new object and children do not inherit it.
WARNING
Under Windows 98, the security attributes do not apply and are simply ignored. If your application is intended only for Windows 98 and not for the Windows NT environment or if no security is needed, this argument may be passed as NULL.
Blocking Inheritance The bInheritHandles parameter of CreateProcess gives you a second chance to prevent a child from inheriting other handles you have already created. If bInheritHandles is FALSE, the new process inherits no handles, even if they were marked inheritable when they were created. Blocking inheritance is useful because many objects, including threads and processes, persist in memory until all the handles to them are closed. If every new child inherits a full set of handles from its parent, many objects will stay in memory until all the child processes exit and their handles are destroyed. Children should be allowed to inherit only the handles they actually need.
Process Type and Priority The creation flags in the fdwCreate parameter govern the type and priority of the new process. The new process may be initially suspended; it may have a console or a GUI window; it may receive debugging information from its child; and it may receive a particular priority class. Here are some of the values that can be combined in the creation flag parameter:
Environment and Directory Settings Each process has its own set of environment variables and its own current directory setting. If the lpszEnvironment and lpszCurrentDir parameters are NULL, the new process copies the block of environment variables and the directory setting from its parent. Environment variables are those defined with the SET command at the command prompt, typically in an Autoexec file. Programmers usually define variables, such as BIN, LIB, and INCLUDE, to tell the compiler and linker where to find particular kinds of files.
More generally, environment variables are a convenient way to customize programs by putting information where it is always available. A parent can send information to its children by defining environment variables. To give the child an entirely new environment, the parent should create a buffer and fill it with null-terminated strings of the form <variable>=<setting>.
TIP
You should be careful to ensure that no spaces appear next to the equal sign. For example, the form <variable> = <setting> is invalid and will not be accepted.
The last string must be followed by two null characters. To give the child a slightly modified version of the parent’s existing environment, the parent can temporarily modify its own settings with GetEnvironmentVariable and SetEnvironmentVariable, create the child, and then restore the old settings.
Process Information Structures The last two parameters of CreateProcess point to structures. The parent fills out the STARTUPINFO structure before calling CreateProcess and receives information about the new process in the PROCESS_INFORMATION structure.
// You fill this out to describe a new process in advance
typedef struct _STARTUPINFO /* si */
{
DWORD cb; // size of(STARTUPINFO)
LPTSTR lpReserved; // should be NULL
LPTSTR lpDesktop; // name of desktop object to run in
LPSTR lpTitle; // caption for console title bar
DWORD dwX; // upper-left corner for new window
DWORD dwY;
DWORD dwXSize; // width of new window
DWORD dwYSize; // height of new window
DWORD dwXCountChars; // width of new console window
DWORD dwYCountChars; // height of new console window
DWORD dwFillAttribute; // text/background colors for console
DWORD dwFlags; // activates fields in this structure
WORD wShowWindow; // iCmdShow parameter value
WORD cbReserved2; // zero
LPBYTE lpReserved2; // NULL
HANDLE hStdInput; // handles for the
HANDLE hStdOutput; // child’s standard
HANDLE hStdError; // I/O devices
} STARTUPINFO, *LPSTARTUPINFO;
You must fill in the first field of the STARTUPINFO structure, but you can initialize all the rest to 0 or NULL to accept default values.
The lpDesktop field is ignored under Windows 98. If you want your programs to also run under Windows NT, however, lpDesktop points to a zero-terminated string specifying either the name of the Desktop only or the name of both the window station and Desktop for this process. The Desktop is the background window on which a user’s programs run. The current system allows only two Desktops: one for logging on and one for the current user. A backslash in the string pointed to by lpDesktop indicates that the string includes both Desktop and window-station names. Otherwise, the lpDesktop string is interpreted as a Desktop name. If lpDesktop is NULL, the new process inherits the window station and Desktop of its parent process.
The lpTitle field is used only for console purposes to supply a title in the title bar when a new console window is created. If lpTitle is NULL, the name of the executable is used instead. For GUI or console processes, which do not create a new console window, this parameter should be NULL.
Many of the other fields in a STARTUPINFO structure matter only for nongraphics processes that will run in console windows instead of regular GUI windows. Most of the fields are ignored unless the values in dwFlags alert the system to use them. dwFlags may contain the values listed in Table 15.3, each value activating a different field or set of fields from the STARTUPINFO structure. You do not need to initialize any of these fields unless you activate them with a flag in dwFlags.
Table 15.3: dwFlag Values in CreateProcess
Flag | Field(s) Activated |
STARTF_USESHOWWINDOW | wShowWindow |
STARTF_USESIZE | dwXSize, dwYSize |
STARTF_USEPOSITION | dwX, dwY |
STARTF_USECOUNTCHARS | dwXCountChars, dwYCountChars |
STARTF_USEFILLATTRIBUTE | dwFillAttribute |
STARTF_USESTDHANDLES | hStdInput, hStdOutput, hStdError |
Additional values for dwFlags do not activate specific fields. The STARTF_FORCEONFEEDBACK and STARTF_FORCEOFFFEEDBACK values force the system to display or omit the waiting cursor that gives the user feedback as an application starts up.
I/O Handles The last three fields allow you to specify standard I/O handles for the child that differ from those of the parent. Normally, the child inherits whatever I/O devices the parent has. The I/O handles provide an easy way to pass the child any handles it needs to receive, such as one end of a pipe. If you use any of the fields, you should set values in all of them. The child receives an invalid handle for any device you leave NULL. Call GetStdHandle to copy any of the parent’s standard device handles into these fields.
NOTE
A process created with the DETACHED_PROCESS flag cannot inherit its parent’s standard I/O devices. The console program initialization procedures do not correctly receive the devices when the process has no console. Microsoft identified this limitation in an early release of Windows NT and added the handle fields as a work-around. They work for any child but are necessary for detached children.
CreateProcess returns TRUE if it succeeds in creating the new object and FALSE if an error occurs. If CreateProcess returns TRUE, it also returns information about the new process and its primary thread in the final parameter, the PROCESS_ INFORMATION structure.
// CreateProcess fills this out to tell you what it created
typedef struct _PROCESS_INFORMATION /* pi */
{
HANDLE hProcess; // handle to the new process
HANDLE hThread; // handle to its primary thread
DWORD dwProcessId; // number identifying new process
DWORD dwThreadId; // number identifying new thread
} PROCESS_INFORMATION;
If your program does not make use of the handles for the new process and its primary thread, you should close both right away. Otherwise, even if the PROCESS_INFORMATION structure is a local variable and goes out of scope, abandoning whatever it contained, the two object entries remain in your process’s object table and the system counts them as open handles.
Because the size of physical system memory limits the total number of processes that can be created, be sure to check for error returns. As an example, one of the early beta versions of Windows NT allowed a 16MB machine to create about 40 processes before failing for lack of memory. This limit will vary from version to version and machine to machine, but it is finite. Memory shortage can also cause CreateThread to fail, though threads consume significantly fewer resources than processes.
The spawn and exec functions in the C runtime library also create new processes. Internally, however, they map to the same Win32 CreateProcess call. Through its parameters, CreateProcess offers more ways to customize the new process than do the C functions. The C runtime functions _getenv and _putenv, which work with a process’s environment variables, also duplicate the Win32 API functions, GetEnvironmentVariable and PutEnvironmentVariable.