Every process is made up of blocks of code and data that are located within the processes own address space. The various resources created during a process are destroyed when the process is terminated.
A process, however, does not execute. A process owns memory, creates and manages handles, etc. but execution actually is performed by the thread. When a process starts, it has a single thread. This thread has a stack pointer and its own local variables, but the rest of its resources are owned by its process. Threads in a single process share almost everything.
Windows manages processes and threads as objects. The process and thread objects, like files, pipes, semaphores, etc., are owned by the kernel. These objects are created via win32 function calls. We then access these objects using handles, which are opaque references to the object. Windows will allocate a block of memory for the new object, initialize the memory, and then pass a handle back to the calling application. This handle is meaningful only to the process that called for it.
We can get a pseudo-handle to the current process using the GetCurrentProcess function. The pseudo-handle is currently defined as -1, and can be passed in to any function that needs a handle as a parameter. For example, we can pass the pseudo-handle returned from GetCurrentProcess in to the GetProcessId function to retrieve the process ID of our process.
Of course, we could also have used GetCurrentProcessId, which requires no arguments, but for illustrative purposes we will use GetProcessId.
The GetProcessHandleCount function returns the number of kernel handles in use by our process.
We call CreateProcess to start a new program. In the *nix systems, it is common to speak of parent and child processes, but these relationships are not as important in win32. A new process is virtually unrelated to its parent process. The CreateProcess function has a number of arguments. In fact, Createprocess has a whopping 10 arguments.
The lpszImageName and lpszCommandLine specify the executable program the command line arguments for that executable. Either lpszImageName or lpszCommandLine can be use to specify the executable name. We usually specify lpszCommandLine and leave lpszImageName set to NULL.
The lpsaProcess and lpsaThread arguments are pointers to the process and threa security structures. NULL values can be put
here to mark default security.
The fdwCreate parameter has a number of flags available, including CREATE_SUSPENDED, CREATE_NEW_PROCESS_GROUP, and DETACHED_PROCESS.
The lpszcurDir parameter specfies the drive and directory for the process. Supplying NULL uses the parent’s working directory.
The lppiProcInfo parameter specifies the PROCESS_INFORMATION structure that will contain the returned process.
Remember, the CreateProcess function only returns a value that tells us whether or not the process was created. The actual handle of a new process is returned in the PROCESS_INFORMATION structure.
Any application can open a Windows object, provided it has the right security. When a thread accesses a Windows object, Windows does not create another block of memory for the object. Instead, the system increments the object’s usage count and returns a handle to that object. When the application no longer needs the object, it should call the CloseHandle function. The Closehandle function causes the usage count for the object to be decremented by one. When the usage count reaches zero, Windows frees the memory allocated to the object.
A process is created when a thread callls the CreateProcess function. The first argument to CreateProcess, the lpszImageName parameter, is typically passed as NULL. When NULL is passed in for lpszImageName, the CreateProcess function assumes that the name of the executable file is contained in the next parameter, lpszCommandLine. This parameter specifies any command-line arguments to be passed to the process, and in the event that the first argument is NULL, it uses the first whitespace delimited string of text as the executable to be called. It is actually possible to get the command-line arguments from within the process using the GetCommandLine function.
The lpsaProcess and lpsaThread parameters identify the security attributes to be passed to the new process and thread objects. It is possible to set these parameters to NULL, accepting the system defaults. Or we can create our own SECURITY_ATTRIBTUES structure to assign our own custom values.
We can allocate our own SECURITY_ATTRIBUTES structure to assign security privileges to a process. When we initialize a SECURITY_ATTRIBUTES structure, we must set the nLength value to sizeof(SECURITY_ATTRIBUTES) The lpSeecurityDescriptor value is set using special functions such as SetSecurityDescriptorOwner. The finaly Boolean value, bInheritHandle, determines whether or not the process is inheritable.
The fdwCreate parameter identifies flags that decide how the process is created. For example, the CREATE_SUSPENDED flag causes the new process to be created with its primary thread in a suspended state. The CREATE_NEW_CONSOLE flag creates an new console for the new process. The lpvEnvironment parameter points to a block of memory contaiing environment strings to be used by the new process. Usually, we pass in NULL for this parameter. It is also possible to alter the environment variables for a process using the SetEnvironmentVariable function.
The lppiProcInfo parameter points to a PROCESS_INFORMATION structure that the CreateProcess function will fill before it returns.
When a new process is created, the system gives each process object and thread object an initial usage count of one. Before CreateProcess returns, it places handles for both the process and thread object into the PROCESS_INFORMATION structure. This raises the usage count for each to two. Therefore, before the process and thread objects may be freed, they must terminate and then the parent process must call CloseHandle to decrement the usage count to zero. To sum up, always be sure to close the handles to processes and threads.
Processes are assigned a unique identifier. The same is true for threads.
The WaitForSingleObject function waits until the specified object completes, or until a specified number of seconds have elapsed.