`
`Windows CE .
`
`Platform
`
`SDKs
`
`Powered by
`
`‘-
`Microsoft”
`Windows“CE
`
`©C
`
`D-ROM
`Included
`
` Microsoft‘g’ Programming Series
`
`
`“DOUG’S CODE
`
`DEMONSTRATES
`
`A PERFECT GRASP
`
`OF WINDOWS CE—
`
`CRAFTY AND ELEGANT. ”
`
`—Char|es Petzoid, author,
`Programming Windows
`
`The
`
`definitive
`
`guide to
`programming
`the Windows CE
`
`API
`
`Douglas Boling
`
`Microsoft Corp. Exhibit 1058
`
`Q
`
`Microsoft Corp. Exhibit 1058
`
`
`
`PROGRAMMING
`Mlcnosow
`WINDOW? CE
`
`Douglas Boling
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`
`
`PUBLISHED BY
`Microsoft Press
`A Division of Microsoft Corporation
`One Microsoft Way
`Redmond, Washington 98052-6399
`
`Copyright © 1998 by Douglas McConnaughey Boling
`
`All rights reserved. No part of the contents of this book may be reproduced or
`transmitted in any form or by any means without the written permission of the publisher.
`
`Library of Congress Cataloging—in-Publication Data
`Boling, Douglas McConnaughey, 1960—
`Programming Microsoft Windows CE / Douglas McConnaughey Boling.
`p.
`cm.
`Includes index.
`ISBN 1—57251-856—2
`1. Microsoft Windows (Computer file)
`(Computers)
`1. Title.
`QA76.76.063B623
`1998
`005.4'469-—dc21
`
`2. Operating Systems
`
`98-39279
`CIP
`
`Printed and bound in the United States of America.
`
`125456789 QMQM 321098
`
`Distributed in Canada by ITP Nelson, a division of Thomson Canada Limited.
`
`A CIP catalogue record for this book is available from the British Library.
`
`Microsoft Press books are available through booksellers and distributors worldwide. For further
`information about international editions, contact your local Microsoft Corporation office. Or
`contact Microsoft Press International directly at fax (425) 956-7529. Visit our Web site at
`ms;2ress.microsoft.com.
`
`Active Desktop, Developer Studio, Microsoft, Microsoft Press, MS—DOS, Visual C++, Win52, Win—
`dows, the Windows CE logo, and Windows NT are either registered trademarks or trademarks of
`Microsoft Corporation in the United States and/or other countries. Other product and company
`names mentioned herein may be the trademarks of their respective owners.
`
`Acquisitions Editor: Eric Stroo
`Project Editor: Kathleen Atkins
`Technical Editor: Jim Fuchs
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8
`
`Processes
`
`and Threads
`
`Like Windows NT, Windows CE is a fully multitasking and multithreaded operating
`
`system. What does that mean? In this chapter I’ll present a few definitions and then
`some explanations to answer that question.
`A process is a single instance of an application. If two copies of Microsoft Pocket
`Word are running, two unique processes are running. Every process has its own,
`protected, 32-MB address space as described in Chapter 6. Windows CE enforces a
`limit of 32 separate processes that can run at any time.
`Each process has at least one thread. A thread executes code within a process. A
`process can have multiple threads running “at the same time.” I put the phrase at the
`same time in quotes because, in fact, only one thread executes at any instant in time.
`The operating system simulates the concurrent execution of threads by rapidly switch—
`ing between the threads, alternatively stopping one thread and switching to another.
`
`PROCESSES
`
`Windows CE treats processes differently than does Windows 98 or Windows NT. 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; FILESYSEXE, which provides file system services;
`GWES.EXE, which provides the GUI support; and DEVICEEXE, which loads and
`maintains the device drivers for the system. On most systems, other processes are
`
`493
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Part II Windows CE Basics
`
`also started, such as the shell, EXPLOREREXE, and, if the system is connected to a
`PC, REPLLOG.EXE and RAPISRVEXE, 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 sys—
`tems don’t need that many processes. A typical H/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 98 or Windows NT, Windows CE processes contain
`much less state information. Since Windows CE supports neither drives nor the con—
`cept of a current directory, the individual processes don’t need to store that informa-
`tion. Windows CE also doesn’t maintain a set of environment variables, so processes
`don’t need to keep an environment block. Windows CE doesn’t support handle in—
`heritance, 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 Win-
`dows 98 or Windows NT. Since Windows CE doesn’t support an environment, all the
`Win32 functions dealing with the environment don’t exist in Windows CE. While
`Windows CE supports threads, it doesn’t support fibers, a lightweight version of a
`thread supported by Windows NT. So, the fiber API doesn’t exist under Windows CE.
`Some functions aren’t supported because there’s an easy way to work around the lack
`of the function. For example, GetCommandLme doesn’t exist in Windows CE, so an
`application needs to save a pointer to the command line passed to WinMain if it needs
`to access it later. Finally, 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 1pApplicationName,
`LPTSTR lpCommandLine,
`LPSECURITY_ATTRIBUTES 1pProcessAttributes,
`LPSECURITY_ATTRIBUTES 1pThreadAttributes.
`BOOL bInheritHandies, DWORD dwCreationFiags,
`LPVOID 1pEnvironment,
`LPCTSTR 1pCurrentDirectory,
`LPSTARTUPINFO 1pStartupInf0,
`LPPROCESS_INFORMAT10N lpProcessInformation);
`
`While the list of parameters looks daunting, most of the parameters must be set to
`NULL 0r 0 because Windows CE doesn’t support security or current directories,
`
`494
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`nor does it handle inheritance. This results in a function prototype that looks more
`like this:
`
`BOOL CreateProcess (LPCTSTR lpApplicationName,
`LPTSTR 1pCommandLine,
`NULL, NULL. FALSE.
`DNORD 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:
`
`1. The path, if any, specified in the lpApplz'catz'onName.
`
`2.
`
`For Windows CE 2.1 or later, the path specified in the SystemPat/a value in
`[HKEY_LOCAL_MACHINE]\Loader. For earlier versions, the root of any
`external storage devices, such as PC Cards.
`
`\
`
`The windows directory, (\Windows).
`
`4. The root directory in the object store, (\).
`
`This action is different from Windows NT, where CreateProcess searches for the
`
`executable only if lpApplz'cationName is set to NULL and the executable name is passed
`through the lpCcommnadLine parameter. In the case of Windows CE, the applica—
`tion name must be passed in the lpApplz'caz’tonName parameter because Windows CE
`doesn’t support the technique of passing a NULL in @Applz’cationName with the ap—
`plication name as the first token in the lpCommansz’ne parameter.
`The lpCommomdLine parameter specifies the command line that will be passed
`to the new process. The only difference between Windows CE and Windows NT 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 exe-
`cutable as the first token in lpCommcmdLine.
`The dwCreatz’onFlags parameter specifies the initial state of the process after it
`has been loaded. Windows CE limits the allowable flags to the following:
`
`I
`
`I
`
`I
`
`0 Creates a standard process.
`
`CREATE_SUSPENDED Creates the process, 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.
`
`I
`
`DEB UG_ONL Y_77-IIS_PROCESS When combined With DEBUG_PROCESS,
`
`debugs a process but doesn’t debug any child processes that are launched
`by the process being debugged.
`
`495
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`PaflH
`
`Windows CE Basics
`
`I
`
`CREAYF_NEW_CONSOLE Forces a new console to be created. This is
`
`supported only in Windows CE 2.1 and later.
`
`The only other parameter of CreateProcess used by Windows CE is lpProcess—
`Information. 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;
`DNORD derocessId;
`DNORD 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 iden-
`tify 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 identify—
`
`ing the process you’re interested in before using them. For example, you can, by us—
`ing 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 stileNameEMAX_PATH];
`TCHAR sszdLineE64];
`DWORD dwCreationFlags;
`PROCESS_INFORMATION pi;
`INT rc;
`
`Tstrcpy (stiTeName, TEXT ("calc"));
`Tstrcpy (sszdLine, TEXT (""));
`dwCreationFTags
`0;
`
`496
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`rc = CreateProcess (stiIeName, sszdLine, NULL, NULL, FALSE,
`dwCreationFlags. NULL, NULL, NULL, &p1);
`
`if (rc) {
`CIoseHandie (p1.hThread);
`CloseHandle (pi.hProcess);
`
`} T
`
`his code launches the standard Calculator applet found on Handheld PCs and Palm—
`
`size PCs. Since the file name doesn’t specify a path, CreateProcess will, using the stan-
`dard Windows CE search path, find calc.exe in the \Windows directory. Because I
`didn’t pass a command line to Cale, I could have simply passed a NULL value in
`the lpCmdLme parameter. But I passed a null string in sszdLme to differentiate the
`lpCmdLme 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 Win52 applica-
`tions to prevent memory leaks.
`
`Terminating a Process
`
`A process can terminate itself by simply returning from the WinMam procedure. For
`console applications, a simple return from main suffices. Windows CE doesn’t sup—
`port the ExitProcess function found in Windows 98 and Windows NT. Instead, you
`can have the primary thread of the process call Exitfloread. 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 ExitT/oread. You can determine the exit code of a process by calling
`
`BOOL GetExitCodeProcess (HANDLE hProcess, LPDNORD 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 run-
`ning, 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 ex-
`pecting 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 much nicer a 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 an-
`other 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 Palm—size PC, where
`
`497
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Partll Windows CE Basics
`
`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 pro-
`cess, you can use TerminateProcess.
`
`TerminateProcess is prototyped as
`
`BOOL TerminateProcess (HANDLE hProcess, DNORD 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 pro—
`cess. You might want to know the handle for a process for other reasons, as well. For
`example, you might want to know when the process terminates. Windows CE sup—
`ports two additional functions that come in handy here (both of which are seldom
`
`discussed). The first function is OpenProcess, which returns thehandle of an already
`running process. OpenProcess is prototyped as
`
`HANDLE OpenProcess (DNORD deesiredAccess, BOOL bInheritHandle,
`DNORD derocessId);
`
`Under Windows CE, the first parameter isn’t used and should be set to 0. The
`
`bIn/oem'tHandle parameter must be set to FALSE because Windows CE doesn’t sup—
`port 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
`
`DNORD GetwindowThreadProcessId (HNND hNnd, LPDNORD 1pderocessId);
`
`This function takes a handle to a window and returns the process ID for the
`process that created the window. 80, using these two functions, you can trace a win-
`dow 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 ReadPr‘ocessMemory (HANDLE hProcess , LPCVOID 1pBaseAddress ,.
`LPVOID lpBuffer, DWORD nSize,
`LPDNORD 1pNumber0fotesRead);
`
`and
`
`BOOL NriteProcessMemory (HANDLE hProcess, LPVOID 1pBaseAddress.
`LPVOID 1pBuffer. DNORD nSize,
`LPDNORD 1pNumberOfotesNritten);
`
`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
`
`498
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`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.
`
`THREADS
`
`A thread is, fundamentally, a unit of execution. That is, it has a stack and a processor
`context, which is a set of values in the CPU internal registers. When a thread is sus—
`pended, the registers are pushed onto the threads stack, the active stack is changed
`to the next thread to be run, that thread’s CPU state is pulled off its stack, and the
`new thread starts executing instructions.
`Threads under Windows CE are similar to threads under Windows NT or Win-
`
`dows 98. Each process has a primary thread. Using the functions that I describe be—
`low, a process can create any number of additional threads within the process. The
`only limit to the number of threads in a Windows CE process is the memory and process
`address space available for the threads stack.
`Threads within a process share the address space of the process. Memory allo-
`cated by one thread is accessible to all threads in the process. Threads share the same
`access rights for handles whether they be file handles, memory objects handles, 0r
`handles to synchronization objects.
`Before Windows CE 2.1, the size of all thread stacks was set at around 58 KB.
`
`Starting with Windows CE 2.1, the stack size of all threads created within a process is
`set by the linker. (The linker switch for setting the stack size in Microsoft Visual C++
`is /5tac/e.) Secondary threads under Windows CE 2.1 are created with the same stack
`size as the primary thread.
`
`The System Scheduler
`
`Windows CE schedules threads in a preemptive manner. Threads run for a quantum
`or time slice, which is usually 25 milliseconds on H/PCs and Palm-size PCs. (OEMS
`developing custom hardware can specify a different quantum.) After that time, if the
`thread hasn’t already relinquished its time slice and if the thread isn’t a time-critical
`thread, it’s suspended and another thread is scheduled to run. Windows CE chooses
`which thread to run based on a priority scheme. Threads of a higher priority are sched-
`uled before threads of lower priority.
`The rules for how Windows CE allocates time among the threads are quite dif-
`ferent from Windows NT and from Windows 98. Unlike Windows NT, Windows CE
`
`processes don’t have a priority class. Under Windows NT, a process is created with a
`priority class. Threads derive their priority based on the priority class of their parent
`processes. A process with a higher-priority class has threads that run at a higher pri-
`ority than threads in a lower—priority Class process. Threads within a process can then
`refine their priority within that process by setting their relative thread priority.
`
`499
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Pan II Windows CE Basics
`
`Because Windows CE has no priority classes, all processes are treated as peers.
`Individual threads can have different priorities, but the process that the thread runs
`within doesn’t influence those priorities. Also, unlike Windows NT, the foreground
`thread in Windows CE doesn’t get a boost in priority.
`In Windows CE, a thread can have one of eight priority levels. Those priorities
`are listed below:
`
`I
`
`I
`
`I
`
`I
`
`7HREAD_PRIOR1TY_77ME_CRITICAL Indicates 3 points above normal
`priority. Threads of this priority aren’t preempted.
`
`THREAD_PRIORITY_HIGHEST Indicates 2 points above normal priority.
`
`THREAD_PRIORITY_ABOVILNORZVML Indicates 1 point above normal
`
`priority.
`
`7HRE4D_PRIORITY_NORM4L Indicates normal priority. All threads are
`created with this priority.
`
`I
`
`7HREAD_PRIORITY_BELOW_NORMAL Indicates 1 point below normal
`
`priority.
`
`I
`
`I
`
`THREAD_PRIORITY_LOWEST Indicates 2 points below normal priority.
`
`YHREAD_PRIORITY_ABOVF_IDLE Indicates 3 points below normal
`
`priority.
`
`I
`
`THREAD_PRIORITY_IDLE Indicates 4 points below normal priority.
`
`All higher-priority threads run before lower-priority threads. This means that
`before a thread set to run at particular priority can be scheduled, all threads that have
`a higher priority must be blocked. A blocked thread is one that’s waiting on some
`system resource or synchronization object before it can continue. Threads of equal
`priority are scheduled in a round—robin fashion. Once a thread has voluntarily given
`up its time slice, is blocked, or has completed its time slice, all other threads of the
`same priority are allowed to run before the original thread is allowed to continue. If
`a thread of higher priority is unblocked and a thread of lower priority is currently
`running, the lower-priority thread is immediately suspended and the higher-priority
`thread is scheduled. Lower-priority threads can never preempt a higher-priority thread.
`There are two exceptions to the rules I just stated. If a thread has a priority
`of THREAD_PRIORITY_TIME_CRITICAL, it’s never preempted, even by another
`THREAD_PRIORITY_TIME_CRITICAL thread. As you can see, a THREAD_PRIORITY_
`TIME_CRITICAL thread can and will starve everyone else in the system unless writ-
`ten carefully. This priority is reserved by convention for interrupt service threads in
`device drivers, which are written so that each thread quickly performs its task and
`releases its time slice.
`
`500
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`The other exception to the scheduling rules happens if a low—priority thread owns
`a resource that a higher-priority thread is waiting on. In this case, the low-priority thread
`is temporarily given the higher-priority thread’s priority in a scheme known as prior—
`ity inversion, so that it can quickly accomplish its task and free the needed resource.
`While it might seem that lower—priority threads never get a chance to run in
`this scheme, it works out that threads are almost always blocked, waiting on something
`
`to free up before they can be scheduled. Threads are always created at THREAD_
`PRIORITY_NORMAL, so, unless they proactively change their priority level, a thread
`
`is usually at an equal priority to most of the other threads in the system. Even at the
`normal priority level, threads are almost always blocked. For example, an application’s
`primary thread is typically blocked waiting on messages. Other threads should be
`designed to block on one of the many synchronization objects available to a Win—
`dows CE application.
`
`Never Do This!
`
`What’s not supported by the arrangement I just described, or by any other thread—
`based scheme, is code like the following:
`
`{
`while (bFIag == FALSE)
`// Do nothing, and spin
`
`} /
`
`/ Now do something.
`
`This kind of code isn’t just bad manners, since it wastes CPU power, it’s a death sen-
`tence to a battery-powered Windows CE device. To understand why this is impor—
`tant, I need to digress into a quick lesson on Windows CE power management.
`Windows CE is designed so that when all threads are blocked, which happens
`over 90 percent of the time, it calls down to the OEM Abstraction Layer (the equiva—
`lent to the BIOS on an MS—DOS machine) to enter a low—power waiting state. Typi-
`
`it simply stops
`cally, this low—power state means that the CPU is halted; that is,
`executing instructions. Because the CPU isn’t executing any instructions, no power—
`consuming reads and writes of memory are performed by the CPU. At this point, the
`only power necessary for the system is to maintain the contents of the RAM and light
`the display. This low—power mode can reduce power consumption by up to 99 per-
`cent of what is required when a thread is running in a well-designed system.
`Doing a quick back-of—the—envelope calculation, say a Palm-size PC is designed
`to run for 15 hours on a couple of AAA batteries. Given that the system turns itself off
`after a few minutes of non—use, this 15 hours translates into a month or two of battery
`
`life in the device for the user. (I’m basing this calculation on the assumption that the
`system indeed spends 90 percent or more of its time in its low-power idle state.) Say
`a poorly written application thread spins on a variable instead of blocking. While this
`application is running, the system will never enter its low-power state. So, instead of
`
`501
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Parill Windows CE Basics
`
`900 minutes of battery time (15 hours X 60 minutes/hour), the system spends 100 per-
`cent of its time at full power, resulting in a battery life of slightly over 98 minutes, or
`right at 1.5 hours. So, as you can see, it’s good to have the system in its low—power
`state.
`
`Fortunately, since Windows applications usually spend their time blocked in a
`
`call to GetMessage, the system power management works by default. However, if you
`plan on using multiple threads in your application, you must use synchronization
`objects to block threads while they’re waiting. First, let’s look at how to create a thread,
`
`and then I’ll dive into the synchronization tools available to Windows CE programs.
`
`Creating a Thread
`
`You create a thread by calling this function:
`
`HANDLE CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes,
`DWORD detackSize,
`LPTHREAD_START_ROUTINE lpStartAddress,
`LPVOID lpParameter, DWORD dwCreationFlags,
`LPDWORD lpThreadId);
`
`As with CreateProcess, Windows CE doesn’t support a number of the parameters in
`CreateTYoread, and so they are set to NULL or O as appropriate. For CreateT/aread,
`the lprreadAtmbutes, and demc/eSz'ze parameters aren’t supported. The parameter
`lprreadAtm‘butes must be set to NULL and detac/eSz’ze is ignored by the system
`and should be set to 0. The third parameter, lpSmwAddress, must point to the start of
`the thread routine. The lpPammeter parameter in Createflaread is an application-
`defined value that’s passed to the thread function as its one and only parameter.
`The dwCreationFlags parameter can be set to either 0 or CREATE_SUSPENDED. If
`
`CREATE_SUSPENDED is passed, the thread is created in a suspended state and must
`be resumed with a call to Resumeflaread. The final parameter is a pointer to a DWORD
`that receives the newly created thread’s ID value.
`
`The thread routine should be prototyped this way:
`
`DWORD WINAPI ThreadFunc (LPVOID 1pArg);
`
`The only parameter is the lpParameler value, passed unaltered from the call to
`CreateT/oread. The parameter can be an integer or a pointer. Make sure, however,
`that you don’t pass a pointer to a stack—based structure that will disappear when the
`routine that called CreateT/aread returns.
`
`If CreateTYoread is successful, it creates the thread and returns the handle to the
`newly created thread. As with CreateProcess, the handle returned should be closed
`
`when you no longer need the handle. Following is a short code fragment that con-
`tains a call to start a thread and the thread routine.
`
`502
`
`.
`.
`.
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`// - - - - - - - - - - — - ~ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - — - - - - --
`//
`//
`HANDLE hThreadl;
`DWORD dwThreadlID = 0;
`INT nParameter = 5;
`
`hThreadl = CreateThread (NULL, 0, ThreadZ. nParameter, 0,
`&dwThread11D);
`
`CloseHandie (hThreadl);
`
`// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --
`// Second thread routine
`//
`
`DNORD WINAPI ThreadZ (PVOID pArg)
`
`{
`
`INT nParam = (INT) pArg;
`
`//
`// Do something here.
`// .
`// .
`// .
`return 0x15;
`
`In this code, the second thread is started with a call to CreateT/aread. The
`nPammeter value is passed to the second thread as the single parameter to the thread
`routine. The second thread executes until it terminates, in this case simply by return-
`
`ing from the routine.
`A thread can also terminate itself by calling this function:
`
`VOID ExitThread (DNORD dwExitCode);
`
`The only parameter is the exit code that’s set for the thread. That thread exit code
`can be queried by another thread using this function:
`
`BOOL GetExitCodeThread (HANDLE hThread, LPDWORD IpExitCode);
`
`The function takes the handle to the thread (not the thread ID) and returns the exit
`
`code of the thread. If the thread is still running, the exit code is STILL_ACTIVE, a con-
`stant defined as 0X0103. The exit code is set by a thread using Exiflbread or the value
`returned by the thread procedure. In the preceding code, the thread sets its exit code
`to 0X15 when it returns.
`
`All threads Within a process are terminated when the process terminates. As I
`said earlier, a process is terminated When its primary thread terminates.
`
`503
`.
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Part II Windows CE Basics
`
`Setting and querying thread priority
`Threads are always created at a priority level of THREAD_PRIORITY_NORMAL. The
`
`thread priority can be changed either by the thread itself or by another thread calling
`this function:
`
`BOOL SetThreadPriority (HANDLE hThread,
`
`int nPriority);
`
`The two parameters are the thread handle and the new priority level. The level passed
`can be one of the constants described previously, ranging from THREAD_PRIORITY_
`IDLE up to THREAD_PRIORITY_TIME_CRITICAL. You must be extremely careful when
`you’re changing a thread’s priority. Remember that threads of a lower priority almost
`never preempt threads of higher priority. So, a simple bumping up of a thread one
`notch above normal can harm the responsiveness of the rest of the system unless that
`thread is carefully written.
`To query the priority level of a thread, call this function:
`
`int GetThreadPriority (HANDLE hThread);
`
`This function returns the priority level of the thread. You shouldn’t use the hard-coded
`priority levels. Instead, use constants, such as THREAD_PRIORITY_NORMAL, defined
`
`by the system. This ensures that any change to the priority scheme in future versions
`of Windows CE doesn’t affect your program.
`
`Suspending and resuming a thread
`
`You can suspend a thread at any time by calling this function:
`
`DWORD SuspendThread (HANDLE hThread);
`
`The only parameter is the handle to the thread to suspend. The value returned is the
`suspend coum‘ for the thread. Windows maintains a suspend count for each thread.
`Any thread with a suspend count greater than 0 is suspended. Since Suspendflaread
`increments the suspend count, multiple calls to Suspendflaread must be matched with
`an equal number of calls to Resumefloread before a thread is actually scheduled to
`run. ResumeCoum is prototyped as
`
`DNORD ResumeThread (HANDLE hThread);
`
`Here again, the parameter is the handle to the thread and the return value is
`
`the previous suspend count. So, if Resumeflaread returns 1, the thread is no longer
`suspended.
`
`At times, a thread simply wants to kill some time. Since I’ve already explained
`why simply spinning in a while loop is a very bad thing to do, you need another way
`to kill time. The best way to do this is to use this function:
`
`void Sleep (DWORD deiiiiseconds);
`
`Sleep suspends the thread for at least the number of milliseconds specified in the
`dez’llz'seconds parameter. Since the quantum, or time slice, on a Windows CE
`
`504
`
`Microsoft Corp. Exhibit 1058
`
`Microsoft Corp. Exhibit 1058
`
`
`
`Chapter 8 Processes and Threads
`
`system is usually 25 milliseconds, specifying very small numbers of milliseconds
`results in sleeps of at least 25 milliseconds. This strategy is entirely valid, and some-
`times it’s equally valid to pass a 0 to Sleep. When a thread passes a O to Sleep, it gives
`up its time slice but is rescheduled immediately according to the scheduling rules I
`described previously.
`
`Thread Local Storage
`
`Thread local storage is a mechanism that allows a routine to maintain separate in—
`stances of data for each thread calling the routine. This capability might not seem
`like much, but it has some very handy uses. Take the following thread routine:
`
`INT g_nGlobal;
`
`// System global variable
`
`int ThreadProc (pStartData)
`INT nValuel;
`INT nValueZ;
`
`{
`
`{
`
`while (unblocked)
`//
`// Do some work.
`//
`
`terminate the thread by returning.
`
`} /
`
`/ We're done now,
`return 0;
`
`} F
`
`or this example, imagine that multiple threads are created to execute t