Windows CE 2.1 Technical Articles  

Working With Processes and Threads in Microsoft Windows CE 2.1

This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Microsoft Corporation

April 2000

Summary:Microsoft Windows CE is a preemptive multitasking operating system that supports the Microsoft Win32 process and thread model using a round-robin, priority-based scheduler. This paper discusses how to create, manage, and terminate processes and threads using this Win32 process and thread model. (15 printed pages)


Interprocess Synchronization


Microsoft Windows CE is a compact, scalable operating system. It uses a subset of the Microsoft Win32 application programming interface (API) that is used on Windows-based desktop and server computers. You can use the same development tools, such as, Microsoft Visual C++ or Microsoft Visual Basic, to create applications for Windows CE–powered devices.

Windows CE is a preemptive multitasking operating system. Multiple applications, or processes, can run within the system at the same time. A process consists of one or more threads, where each thread executes code in that process. When you correctly use threads, your application runs more quickly and efficiently.

Windows CE uses the hardware memory management unit (MMU) to implement virtual memory and to protect processes. While other operating systems either do not use the MMU or make limited use of it, Windows CE uses the MMU to make the system more flexible and reliable. The hardware protects processes from one another, making the system suitable for products where processes are added after the system is built, without the knowledge or help of the system builder. By enabling these third-party programs, the end product can be an open platform supporting a broader range of customer solutions without giving up reliability.

Virtual memory allows the system builder to make more efficient use of memory and code space. Code can be executed in read-only memory (ROM), compressed in ROM, and paged into random access memory (RAM), or stored in a file system and paged into RAM at execution time.

To help you take advantage of the efficiency offered by Windows CE, this paper provides information about how to create, manage, and terminate processes and threads using the Win32 process and thread model supported by Windows CE.


A process is a single instance of a running application. Windows CE supports a maximum of 32 simultaneous processes. Each process has at least a primary thread, which executes code within the process. You can also create additional threads in a process, limited only by RAM.

Windows CE has a 4GB address space with full protection between processes. The virtual address space for each process is 32MB. You can allocate additional memory outside of the 32MB by using memory-mapped files. Also, allocations greater than 2MB using the VirtualAlloc function with the MEM_RESERVE and PAGE_NOACCESS flags and without a start address are allocated outside of the process address space.

A process contains a default heap whose size is limited only by physical memory. The process can own other resources such as files and dynamic-link libraries (DLLs). Windows CE does not support environment variables or current directory, and the search order for .exe files is as follows:

  • Windows (\windows) directory
  • Root directory (\) of the Windows CE device
  • OEM-specified directories
  • For Microsoft Windows CE Platform Builder 2.11 users only: OEM-defined shell (\ceshell) directory

    Also note that Windows CE does not support process priority classes, which restrict the thread priorities that can be assigned in a process.

    Creating a Process

    When you create a process, Windows CE allocates memory for the stack and heap and creates the primary thread for the process. The system then schedules the process to be run.

    The CreateProcess function creates a process and returns the handles and identifiers for the process and its primary thread. Many of this function's parameters are set to NULL or FALSE because Windows CE does not support features on the desktop, such as, security, current directory, or handle inheritance. The resulting function and required parameters are as follows:

    BOOL CreateProcess (LPCTSTR lpApplicationName, LPTSTR lpCommandLine, NULL, NULL, FALSE, DWORD dwCreationFlags, NULL, NULL, NULL, LPPROCESS_INFORMATION lpProcessInformation);
    Parameter Description
    lpApplicationName Specifies the name of the application to execute. This is a Unicode sting, and you cannot pass NULL.
    lpCommandLine Specifies the command line passed to the new process. The command line is always passed as a Unicode sting.
    dwCreationFlags Specifies the initial state of the process after it has been loaded. This parameter must be one of the following flags:
    0 creates a standard process.
    CREATE_SUSPENDED creates the process then suspends the primary thread.
    DEBUG_PROCESS treats the process being created as a process being debugged by the caller.
    DEBUG_ONLY_THIS_PROCESS, when combined with DEBUG_PROCESS, debugs a process but does not debug any child processes that are launched by the process being debugged.
    CREATE_NEW_CONSOLE forces creation of a new console.
    lpProcessInformation Points to a PROCESS_INFORMATION structure that the CreateProcess function fills with information about the new process. You can pass NULL.

    If the process cannot be run, the CreateProcess function returns FALSE. For more detailed information about the failure, use the GetLastError function.

    Terminating a Process

    The preferred method for process termination is for a process to terminate itself by returning from its primary thread. You can terminate the primary thread of the process by calling the ExitThread function. For more information, see the section on Terminating a Thread. The ExitThread function returns the exit code of the process.

    You can also terminate a process with one of the following methods:

    • Use interprocess communication to tell the process to terminate itself. Interprocess communication is discussed later in this paper.
    • If the process has a message queue, send a WM_CLOSE message to the main window of the process. An application may not close if it does not receive this message and may display a message box.
    • As a last resort, you can use the TerminateProcess function, which does not notify attached DLLs that the process is terminating. Specify the handle and exit code of the process, as follows:

      BOOL TerminateProcess (HANDLE hProcess, DWORD uExitCode);

      You can determine the exit code for a process with the GetExitCodeProcess function. If the process is still running, the returned exit code is the constant STILL_ACTIVE. Specify the handle to the process, and the function returns the exit code, as follows:

      BOOL GetExitCodeProcess (HANDLE hProcess, LPDWORD lpExitCode);

      As an alternative, you can also use the WaitForSingleObject function. Pass the process handle as the first parameter and a timeout value for the second parameter-for more information, see the section on Wait Functions. The process handle is signaled when the process terminates.

      Also, when you no longer need a process handle, close it by using the CloseHandle function. This function frees data associated with an object.

      Other Process Information

      Windows CE also supports the following process functions.

      If you want to know the: Use:
      Handle to a process HANDLE GetCurrentProcess (void);
      Identifier of a process DWORD GetCurrentProcessId (void);
      Handle of another process HANDLE OpenProcess (0, FALSE, DWORD dwProcessId);
      Process identifier for the process that created a particular window DWORD GetWindowThreadProcessId (HWND hWnd, LPWORD lpdwProcessId);

      A process can directly read from or write to an accessible memory space of another process using the ReadProcessMemory and WriteProcessMemory functions. The functions and required parameters are as follows:

      BOOL ReadProcessMemory (HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
      BOOL WriteProcessMemory (HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesWritten);
      Parameter Description
      hProcess Specifies the handle of the other process. Use the OpenProcess function to return a process handle.
      lpBaseAddress Specifies base address of the process to be read or written.
      lpBuffer Specifies name of the local buffer from or to which the data is to be read or written.
      nSize Specifies size of the local buffer from or to which the data is to be read or written.
      lpNumberOfBytesReadlpNumberOfBytesWritten Specifies number of bytes read or written.


      A thread executes code in a process. Each thread has a stack, where the linker sets the stack size of all threads created in a process. Each process has a primary thread and can have as many additional threads as permitted by available RAM. Threads within a process share the address space of the process.

      A thread also has associated registers such as the instruction pointer. These associated registers are known as the context. You can use the GetThreadContext function to retrieve the context of the specified thread and the SetThreadContext function to set the context in the specified thread, as follows:

      BOOL GetThreadContext (HANDLE hThread, LPCONTEXT lpContext); BOOL SetThreadContext (HANDLE hThread, CONST CONTEXT *lpContext);

      Threads can be in one of the following states: running, suspended, sleeping, blocked, or terminated. Multi-threaded applications must avoid two threading problems: deadlocks and race conditions. A deadlock occurs when each thread is waiting for the other to do something. A race condition occurs when threads are sharing a value and the final result of the value depends on the scheduling order of the threads.

      Creating a Thread

      To create a thread, first define a thread routine and then call the CreateThread function, as follows:

      HANDLE CreateThread NULL, 0, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId);
      Parameter Description
      lpStartAddress Points to the start of the thread routine.
      lpParameter Specifies an application-defined value that is passed to the thread routine.
      dwCreationFlags Set to 0 or CREATE_SUSPENDED.
      lpThreadId Points to a DWORD that receives the new thread's identifier.

      The CreateThread function returns the handle and identifier of the new thread. If you otherwise need to know the identifier or handle of a thread, use the GetCurrentThreadId function to return the thread identifier, and the GetCurrentThread function to return a handle to the thread. These functions are defined as follows:

      HANDLE GetCurrentThread (void);
      DWORD GetCurrentThreadId (void);

      Threads are always created with a priority of THREAD_PRIORITY_NORMAL. To change the priority level of a thread, call the SetThreadPriority function. For more information about priorities, see the section on Scheduling Threads.

      Terminating a Thread

      Thread termination is consistent with Windows-based desktop platforms. Thread and process termination are linked in the following ways:

      • If an exception occurs in a thread, its process is terminated.
      • If a primary thread terminates, its process is also terminated.
      • When a process is terminated, all its threads are terminated.

        The ExitThread function terminates a thread and frees all resources used by the thread. The only parameter is the thread's exit code, as follows:

        VOID ExitThread (DWORD dwExitCode);

        You can determine the exit code of a thread by using the GetExitCodeThread function. If the thread is still running, the exit code is STILL_ACTIVE. The function takes the handle to the thread and returns the exit code, as follows:

        BOOL GetExitCodeThread (HANDLE hThread, LPDWORD lpExitCode);

        As a last resort, you can use the TerminateThread function, which does not execute termination code and may not free resources. The parameters are the handle and exit code of the thread, as follows:

        TerminateThread (HANDLE hThread, DWORD dwExitCode);

        When you no longer need a thread handle, be sure to close it with the CloseHandle function to free resources.

        Scheduling Threads

        Windows CE uses a priority-based time-slice algorithm to schedule the execution of threads. Because Windows CE does not have priority classes, the process in which the thread runs does not influence thread priorities. All the priorities can be used in the same process. A thread can have one of the eight priorities shown in the following table.

        Priority Value Typically used for:
        THREAD_PRIORITY_TIME_CRITICAL Real-time processing and device drivers
        THREAD_PRIORITY_HIGHEST Real-time processing and device drivers
        THREAD_PRIORITY_ABOVE_NORMAL Kernel threads and normal applications
        THREAD_PRIORITY_NORMAL Kernel threads and normal applications
        THREAD_PRIORITY_BELOW_NORMAL Kernel threads and normal applications
        THREAD_PRIORITY_LOWEST Applications that can always be preempted
        THREAD_PRIORITY_ABOVE_IDLE Applications that can always be preempted
        THREAD_PRIORITY_IDLE Applications that can always be preempted

        To query the priority level of a thread, call the GetThreadPriority function. To change the priority level of a thread, call the SetThreadPriority function. Both functions take the handle to the thread, and the SetThreadPriority function takes the new priority level, as follows:

        int GetThreadPriority (HANDLE hThread);
        BOOL SetThreadPriority (HANDLE hThread, int nPriority);

        Threads with a higher priority are scheduled to run first. Threads with the same priority run in a round-robin fashion-when a thread has stopped running, all other threads of the same priority run before the original thread can continue. Threads at a lower priority do not run until all threads with a higher priority have finished, that is, until they either yield or are blocked. If one thread is running and a thread of higher priority is unblocked, the lower-priority thread is immediately suspended and the higher-priority thread is scheduled.

        Threads run for a time slice, or quantum, which has a default value of 25 milliseconds, but the OEM can specify a different quantum. After that time, if the thread has not relinquished its time slice and is not time critical, it is suspended and another thread is scheduled to run. Threads at the highest priority level-THREAD_PRIORITY_TIME_CRITICAL-continue executing without interruption until they have finished, unless interrupted by an Interrupt Service Routine (ISR).

        Thread priorities are fixed and do not change. Windows CE does not age priorities, boost foreground thread priorities, or mask interrupts based on these priority levels. The kernel can temporarily modify thread priorities when a low-priority thread is using a resource that delays the execution of a high-priority thread wanting to use the same resource. Windows CE boosts the priority of the low-priority thread until it releases the resource required by the higher priority thread. This scenario is called priority inheritance, and is designed to prevent a low-level thread running before higher-level threads, a problem known as priority inversion. Windows CE will go through the entire chain of threads trying to correct a priority inversion problem.

        You can suspend a thread using the SuspendThread function. To resume running a thread, call the ResumeThread function. You must match multiple calls to the SuspendThread function with the same number of calls to the ResumeThread function. The only parameter for both functions is the handle to the thread. To suspend a thread for a specified number of milliseconds, use the Sleep function. If you specify a sleep time of less than the quantum, the thread sleeps for a quantum. These functions are defined as follows:

        DWORD SuspendThread (HANDLE hThread);
        DWORD ResumeThread (HANDLE hThread);
        void Sleep (DWORD dwMilliseconds);

        You can profile the performance of a thread using the GetThreadTimes function, which returns how much time a thread has run.

        Thread Local Storage

        Thread local storage (TLS) lets a thread maintain separate instances of data. Each thread within a process can have its own set of values that are guaranteed to be unique. The TlsAlloc function reserves a TLS slot and sets the value in the slot to zero for all currently running threads. New threads are also created with their TLS array initialized to zero.

        After being assigned a TLS slot, each thread can access its unique data by calling the TlsGetValue or TlsSetValue function. The TlsFree function frees the TLS slot. The TLS functions are defined as follows, where dwTlsIndex specifies the slot that contains the data and lpTlsValue specifies the value to be stored in the calling thread's TLS slot:

        DWORD TlsAlloc (void);
        BOOL TlsSetValue (DWORD dwTlsIndex, LPVOID lpTlsValue);
        LPVOID TlsGetValue (DWORD dwTlsIndex);
        BOOL TlsFree (DWORD dwTlsIndex);

        Thread Synchronization

        Synchronization is needed to avoid conflict over shared resources. You can coordinate the activities of threads by using wait functions, synchronization objects, and interlocked functions. Windows CE supports the following synchronization objects:

        • Event objects
        • Mutex (mutual exclusion) objects
        • Critical sections

          Windows CE queues event, mutex, and critical section requests in "FIFO-by-priority" order: a different FIFO queue is defined for each of the eight priority levels. A new request from a thread at a given priority is placed at the end of that priority's list.

          Wait Functions

          Windows CE supports single-object and multiple-object wait functions that block or unblock a thread based on a signaled or non-signaled state of an object. A wait function causes a thread to enter an efficient state that requires very little CPU processing power and battery power.

          The WaitForSingleObject function lets a thread wait on a single object. The object can be a synchronization object, such as, an event or mutex object, or can be handles to processes and threads. These handles are signaled when their processes or threads terminate. Thus, a process can monitor another process or thread and perform some action based on the status of the process or thread.

          The WaitForMultipleObjects function lets a thread wait on multiple synchronization objects. The wait can end when any one of the objects is signaled. Windows CE does not support waiting for all objects to be signaled.

          Do not use the WaitForSingleObject or WaitForMultipleObjects function on the thread that processes messages. A thread that is blocked waiting on an object cannot retrieve and dispatch messages. You can use the MsgWaitForMultipleObjects function. This function combines the WaitForMultipleObjects function with a check into the message queue so that the function returns if any of the selected categories of messages are received during the wait.

          The wait functions are defined as follows:

          DWORD WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds);
          DWORD WaitForMultipleObjects (DWORD nCount, CONST HANDLE *lpHandles, FALSE, DWORD dwMilliseconds);
          DWORD MsgWaitForMultipleObjects (DWORD nCount, LPHANDLE pHandles, FALSE, DWORD dwMilliseconds, DWORD dwWakeMasks);
          Parameter Description
          hHandle Specifies the handle to the object being waited on.
          dwMilliseconds Specifies a timeout value. If you do not want a timeout, pass the value INFINITE.
          nCount Specifies a count of the number of event or mutex objects to wait on.
          *lpHandles orpHandles Specifies a pointer to the array of handles for the event or mutex object.
          dwWakeMasks Specifies a flag that indicates the message category that, when received in the message queue of the thread, causes the function to return. Windows CE supports the following flags:

          QS_ALLINPUT = any message

          QS_INPUT = input message

          QS_KEY = key up, key down, or syskey up or down message

          QS_MOUSE = mouse move or mouse click message

          QS_MOUSEBUTTON = mouse click message has been received

          QS_MOUSEMOVE = mouse move message

          QS_PAINT = WM_PAINT message

          QS_POSTMESSAGE = posted message, other than one in this list

          QS_SENDMESSAGE = sent message, other than one in this list

          QS_TIMER = WM_TIMER message

          Event Objects

          An event object notifies a thread that an event has occurred. Use the CreateEvent function to create an event object. An event object can be named or unnamed, as specified in the lpName parameter. It can automatically reset itself from a signaled state to a nonsignaled state or require a manual reset, as specified in the bManualReset parameter. The bInitialState parameter specifies whether the event object is created in the signaled or nonsignaled state. The function is as follows:

          HANDLE CreateEvent (NULL, BOOL bManualReset, BOOL bInitialState, LPTSTR lpName);

          Named events can be shared with other processes. Threads in other processes can open a handle to an existing event object by specifying its name in a call to the CreateEvent function. To determine whether a CreateEvent function created a new object or opened an existing object, you can call the GetLastError function immediately after calling the CreateEvent function. If the GetLastError function returns ERROR_ALREADY_EXISTS, the call opened an existing event. In other words, if the name specified in a call to the CreateEvent function matches the name of an existing event object, the function returns the handle of the existing object. When using this technique for event objects, none of the calling processes should request immediate ownership of the event, because it is uncertain which process actually gets ownership.

          To signal an event, use the SetEvent or PulseEvent function. SetEvent does not automatically reset the event object to a nonsignaled state. The PulseEvent function signals the event then resets it. For manual events, the PulseEvent function unblocks all threads waiting on the event. For automatic events, the PulseEvent function unblocks only one thread.

          If an event object can reset itself, you need only use the SetEvent function to signal. The event object is then automatically reset to the nonsignaled state when one thread is unblocked after waiting on that event. To manually reset an event, use the ResetEvent function. These event functions are defined as follows:

          BOOL ResetEvent (HANDLE hEvent);
          BOOL SetEvent (HANDLE hEvent);
          BOOL PulseEvent (HANDLE hEvent);

          To close an event object, call the CloseHandle function. If the event object is named, Windows CE maintains a use count on the object, and you must make one call to the CloseHandle function for every call to the CreateEvent function.

          Mutex Objects

          A mutex (mutual exclusion) object is a synchronization object that is signaled when it is not owned by a thread and nonsignaled when it is owned. A mutex object is especially useful for coordinating exclusive access to a resource, such as, a block of memory across multiple threads, including threads in other processes.

          Use the CreateMutex function to create a mutex object. A mutex object can be named or unnamed, as specified in the lpName parameter. The bInitialOwner parameter specifies if the calling thread should immediately own the newly created mutex object, as follows:

          HANDLE CreateMutex (NULL, BOOL bInitialOwner, LPCTSTR lpName);

          Threads in other processes can open a handle to an existing mutex object by specifying its name in a call to the CreateMutex function. To determine whether a mutex object already exists, you can call the GetLastError function immediately after calling the CreateMutex function. If the GetLastError function returns ERROR_ALREADY_EXISTS, the call opened an existing mutex object.

          A thread requests ownership of a mutex object by using a wait function. If another thread owns the mutex object, the wait function blocks the requesting thread until the owning thread releases the mutex object with the ReleaseMutex function. This function takes the handle to the mutex, as follows:

          BOOL ReleaseMutex (HANDLE hMutex);

          If a thread owns a mutex object and specifies it in repeated calls to one of the wait functions, the wait call immediately returns. This prevents a thread from blocking itself while waiting for a mutex object that the thread already owns. To release ownership of a mutex object, the thread must call the ReleaseMutex function once for each call to the wait function.

          If a thread terminates without releasing ownership of a mutex object, the object is considered abandoned. A waiting thread can acquire ownership of an abandoned mutex object, but the return value for the wait function indicates that the mutex object is abandoned. To be safe, assume that an abandoned mutex object indicates that an error has occurred and that any shared resource protected by the mutex object is in an undefined state. If the thread proceeds as though the mutex object had not been abandoned, the object's abandoned flag is cleared when the thread releases ownership. Clearing the abandoned flag restores typical behavior if a handle to the mutex object is subsequently specified in a wait function.

          Now a few words about naming mutex and event objects. The name of an event or mutex object is limited to the number of characters defined by MAX_PATH. The name can include any character except the backward slash (\). The names of mutex and event objects share the same space. When you create an object, if you specify a name that is in use by an object of another type, the function succeeds, but the GetLastError function returns ERROR_ALREADY_EXISTS. To avoid this error, use unique names and check return values for duplicate-name errors.

          Critical Sections

          When multiple threads have shared access to the same resource, the threads can interfere with one another. A critical section protects a section of code by blocking other threads from entering that section when it already contains a thread. A critical section cannot be shared with other processes.

          To use a critical section, you first declare a CRITICAL_SECTION structure and then create a critical section handle with the InitializeCriticalSection function. A thread calls the EnterCriticalSection function to request ownership of a critical section and the LeaveCriticalSection function to release ownership. If another thread already owns a critical section, the thread calling the EnterCriticalSection function is blocked until the other thread releases the critical section.

          If a thread owns a critical section and makes additional calls to the EnterCriticalSection function, the function returns immediately. This prevents a thread from blocking itself while waiting for a critical section that the thread already owns. To release ownership, the thread must call the LeaveCriticalSection function once for each time the thread entered the critical section. When you are finished with a critical section, call the DeleteCriticalSection function to release the system resources that were allocated when you initialized the critical section.

          All of the critical section functions take a pointer to a CRITICAL_SECTION structure, as follows:

          void InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
          void EnterCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
          void LeaveCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
          void DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection);

          Interlocked Functions

          Interlocked functions synchronize access to a variable that is shared by multiple threads. The purpose is to prevent a thread from being preempted when it is in the middle of modifying or checking a variable.

          The InterlockedIncrement and InterlockedDecrement functions increment and decrement a shared variable and check the resulting value. The InterlockedExchange function exchanges the values of the specified variables. The InterlockedTestExchange function exchanges the values of the specified variables if one of the variables is equal to a specified value. These functions are defined as follows:

          LONG InterlockedIncrement (LPLONG lpAddend);
          LONG InterlockedDecrement (LPLONG lpAddend);
          LONG InterlockedExchange (LPLONG Target, LONG Value);
          LONG WINAPI InterlockedTestExchange (LPLONG Target, LONG OldValue, LONG NewValue);
          Parameter Description
          lpAddend Specifies a pointer to the variable to increment or decrement.
          Target Specifies pointer to target variable.
          Value Specifies new value of the variable.
          OldValue Specifies value to check against.
          NewValue Specifies value to set to if Target is equal to OldValue.


          Threads rely on messages to initiate processing, control system resources and communicate with the operating system and user. Windows messages originate from a variety of sources, including the operating system; user activity, such as, keyboard, mouse, or touch screen actions; and other running processes or threads.

          When a message is sent to a thread, that message is placed in a message queue for that thread for eventual handling. Each thread owns a message queue that is completely independent of message queues owned by other threads. Threads typically have a message loop that runs continuously, retrieving and handling messages. When no messages are in the queue and the thread is not engaged in any other activity, the system can suspend the thread, thereby saving CPU resources. Not all threads have a message queue. Only a thread that has an associated window has a message queue.

          You can use messages for control purposes, to initiate various types of processing in your application, and to pass data using message parameters. For example, a thread might receive a message indicating that a touch screen has been activated, and the message parameter might indicate the x and y coordinates of the user's action. In another type of message, the parameter might include a pointer or handle to a data structure, window, or other object.

          Interprocess Synchronization

          Walls between processes protect against the casual exchange of data; however, two processes may need to communicate at times. Windows CE does not support the DuplicateHandle function or handle inheritance. You can use WM_COPYDATA or a memory-mapped object to communicate between processes.


          WM_COPYDATA sends blocks of data from one process to another, but is limited to copying fixed blocks of data at a specific time.

          WM_COPYDATA wParam = (WPARAM)(HWND) hwndFrom; lParam = (LPARAM)(PCOPYDATASTRUCT) pcds;

          The hwnd parameter is the handle to the window passing the data, and the pcds parameter is a pointer to a COPYDATASTRUCT structure that contains the data to be passed.

          Memory-Mapped Objects

          Two processes can use a named memory-mapped object to allocate a shared block of memory that is equally accessible to both processes at the same time. With a named memory-mapped object, the system keeps a proper use count on the object to prevent one process from freeing the block while the other process is still using the block.

          To synchronize processes when they are reading and writing data in the memory block, you can use named event and mutex objects. Once a process has created a named event or mutex object, other processes can use the name in a call to the CreateEvent or CreateMutex function to open a handle to the object. Because multiple processes can have handles to the same event or mutex object, these objects can be used to synchronize processes. Note that name comparison is case sensitive.

          When using a memory-mapped object for interprocess communication, you only need to share memory, which means you are not required to have a memory-mapped file created by the CreateFileForMapping function. You just need to pass -1 in the handle field of the CreateFileMapping function. You can then use the MapViewOfFile function to map a view to the object, which allocates system resources and returns a pointer to the memory-mapped object. You now have a cheap and effective way to access a region of memory from multiple processes. When you are finished, unmap the view by calling the UnmapViewOfFile function.

          The mapping functions are defined as follows:

          HANDLE CreateFileMapping (HANDLE hFile, NULL, DWORD flProtect,
          DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName);
          LPVOID MapViewofFile (HANDLE hFileMappingObject, DWORD dwDesiredAccess,
          DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow,
          DWORD dwNumberOfBytesToMap);
          BOOL UnmapViewofFile (LPCVOID lpBaseAddress);
          Parameter Description
          hFile Specifies handle to an open memory-mapped file. Set to -1 if you are not using a memory-mapped file.
          flProtect Specifies protection flags for the virtual pages that will contain the file data.
          dwMaximumSizeHigh anddwMaximumSizeLow Specify expected maximum size of the object.
          lpName Specifies name of object.v
          hFileMappingObject Specifies handle of the mapping object.
          dwDesiredAccess Specifies access rights: FILE_MAP_READ, FILE_MAP_WRITE, or FILE_MAP_ALL.
          dwFileOffsetHigh anddwFileOffsetLow Specify the starting point in the file where the view starts.
          dwNumberOfBytesToMap Specifies the size of the view window.
          lpBaseAddress Specifies the pointer to the base address of the view.