Processes
Windows CE treats processes differently than does Windows XP. First and foremost, Windows CE has the aforementioned system limit of 32 processes being run at any one time. When the system starts, at least four processes are created: NK.exe, which provides the kernel services; FileSys.exe, which provides file system services; GWES.exe, which provides the GUI support; and Device.exe, which loads and maintains the device drivers for the system. On most systems, other processes are also started, such as the shell, Explorer.exe, and, if the system is connected to a PC, Repllog.exe and RAPISrv.exe, which service the link between the PC and the Windows CE system. This leaves room for about 24 processes that the user or other applications that are running can start. While this sounds like a harsh limit, most systems don't need that many processes. A typical Pocket PC that's being used heavily might have 15 processes running at any one time.Windows CE diverges from its desktop counterparts in other ways. Compared with processes under Windows XP, Windows CE processes contain much less state information. Since Windows CE doesn't support the concept of a current directory, the individual processes don't need to store that information. Windows CE doesn't maintain a set of environment variables, so processes don't need to keep an environment block. Windows CE doesn't support handle inheritance, so there's no need to tell a process to enable handle inheritance. Because of all this, the parameter-heavy CreateProcess function is passed mainly NULLs and zeros, with just a few parameters actually used by Windows CE.Many of the process and thread-related functions are simply not supported by Windows CE because the system doesn't support certain features supported by Windows XP. Since Windows CE doesn't support an environment, all the Win32 functions dealing with the environment don't exist in Windows CE. Some functions aren't supported because there's an easy way to work around the lack of the function. For example, ExitProcess doesn't exist under Windows CE. But as you might expect, there's a workaround that allows a process to close.Enough of what Windows CE doesn't do; let's look at what you can do with Windows CE.
Creating a Process
The function for creating another process is
BOOL CreateProcess (LPCTSTR lpApplicationName,
LPCTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles, DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
While the list of parameters looks daunting, most of the parameters must be set to NULL or 0 because Windows CE doesn't support security or current directories, nor does it handle inheritance. This results in a function prototype that looks more like this:
BOOL CreateProcess (LPCTSTR lpApplicationName,
LPTSTR lpCommandLine, NULL, NULL, FALSE,
DWORD dwCreationFlags, NULL, NULL, NULL,
LPPROCESS_INFORMATION lpProcessInformation);
The parameters that remain start with a pointer to the name of the application to launch. Windows CE looks for the application in the following directories, in this order:
The path, if any, specified in lpApplicationName
The Windows directory, (\Windows)
The root directory in the object store, (\)
The directories in the path specified in the SystemPath value in [HKEY_LOCAL_MACHINE]\Loader
This action is different from Windows XP, where CreateProcess searches for the executable only if lpApplicationName is set to NULL and the executable name is passed through the lpCommandLine parameter. In the case of Windows CE, the application name must be passed in the lpApplicationName parameter because Windows CE doesn't support the technique of passing a NULL in lpApplicationName with the application name as the first token in the lpCommandLine parameter.The lpCommandLine parameter specifies the command line that will be passed to the new process. The only difference between Windows CE and Windows XP in this parameter is that under Windows CE the command line is always passed as a Unicode string. And as I mentioned previously, you can't pass the name of the executable as the first token in lpCommandLine.The dwCreationFlags parameter specifies the initial state of the process after it has been loaded. Windows CE limits the allowable flags to the following:
0Creates a standard process.
CREATE_SUSPENDED Creates the process and then suspends the primary thread.
DEBUG_PROCESS The process being created is treated as a process being debugged by the caller. The calling process receives debug information from the process being launched.
DEBUG_ONLY_THIS_PROCESS When combined with DEBUG_ PROCESS, debugs a process but doesn't debug any child processes that are launched by the process being debugged.
CREATE_NEW_CONSOLE Forces a new console to be created.
The only other parameter of the CreateProcess function that Windows CE uses is lpProcessInformation. This parameter can be set to NULL, or it can point to a PROCESS_INFORMATION structure that's filled by CreateProcess with information about the new process. The PROCESS_INFORMATION structure is defined this way:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
The first two fields in this structure are filled with the handles of the new process and the handle of the primary thread of the new process. These handles are useful for monitoring the newly created process, but with them comes some responsibility. When the system copies the handles for use in the PROCESS_INFORMATION structure, it increments the use count for the handles. This means that if you don't have any use for the handles, the calling process must close them. Ideally, they should be closed immediately following a successful call to CreateProcess. I'll describe some good uses for these handles later in this chapter, in the section "Synchronization."The other two fields in the PROCESS_INFORMATION structure are filled with the process ID and primary thread ID of the new process. These ID values aren't handles but simply unique identifiers that can be passed to Windows functions to identify the target of the function. Be careful when using these IDs. If the new process terminates and another new one is created, the system can reuse the old ID values. You must take measures to assure that ID values for other processes are still identifying the process you're interested in before using them. For example, you can, by using synchronization objects, be notified when a process terminates. When the process terminated, you would then know not to use the ID values for that process.Using the create process is simple, as you can see in the following code fragment:
TCHAR szFileName[MAX_PATH];
TCHAR szCmdLine[64];
DWORD dwCreationFlags;
PROCESS_INFORMATION pi;
int rc;
lstrcpy (szFileName, TEXT ("calc"));
lstrcpy (szCmdLine, TEXT ("));
dwCreationFlags = 0;
rc = CreateProcess (szFileName, szCmdLine, NULL, NULL, FALSE,
dwCreationFlags, NULL, NULL, NULL, &pi);
if (rc) {
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
}
This code launches the standard Calculator applet found on the Pocket PC. Since the filename doesn't specify a path, CreateProcess will, using the standard Windows CE search path, find Calc.exe in the \Windows directory. Because I didn't pass a command line to Calc, I could have simply passed a NULL value in the lpCmdLine parameter. But I passed a null string in szCmdLine to differentiate the lpCmdLine parameter from the many other parameters in CreateProcess that aren't used. I used the same technique for dwCreationFlags. If the call to CreateProcess is successful, it returns a nonzero value. The code above checks for this and, if the call was successful, closes the process and thread handles returned in the PROCESS_INFORMATION structure. Remember that this must be done by all Win32 applications to prevent memory leaks.
Terminating a Process
A process can terminate itself by simply returning from the WinMain procedure. For console applications, a simple return from main suffices. Windows CE doesn't support the ExitProcess function found in Windows XP. Instead, you can have the primary thread of the process call ExitThread. Under Windows CE, if the primary thread terminates, the process is terminated as well, regardless of what other threads are currently active in the process. The exit code of the process will be the exit code provided by ExitThread. You can determine the exit code of a process by calling
BOOL GetExitCodeProcess (HANDLE hProcess, LPDWORD lpExitCode);
The parameters are the handle to the process and a pointer to a DWORD that receives the exit code that was returned by the terminating process. If the process is still running, the return code is the constant STILL_ACTIVE.You can terminate another process. But while it's possible to do that, you shouldn't be in the business of closing other processes. The user might not be expecting that process to be closed without his or her consent. If you need to terminate a process (or close a process, which is the same thing but a much nicer word), the following methods can be used.If the process to be closed is one that you created, you can use some sort of interprocess communication to tell the process to terminate itself. This is the most advisable method because you've designed the target process to be closed by another party. Another method of closing a process is to send the main window of the process a WM_CLOSE message. This is especially effective on the Pocket PC, where applications are designed to respond to WM_CLOSE messages by quietly saving their state and closing. Finally, if all else fails and you absolutely must close another process, you can use TerminateProcess.TerminateProcess is prototyped as
BOOL TerminateProcess (HANDLE hProcess, DWORD uExitCode);
The two parameters are the handle of the process to terminate and the exit code the terminating process will return.
Other Processes
Of course, to terminate another process, you've got to know the handle to that process. You might want to know the handle to a process for other reasons as well. For example, you might want to know when the process terminates. Windows CE supports two additional functions that come in handy here (both of which are seldom discussed). The first function is OpenProcess, which returns the handle of an already running process. OpenProcess is prototyped as
HANDLE OpenProcess (DWORD dwDesiredAccess, BOOL bInheritHandle,
DWORD dwProcessId);
Under Windows CE, the first parameter isn't used and should be set to 0. The bInheritHandle parameter must be set to FALSE because Windows CE doesn't support handle inheritance. The final parameter is the process ID value of the process you want to open.The other function useful in this circumstance is
DWORD GetWindowThreadProcessId (HWND hWnd, LPDWORD lpdwProcessId);
This function takes a handle to a window and returns the process ID for the process that created the window. So using these two functions, you can trace a window back to the process that created it.Two other functions allow you to directly read from and write to the memory space of another process. These functions are
BOOL ReadProcessMemory (HANDLE hProcess, LPCVOID lpBaseAddress,
LPVOID lpBuffer, DWORD nSize,
LPDWORD lpNumberOfBytesRead);
and
BOOL WriteProcessMemory (HANDLE hProcess, LPVOID lpBaseAddress,
LPVOID lpBuffer, DWORD nSize,
LPDWORD lpNumberOfBytesWritten);
The parameters for these functions are fairly self-explanatory. The first parameter is the handle of the remote process. The second parameter is the base address in the other process's address space of the area to be read or written. The third and fourth parameters specify the name and the size of the local buffer in which the data is to be read from or written to. Finally, the last parameter specifies the bytes actually read or written. Both functions require that the entire area being read to or written from must be accessible. Typically, you use these functions for debugging, but there's no requirement that this be their only use.