The CreateProcess Function

[Previous] [Next]

You create a process with the CreateProcess function:

 BOOL CreateProcess( PCTSTR pszApplicationName, PTSTR pszCommandLine, PSECURITY_ATTRIBUTES psaProcess, PSECURITY_ATTRIBUTES psaThread, BOOL bInheritHandles, DWORD fdwCreate, PVOID pvEnvironment, PCTSTR pszCurDir, PSTARTUPINFO psiStartInfo, PPROCESS_INFORMATION ppiProcInfo); 

When a thread calls CreateProcess, the system creates a process kernel object with an initial usage count of 1. This process kernel object is not the process itself but a small data structure that the operating system uses to manage the process—you can think of the process kernel object as a small data structure that consists of statistical information about the process. The system then creates a virtual address space for the new process and loads the code and data for the executable file and any required DLLs into the process's address space.

The system then creates a thread kernel object (with a usage count of 1) for the new process's primary thread. Like the process kernel object, the thread kernel object is a small data structure that the operating system uses to manage the thread. This primary thread begins by executing the C/C++ run-time startup code, which eventually calls your WinMain, wWinMain, main, or wmain function. If the system successfully creates the new process and primary thread, CreateProcess returns TRUE.

NOTE
CreateProcess returns TRUE before the process has fully initialized. This means that the operating system loader has not attempted to locate all the required DLLs yet. If a DLL can't be located or fails to initialize correctly, the process is terminated. Since CreateProcess returned TRUE, the parent process is not aware of any initialization problems.

OK, that's the broad overview. The following sections dissect each of CreateProcess's parameters.

pszApplicationName and pszCommandLine

The pszApplicationName and pszCommandLine parameters specify the name of the executable file the new process will use and the command-line string that will be passed to the new process, respectively. Let's talk about the pszCommandLine parameter first.

NOTE

Notice that the pszCommandLine parameter is prototyped as a PTSTR. This means that CreateProcess expects that you are passing the address of a non-constant string. Internally, CreateProcess actually does modify the command-line string that you pass to it. But before CreateProcess returns, it restores the string to its original form.

This is important because an access violation will occur if your command-line string is contained in a read-only portion of your file image. For example, the following code causes an access violation because Visual C++ 6.0 places the "NOTEPAD" string in read-only memory:

 STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess(NULL, TEXT("NOTEPAD"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 

When CreateProcess attempts to modify the string, an access violation occurs. (Earlier versions of Visual C++ placed the string in read/write memory so calls to CreateProcess did not cause access violations.)

The best way to solve this problem is to copy the constant string to a temporary buffer before calling CreateProcess as follows:

 STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; TCHAR szCommandLine[] = TEXT("NOTEPAD"); CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); 

You might also look into using Visual C++'s /Gf and /GF compiler switches, which control the elimination of duplicate strings and determine whether those strings are placed in a read-only section. (Also note that the /ZI switch, which allows the use of Visual Studio's Edit & Continue debugging feature, implies the /GF switch.) The best thing you can do is to use the /GF compiler switch and a temporary buffer. The best thing Microsoft can do is fix CreateProcess so that it takes over the responsibility of making a temporary copy of the string so we don't have to do it. Maybe this will happen in a future version of Windows.

By the way, if you are calling the ANSI version of CreateProcess on Windows 2000, you will not get an access violation because a temporary copy of the command-line string is made. (For more information about this, see Chapter 2.)

You use the pszCommandLine parameter to specify a complete command line that CreateProcess uses to create the new process. When CreateProcess parses the pszCommandLine string, it examines the first token in the string and assumes that this token is the name of the executable file you want to run. If the executable file's name does not have an extension, an .exe extension is assumed. CreateProcess also searches for the executable in the following order:

  1. The directory containing the .exe file of the calling process
  2. The current directory of the calling process
  3. The Windows system directory
  4. The Windows directory
  5. The directories listed in the PATH environment variable

Of course, if the filename includes a full path, the system looks for the executable using the full path and does not search the directories. If the system finds the executable file, it creates a new process and maps the executable's code and data into the new process's address space. The system then calls the C/C++ run-time startup routine. As noted earlier, the C/C++ run-time startup routine examines the process's command line and passes the address to the first argument after the executable file's name as (w)WinMain's pszCmdLine parameter.

All of this happens as long as the pszApplicationName parameter is NULL (which should be the case more than 99 percent of the time). Instead of passing NULL, you can pass the address to a string containing the name of the executable file you want to run in the pszApplicationName parameter. Note that you must specify the file's extension; the system will not automatically assume that the filename has an .exe extension. CreateProcess assumes that the file is in the current directory unless a path precedes the filename. If the file can't be found in the current directory, CreateProcess doesn't look for the file in any other directory—it simply fails.

Even if you specify a filename in the pszApplicationName parameter, however, CreateProcess passes the contents of the pszCommandLine parameter to the new process as its command line. For example, say that you call CreateProcess like this:

 // Make sure that the path is in a read/write section of memory. TCHAR szPath[] = TEXT("WORDPAD README.TXT"); // Spawn the new process. CreateProcess(TEXT("C:\\WINNT\\SYSTEM32\\NOTEPAD.EXE"),szPath,...); 

The system invokes the Notepad application, but Notepad's command line is WORDPAD README.TXT. This quirk is certainly a little strange, but that's how CreateProcess works. This capability provided by the pszApplicationName parameter was actually added to CreateProcess to support Windows 2000's POSIX subsystem.

psaProcess, psaThread, and bInheritHandles

To create a new process, the system must create a process kernel object and a thread kernel object (for the process's primary thread). Because these are kernel objects, the parent process gets the opportunity to associate security attributes with these two objects. You use the psaProcess and psaThread parameters to specify the desired security for the process object and the thread object, respectively. You can pass NULL for these parameters, in which case the system gives these objects default security descriptors. Or you can allocate and initialize two SECURITY_ATTRIBUTES structures to create and assign your own security privileges to the process and thread objects.

Another reason to use SECURITY_ATTRIBUTES structures for the psaProcess and psaThread parameters is if you want either of these two object handles to be inheritable by any child processes spawned in the future by this parent process. (I discussed the theory behind kernel object handle inheritance in Chapter 3.)

Figure 4-2 is a short program that demonstrates kernel object handle inheritance. Let's say that Process A creates Process B by calling CreateProcess and passing the address of a SECURITY_ATTRIBUTES structure for the psaProcess parameter in which the bInheritHandle member is set to TRUE. In this same call, the psaThread parameter points to another SECURITY_ATTRIBUTES structure in which its bInheritHandle member is set to FALSE.

When the system creates Process B, it allocates both a process kernel object and a thread kernel object and returns handles back to Process A in the structure pointed to by the ppiProcInfo parameter (discussed shortly). Process A can now manipulate the newly created process object and thread object by using these handles.

Now let's say that Process A will call CreateProcess a second time to create Process C. Process A can decide whether to grant Process C the ability to manipulate some of the kernel objects that Process A has access to. The bInheritHandles parameter is used for this purpose. If bInheritHandles is set to TRUE, the system causes Process C to inherit any inheritable handles in Process A. In this case, the handle to Process B's process object is inheritable. The handle to Process B's primary thread object is not inherited no matter what the value of the bInheritHandles parameter to CreateProcess is. Also, if Process A calls CreateProcess, passing FALSE for the bInheritHandles parameter, Process C does not inherit any of the handles currently used by Process A.

Figure 4-2. An example of kernel object handle inheritance

Inherit.c

 /************************************************************ Module name: Inherit.c Notices: Copyright (c) 2000 Jeffrey Richter ************************************************************/ #include <Windows.h> int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE, PSTR pszCmdLine, int nCmdShow) { // Prepare a STARTUPINFO structure for spawning processes. STARTUPINFO si = { sizeof(si) }; SECURITY_ATTRIBUTES saProcess, saThread; PROCESS_INFORMATION piProcessB, piProcessC; TCHAR szPath[MAX_PATH]; // Prepare to spawn Process B from Process A. // The handle identifying the new process // object should be inheritable. saProcess.nLength = sizeof(saProcess); saProcess.lpSecurityDescriptor = NULL; saProcess.bInheritHandle = TRUE; // The handle identifying the new thread // object should NOT be inheritable. saThread.nLength = sizeof(saThread); saThread.lpSecurityDescriptor = NULL; saThread.bInheritHandle = FALSE; // Spawn Process B. lstrcpy(szPath, TEXT("ProcessB")); CreateProcess(NULL, szPath, &saProcess, &saThread, FALSE, 0, NULL, NULL, &si, &piProcessB); // The pi structure contains two handles // relative to Process A: // hProcess, which identifies Process B's process // object and is inheritable; and hThread, which identifies // Process B's primary thread object and is NOT inheritable. // Prepare to spawn Process C from Process A. // Since NULL is passed for the psaProcess and psaThread // parameters, the handles to Process C's process and // primary thread objects default to "noninheritable." // If Process A were to spawn another process, this new // process would NOT inherit handles to Process C's process // and thread objects. // Because TRUE is passed for the bInheritHandles parameter, // Process C will inherit the handle that identifies Process // B's process object but will not inherit a handle to // Process B's primary thread object. lstrcpy(szPath, TEXT("ProcessC")); CreateProcess(NULL, szPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &piProcessC); return(0); } 

fdwCreate

The fdwCreate parameter identifies flags that affect how the new process is created. You can specify multiple flags if you combine them with the bitwise OR operator.

  • The DEBUG_PROCESS flag tells the system that the parent process wants to debug the child process and any processes spawned by the child process in the future. This flag tells the system to notify the parent process (now the debugger) when certain events occur in any of the child processes (the debuggees).
  • The DEBUG_ONLY_THIS_PROCESS flag is similar to DEBUG_PROCESS except that the debugger is notified only of special events occurring in the immediate child process. If the child process spawns any additional processes, the debugger is not notified of events in these processes.
  • The CREATE_SUSPENDED flag causes the new process to be created, but its primary thread is suspended. This allows the parent process to modify memory in the child process's address space, alter the child process's primary thread's priority, or add the process to a job before the process has had a chance to execute any code. Once the parent process has modified the child process, the parent process allows the child process to execute code by calling the ResumeThread function (discussed in Chapter 7).
  • The DETACHED_PROCESS flag blocks a CUI-based process's access to its parent's console window and tells the system to send its output to a new console window. If a CUI-based process is created by another CUI-based process, the new process will, by default, use the parent's console window. (When you run the C compiler from the command shell, a new console window isn't created; the output is simply appended to the bottom of the existing console window.) By specifying this flag, the new process will send its output to a new console window.
  • The CREATE_NEW_CONSOLE flag tells the system to create a new console window for the new process. Specifying both the CREATE_NEW_CONSOLE and DETACHED_PROCESS flags results in an error.
  • The CREATE_NO_WINDOW flag tells the system not to create any console window for the application. You can use this flag to execute a console application without a user interface.
  • The CREATE_NEW_PROCESS_GROUP flag modifies the list of processes that are notified when the user presses the Ctrl+C or Ctrl+Break keys. If you have several CUI-based processes running when the user presses one of these key combinations, the system notifies all the processes in a process group that the user wants to break out of the current operation. By specifying this flag when creating a new CUI-based process, you create a new process group. If the user presses Ctrl+C or Ctrl+Break while a process in this group is active, the system notifies only processes in this group of the user's request.
  • The CREATE_DEFAULT_ERROR_MODE flag tells the system that the new process should not inherit the error mode used by the parent process. (See the SetErrorMode function discussion earlier in this chapter.)
  • The CREATE_SEPARATE_WOW_VDM flag is useful only when you invoke a 16-bit Windows application on Windows 2000. It tells the system to create a separate Virtual DOS Machine (VDM) and run the 16-bit Windows application in this VDM. By default, all 16bit Windows applications execute in a single shared VDM. The advantage of running an application in a separate VDM is that if the application crashes, it kills only the single VDM; any other programs running in distinct VDMs continue to function normally. Also, 16bit Windows applications that run in separate VDMs have separate input queues. This means that if one application hangs momentarily, applications in separate VDMs continue to receive input. The disadvantage of running multiple VDMs is that each VDM consumes a significant amount of physical storage. Windows 98 runs all 16-bit Windows applications in a single virtual machine—you cannot override this.
  • The CREATE_SHARED_WOW_VDM flag is useful only when you invoke a 16-bit Windows application on Windows 2000. By default, all 16-bit Windows applications run in a single VDM unless the CREATE_SEPARATE_WOW_VDM flag is specified. However, you can override this default behavior by setting the DefaultSeparate VDM value in the registry under HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\WOW to yes. The CREATE_SHARED_WOW_VDM flag then runs the 16-bit Windows application in the system's shared VDM. (You must reboot after changing this registry setting.)
  • The CREATE_UNICODE_ENVIRONMENT flag tells the system that the child process's environment block should contain Unicode characters. By default, a process's environment block contains ANSI strings.
  • The CREATE_FORCEDOS flag forces the system to run the MSDOS application that is embedded inside a 16-bit OS/2 application.
  • The CREATE_BREAKAWAY_FROM_JOB flag allows a process in a job to spawn a new process that is disassociated from the job. (See Chapter 5 for more information.)

The fdwCreate parameter also allows you to specify a priority class. However, you don't have to do this, and for most applications you shouldn't—the system will assign a default priority class to the new process. The following table shows the possible priority classes.

Priority Class Flag Identifier
Idle IDLE_PRIORITY_CLASS
Below normal BELOW_NORMAL_PRIORITY_CLASS
Normal NORMAL_PRIORITY_CLASS
Above normal ABOVE_NORMAL_PRIORITY_CLASS
High HIGH_PRIORITY_CLASS
Realtime REALTIME_PRIORITY_CLASS

These priority classes affect how the threads contained within the process are scheduled with respect to other processes' threads. See the section titled "An Abstract View of Priorities" in Chapter 7 for more information.

NOTE
The BELOW_NORMAL_PRIORITY_CLASS and ABOVE_NORMAL_PRIORITY_CLASS priority classes are new in Windows 2000; they are not supported on Windows NT 4 (or earlier), Windows 95, or Windows 98.

pvEnvironment

The pvEnvironment parameter points to a block of memory that contains environment strings that the new process will use. Most of the time, NULL is passed for this parameter, causing the child process to inherit the set of environment strings that its parent is using. Alternatively, you can use the GetEnvironmentStrings function:

 PVOID GetEnvironmentStrings(); 

This function gets the address of the environment string data block that the calling process is using. You can use the address returned by this function as the pvEnvironment parameter of CreateProcess. This is exactly what CreateProcess does if you pass NULL for the pvEnvironment parameter. When you no longer need this block of memory, you should free it by calling FreeEnvironmentStrings:

 BOOL FreeEnvironmentStrings(PTSTR pszEnvironmentBlock); 

pszCurDir

The pszCurDir parameter allows the parent process to set the child process's current drive and directory. If this parameter is NULL, the new process's working directory will be the same as that of the application spawning the new process. If this parameter is not NULL, pszCurDir must point to a zero-terminated string containing the desired working drive and directory. Notice that you must specify a drive letter in the path.

psiStartInfo

The psiStartInfo parameter points to a STARTUPINFO structure:

 typedef struct _STARTUPINFO { DWORD cb; PSTR lpReserved; PSTR lpDesktop; PSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; PBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; 

Windows uses the members of this structure when it creates the new process. Most applications will want the spawned application simply to use default values. At a minimum, you should initialize all the members in this structure to zero and then set the cb member to the size of the structure:

 STARTUPINFO si = { sizeof(si) }; CreateProcess(..., &si, ...); 

If you fail to zero the contents of the structure, the members will contain whatever garbage is on the calling thread's stack. Passing this garbage to CreateProcess means that sometimes the new process will be created and sometimes it won't, depending on the garbage. It is important to set the unused members of this structure to zero so that CreateProcess will work consistently. Failing to do so is one of the most common mistakes I see developers make.

Now, if you want to initialize some of the members of the structure, you simply do so before the call to CreateProcess. We'll discuss each member in turn. Some members are meaningful only if the child application creates an overlapped window; others are meaningful only if the child performs CUI-based input and output. Table 4-3 describes the usefulness of each member.

Table 4-3. The members of the STARTUPINFO structure

Member Window, Console, or Both Purpose
cb Both Contains the number of bytes in the STARTUPINFO structure. Acts as a version control in case Microsoft expands this structure in the future. Your application must initialize cb to sizeof(STARTUPINFO).
lpReserved Both Reserved. Must be initialized to NULL.
lpDesktop Both Identifies the name of the desktop on which to start the application. If the desktop exists, the new process is associated with the specified desktop. If the desktop does not exist, a desktop with default attributes is created with the specified name for the new process. If lpDesktop is NULL (which is most common), the process is associated with the current desktop.
lpTitle Console Specifies the window title for a console window. If lpTitle is NULL, the name of the executable file is used as the window title.
dwX
dwY
Both Specify the x and y coordinates (in pixels) of the location where the application's window should be placed on the screen. These coordinates are used only if the child process creates its first overlapped window with CW_USEDEFAULT as the x parameter of CreateWindow. For applications that create console windows, these members indicate the upper left corner of the console window.
dwXSize Both Specify the width and height (in pixels) of an dwYSize application's window. These values are used only if the child process creates its first overlapped window with CW_USEDEFAULT as the nWidth parameter of CreateWindow. For applications that create console windows, these members indicate the width and height of the console window.
dwXCountChars
dwYCountChars
Console Specify the width and height (in characters) of a child's console windows.
dwFillAttribute Console Specifies the text and background colors used by a child's console window.
dwFlags Both See the following section and the table below.
wShowWindow Window Specifies how the child's first overlapped window should appear if the application's first call to ShowWindow passes SW_SHOWDEFAULT as the nCmdShow parameter. This member can be any of the SW_* identifiers normally used with the ShowWindow function.
cbReserved2 Both Reserved. Must be initialized to 0.
lpReserved2 Both Reserved. Must be initialized to NULL.
hStdInput
hStdOutput
hStdError
Console Specify handles to buffers for console input and output. By default, the hStdInput identifies a keyboard buffer; hStdError hStdOutput and identify a console window's buffer.

Now, as promised, I'll discuss the dwFlags member. This member contains a set of flags that modify how the child process is to be created. Most of the flags simply tell CreateProcess whether other members of the STARTUPINFO structure contain useful information or whether some of the members should be ignored. The following table shows the list of possible flags and their meanings.

Flag Meaning
STARTF_USESIZE Use the dwXSize and dwYSize members.
STARTF_USESHOWWINDOW Use the wShowWindow member.
STARTF_USEPOSITION Use the dwX and dwY members.
STARTF_USECOUNTCHARS Use the dwXCountChars and dwYCountChars members.
STARTF_USEFILLATTRIBUTE Use the dwFillAttribute member.
STARTF_USESTDHANDLES Use the hStdInput, hStdOutput, and hStdError members.
STARTF_RUN_FULLSCREEN Forces a console application running on an x86 computer to start in full-screen mode.

Two additional flags, STARTF_FORCEONFEEDBACK and STARTF_FORCEOFFFEEDBACK, give you control over the mouse cursor when you invoke a new process. Because Windows supports true preemptive multitasking, you can invoke an application and, while the process is initializing, use another program. To give visual feedback to the user, CreateProcess temporarily changes the system's arrow cursor to a new cursor called a start glass:

This cursor indicates that you can wait for something to happen or you can continue to use the system. The CreateProcess function gives you more control over the cursor when invoking another process. When you specify the STARTF_FORCEOFFFEEDBACK flag, CreateProcess does not change the cursor into the start glass.

STARTF_FORCEONFEEDBACK causes CreateProcess to monitor the new process's initialization and to alter the cursor based on the result. When CreateProcess is called with this flag, the cursor changes into the start glass. If, after two seconds, the new process does not make a GUI call, CreateProcess resets the cursor to an arrow.

If the process makes a GUI call within two seconds, CreateProcess waits for the application to show a window. This must occur within five seconds after the process makes the GUI call. If a window is not displayed, CreateProcess resets the cursor. If a window is displayed, CreateProcess keeps the start glass cursor on for another five seconds. If at any time the application calls the GetMessage function, indicating that it is finished initializing, CreateProcess immediately resets the cursor and stops monitoring the new process.

Before concluding this section, I'd like to mention STARTUPINFO's wShowWindow member. You initialize this member to the value that is passed to (w)WinMain's last parameter, nCmdShow. This member indicates the value you want passed to the new process's (w)WinMain function's last parameter, nCmdShow. It is one of the identifiers that can be passed to the ShowWindow function. Usually, nCmdShow's value is either SW_SHOWNORMAL or SW_SHOWMINNOACTIVE. However, it can sometimes be SW_SHOWDEFAULT.

When you invoke an application from the Explorer, the application's (w)WinMain function is called with SW_SHOWNORMAL passed as the nCmdShow parameter. If you create a shortcut for the application, you can use the shortcut's property page to tell the system how the application's window should first appear. Figure 4-3 shows the property page for a shortcut that runs Notepad. Notice that the Run option's combo box allows you to specify how Notepad's window is displayed.

Figure 4-3. The property page for a shortcut that runs Notepad

When you use Explorer to invoke this shortcut, Explorer prepares the STARTUPINFO structure properly and calls CreateProcess. Notepad executes and its (w)WinMain function is passed SW_SHOWMINNOACTIVE for the nCmdShow parameter.

In this way, the user can easily start an application with its main window showing in the normal state, minimized state, or maximized state.

Finally, an application can call the following function to obtain a copy of the STARTUPINFO structure that was initialized by the parent process. The child process can examine this structure and alter its behavior based on the values of the structure's members.

 VOID GetStartupInfo(LPSTARTUPINFO pStartupInfo); 

NOTE
Although the Windows documentation does not explicitly say so, you must initialize the cb member of the structure before calling GetStartupInfo as follows:

 STARTUPINFO si = { sizeof(si) }; GetStartupInfo(&si);  

ppiProcInfo

The ppiProcInfo parameter points to a PROCESS_INFORMATION structure that you must allocate; CreateProcess initializes the members of this structure before it returns. The structure appears as follows:

 typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION; 

As already mentioned, creating a new process causes the system to create a process kernel object and a thread kernel object. At creation time, the system gives each object an initial usage count of 1. Then, just before CreateProcess returns, the function opens the process object and the thread object and places the process-relative handles for each in the hProcess and hThread members of the PROCESS_INFORMATION structure. When CreateProcess opens these objects internally, the usage count for each becomes 2.

This means that before the system can free the process object, the process must terminate (decrementing the usage count by 1) and the parent process must call CloseHandle (decrementing the usage count again by 1 making it 0). Similarly, to free the thread object, the thread must terminate and the parent process must close the handle to the thread object. (See the "Child Processes" section at the end of this chapter for more information about freeing thread objects.)

NOTE

You must close the handles to the child process and its primary thread to avoid resource leaks while your application is running. Of course, the system will clean up these leaks automatically when your process terminates, but well-written software explicitly closes these handles (by calling the CloseHandle function) when the process no longer needs to access the child process and its primary thread. Failure to close these handles is one of the most common mistakes developers make.

For some reason, many developers believe that closing the handle to a process or thread forces the system to kill that process or thread. This is absolutely not true. Closing the handle simply tells the system that you are not interested in the process or thread's statistical data. The process or thread will continue to execute until it terminates on its own.

When a process kernel object is created, the system assigns the object a unique identifier; no other process kernel object in the system will have the same ID number. The same is true for thread kernel objects. When a thread kernel object is created, the object is assigned a unique, system-wide ID number. Process IDs and thread IDs share the same number pool. This means that it is impossible for a process and a thread to have the same ID. In addition, an object is never assigned an ID of 0. Before CreateProcess returns, it fills the dwProcessId and dwThreadId members of the PROCESS_INFORMATION structure with these IDs. IDs simply make it easy for you to identify the processes and threads in the system. IDs are mostly used by utility applications (such as the Task Manager) and rarely by productivity applications. For this reason, most applications ignore IDs altogether.

If your application uses IDs to track processes and threads, you must be aware that the system reuses process and thread IDs immediately. For example, let's say that when a process is created, the system allocates a process object and assigns it the ID value 122. If a new process object is created, the system doesn't assign the same ID number. However, if the first process object is freed, the system might assign 122 to the next process object created. Keep this in mind so you avoid writing code that references an incorrect process object or thread. It's easy to acquire a process ID and save the ID; but the next thing you know, the process identified by the ID is freed and a new process is created and given the same ID. When you use the saved process ID, you end up manipulating the new process, not the process whose ID you originally acquired.

Occasionally, you'll work on an application that wants to determine its parent process. The first thing you should know is that a parent-child relationship exists between processes only at the time when the child is spawned. Just before the child process begins executing code, Windows does not consider a parent-child relationship to exist anymore. Earlier versions of Windows didn't offer functions that allowed a process to query its parent process. The ToolHelp functions now make this possible via the PROCESSENTRY32 structure. Inside this structure is a th32ParentProcessID member that the documentation claims will return the ID of the process's parent.

The system does remember the ID of each process's parent process, but since IDs are immediately reused, by the time you get your parent process's ID, that ID might identify a completely different process running in the system. Your parent process will probably have terminated. If your application needs to communicate with its "creator," you are better off not using IDs; instead, you should define a more persistent mechanism to communicate—kernel objects, window handles, and so forth.

The only way to guarantee that a process or thread ID isn't reused is to make sure that the process or thread kernel object doesn't get destroyed. If you have just created a new process or thread, you can do this simply by not closing the handles to these objects. Then, once your application has finished using the ID, call CloseHandle to release the kernel object(s) and remember that it is no longer safe for you to use or rely on the process ID. If you are the child process, you can do nothing to ensure the validity of your parent's process or thread IDs unless the parent process duplicates handles for its own process or thread objects and allows you, the child process, to inherit these handles.



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net