This exercise is designed to get you started as a Windows NT systems programmer. As usual, it’s not a bad idea at all to go through this with a friend, provided that when you have finished you have both spent some time at the keyboard exploring. You shouldn’t be able to break anything; if you manage it, blame Microsoft and come and see me to collect a small prize.
(you don’t need to type this in; cut and paste it from the web: http://www.doc.ic.ac.uk/~phjk/OperatingSystemsConcepts/Exercises/Software/ProcessCreator.cpp)
//
ProcessCreationExample.cpp - a simple example of process creation
// Paul Kelly,
Imperial College, October 2000
//
#include
<iostream.h>
#include
<windows.h>
// ProcessFactory:
allows us to set up the configuration options for a process then create it
//
class ProcessFactory
{
private:
STARTUPINFO startInfo; // configuration options for
new process
PROCESS_INFORMATION processInfo; // information about the
created process
public:
ProcessFactory(); // Constructor
~ProcessFactory(); // Destructor
void doCreateProcess(char
*CommandLine);
// could add methods here to set
configuration options
};
ProcessFactory::ProcessFactory() // Constructor
{
ZeroMemory(&startInfo,
sizeof(startInfo)); //
set all config options to default
startInfo.cb =
sizeof(startInfo); // tell OS how big it is
}
ProcessFactory::~ProcessFactory() //
Destructor
{
CloseHandle(&processInfo.hThread); // Release our references to
CloseHandle(&processInfo.hProcess); // information about the process
}
void ProcessFactory::doCreateProcess(char
*CommandLine)
{
int ret = CreateProcess(NULL,
CommandLine,
NULL,
NULL, FALSE, NULL, NULL, NULL, &startInfo, &processInfo);
if (!ret) {
cerr
<< "CreateProcess failed on error " <<
GetLastError() << " \n";
ExitProcess(1);
}
}
(Continued on next page)
int main()
{
int NChildProcesses;
cerr << "How many
processes should be created? ";
cin >> NChildProcesses;
cerr << "No of
child processes: " << NChildProcesses << "\n";
for (int i=0;
i<NChildProcesses && i<100; ++i) {
ProcessFactory
PF;
PF.doCreateProcess("c:\\WINNT\\SYSTEM32\\CALC.EXE");
}
return 0;
}
The Windows NT kernel is the heart of the operating system. When asked to run an application program, it creates a “virtual machine” for the program to run in – including a “virtual memory” for its code and data. It then creates a process to execute this code. Each process starts with just one “thread” of control; later, other threads can be created so that different activities within the same application can be interleaved. All the threads within a process share access to the same code and data.
“ProcessFactory” is an example of the “factory”design pattern (Gamma et al). A factory is a class whose purpose is to construct an object. Of course, in many cases you can do this with the normal C++ constructor. A factory abstracts the object creation process so we can control it while retaining a simple object creation interface. I used a factory here to encapsulate the two data structures associated with creating a process, and to provide a convenient way of setting the configuration options (“startInfo”) before calling the operating system’s “CreateProcess” function.
The CreateProcess() function is part of Microsoft’s Win32 Application Programming Interface (“API”). It is documented at http://msdn.microsoft.com/library/psdk/winbase/prothred_9dpv.htm - which is part of the Microsoft Developer’s Network website, http://msdn.microsoft.com/. The function prototype for CreateProcess() is:
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module
LPTSTR
lpCommandLine, //
command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL
bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID
lpEnvironment, //
new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO
lpStartupInfo, //
startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);
The field types (LPCTSTR, LPSECURITY_ATTRIBUTES etc etc) are #defined in the header file <windows.h>. In most cases they are simple C/C++ data types, but Microsoft may change their definition at some point, so use these macros. For this exercise, we don’t need most of these options, so most of the parameters to CreateProcess() are NULL. LpStartupInfo is a pointer to a further block of configuration options with the following structure (see MSDN again for more information):
typedef struct _STARTUPINFO
{
DWORD cb;
LPTSTR lpReserved;
LPTSTR lpDesktop;
LPTSTR lpTitle;
DWORD dwX; //
specify position of the new process’s window
DWORD dwY; //
(only used if dwFlags specifies
STARTF_USESIZE)
DWORD dwXSize; //
specify size of the new process’s window
DWORD dwYSize; // (only used if dwFlags specifies STARTF_USESIZE)
DWORD dwXCountChars; // specify no of columns in
console window
DWORD dwYCountChars; // (only used if dwFlags specifies STARTF_USECOUNTCHARS)
DWORD dwFillAttribute;
DWORD dwFlags; //
bit field determines which fields used when process creates window.
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput; //
a handle for manipulating process’s standard input stream
HANDLE hStdOutput; //
a handle for manipulating process’s standard output stream
HANDLE hStdError; //
a handle for manipulating process’s error output stream
} STARTUPINFO, *LPSTARTUPINFO;
Again, we don’t need to set any of these parameters in this simple example, so we set them all to zero (the ZeroMemory() call above).
The PROCESS_INFORMATION
structure is filled in by the CreateProcess function with information about a
newly created process and its primary thread.
Here is its structure (see MSDN again for more information):
typedef struct
_PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
The CreateProcess() function gets the operating system to create a process (consisting of a virtual machine, with a virtual address space preconstructed to contain the program code you specified, together with its initialized data, and it working memory space). hProcess is a handle to the newly created process. The handle is used to specify the process in all functions that perform operations on the process object. The operating system also creates a thread to run in that process. hThread is a handle to the primary thread of the newly created process. The handle is used to specify the thread in all functions that perform operations on the thread object. dwProcessId is a global process identifier number that can be used to identify a process (for example using taskmgr). dwThreadId is a global thread identifier number that can be used to identify the thread.
Gary Nutt, Operating Systems Projects using Windows NT (Addison Wesley1999)
Gives brief and very useful intro to NT’s internal structure
David A Solomon, Inside Windows NT (Second edition, Microsoft Press 1998)
The most widely-recommended source for understanding how NT works
Microsoft, Windows NT Workstation Resource Kit (Microsoft Press, 1996)
Far from brief but often useful to fill in gaps and provide details.
Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. October 1994. The definitive (“gang of four”) text on design patterns.
Microsoft Developers’ Network (MSDN), URL: http://msdn.microsoft.com/