The New U: What's New in USER32

Kyle Marsh
Microsoft Developer Network Technology Group

Created: September 29, 1993

Click to open or copy the files in the THRD32 sample application.

Abstract

This article discusses some of the new features available in USER32, the 32-bit version of USER found in the 32-bit versions of the Microsoft® Windows® operating system. Porting issues from Win16, asynchronous input queues, thread-localized input states, single-instance applications, window classes, and Windows hooks are among the topics discussed.

Introduction

Windows NT™ has been released, Win32s™ is now available for Microsoft® Windows® version 3.1, and the next version of Windows (called Windows 95) is on its way. 32-bit environments for Windows are available now, and in a matter of months all Windows platforms will become 32-bit platforms. Now is the time to build new 32-bit applications and to port your existing 16-bit applications to 32 bits.

USER32 handles the window manager chores—for example, windows, menus, controls, hooks, and dialog boxes—for Windows. In 16-bit versions of Windows, USER16 handles these tasks. This article will discuss some of the differences between USER16 and USER32.

Porting Issues

Developers of 16-bit applications will find that one of the best features of USER32 is the ease in porting from USER16 to USER32.

Widened Functions

USER32 runs on the 32-bit versions of Windows, which use the Win32® application programming interface (API). This means that a number of data items that used to be 16 bits are now 32 bits. Handles are the most noticeable of these: All window handles are now 32-bit handles instead of 16-bit handles. Although this sounds like a significant change, it has almost no effect on most applications. All functions that took window handles still take window handles; the window handles just happen to be different now. The SetWindowText call still looks like this:

SetWindowText(HWND hWnd, LPSTR text) 

What about the LPSTR argument? Doesn't that need to be changed for Win32? Well, yes and no. In Win32 environments, all pointers are the same. There is no concept of near or far pointers, so LPSTR has the same meaning as NPSTR. Actually, you should replace all references to LPSTR and NPSTR with PSTR, which is a pointer to a string. Because this requires a lot of work for developers, the Win32 Software Development Kit (SDK) for Windows NT defines the following macros:

#define LPSTR PSTR
#define NPSTR PSTR

These macros eliminate the need for developers to change their source code for Win32 while leaving the source compatible with Win16. However, when Win32 becomes the norm, the references to LPSTR will be out of place. I prefer to use PSTR only for new applications I develop for Win32; I use LPSTR for existing applications and for applications that require 16-bit and 32-bit executables.

As a result, although most USER16 functions were widened to take 32-bit arguments, the application developer does not need to worry about any of them. Your USER16 calls for 16-bit applications will work just fine for USER32.

One area where the widening of arguments to 32 bits does affect your application is in window procedures. These procedures take four arguments, the third of which is often defined as:

WORD wParam

The name wParam implies a WORD in Hungarian notation. In Win32, this value changes to a 32-bit value and is defined as:

UINT uParam

Actually, the UINT type is portable between Win16 and Win32 because it is defined as an unsigned integer on both systems. Using this data type will leave your source code compatible with both 16-bit and 32-bit versions of Windows.

The application developer has to decide how to deal with the new wParam. Changing the name of the parameter to uParam provides a visual clue in a ported (or new) application that the code was ported from (or developed for) the Win32 environment. It also complies with Hungarian notation. However, many developers prefer to continue using Windows 3.1 argument names, so they use:

UINT wParam

To simplify matters, the Windows header files define a macro that allows the developer to keep the old name, maintain consistency with Hungarian notation, and ensure portability between 16-bit and 32-bit applications:

WPARAM wParam

where WPARAM is defined as:

typedef UINT WPARAM

in both Win16 and Win32.

Removed and Obsolete Functions

The first thing to note when moving to USER32 is that any undocumented functions your application may be using will probably not work in USER32.

The following functions were eliminated in Win32. Applications that call these functions will need to remove the references from the source code.

Functions Replaced by Macros

Some of the functions that Win32 made obsolete can be simulated by calling other functions in USER32. The Win32 header files define the following macros to make this transition easier and to increase source code compatibility between Win16 and Win32:

Obsolete function New function referenced by macro
AnsiLower CharLower
AnsiLowerBuff CharLowerBuff
AnsiNext CharNext
AnsiPrev CharPrev
AnsiUpper CharUpper
AnsiUpperBuff CharUpperBuff
CopyCursor CopyIcon
DefHookProc CallNextHookEx
EnumTaskWindows EnumThreadWindows
GetNextWindow GetWindow
GetSysModalWindow Defined to NULL
GetWindowTask GetWindowThreadProcessId
PostAppMessage PostThreadMessage
SetSysModalWindow Defined to NULL

Functions Moved out of USER32

The following functions were moved out of USER32 and into other components of Windows NT:

Functions Moved into USER32

The dynamic-data exchange (DDE) functions, which had been in a separate dynamic-link library (DLL), have been moved into USER32.

Changed Messages

The move to 32 bits made it necessary to change the way some messages used wParam and lParam. In Win16, wParam and lParam consisted of three 16-bit locations for data items. Some messages would then pack three data items into these locations. Win32 has four 16-bit locations. The additional location does not require a change to data item packaging, but some of the data items have changed from 16 bits to 32 bits, so they no longer fit where they used to. Most commonly, handles were packed into the lParam argument. For example, the WM_COMMAND message in Win16 uses wParam and lParam as follows:

In Win32, WM_COMMAND now uses:

This change was required because the hWnd now needs the entire lParam argument.

In the following messages, wParam and lParam were repackaged to take the 32-bit data types into account.

The WM_CTLCOLOR message had three 16-bit data items that could not be repacked into the two 32-bit parameters available. In this case, Win32 replaces the WM_CTLCOLOR message with the following messages:

Window and Class Words

Many applications use the GetClassWord, SetClassWord, GetWindowWord, and SetWindowWord functions to get and set information for classes and windows. These functions still exist in USER32, but the data values for which they were used have become 32-bit data types. As a result, the long versions of the GetClassLong, SetClassLong, GetWindowLong, and SetWindowLong functions must be used with the new constants defined for the offsets.

USER16 word constants USER32 long constants
GCW_CBCLSEXTRA GCL_CBCLSEXTRA
GCW_CBWNDEXTRA GCL_CBWNDEXTRA
GCW_HBRBACKGROUND GCL_HBRBACKGROUND
GCW_HCURSOR GCL_HCURSOR
GCW_HICON GCL_HICON
GCW_HMODULE GCL_HMODULE
GCW_STYLE GCL_STYLE
GWW_HINSTANCE GWL_HINSTANCE
GWW_HWNDPARENT GWL_HWNDPARENT
GWW_ID GWL_ID

The New USER32 Functions

When the USER32 API was being finalized, Windows 3.1 was also in the finalization stage. As a result, the functions for USER32 and USER16 are very similar. Many new Win32 functions were added to Windows 3.1:

In addition, USER32 was enhanced with dynamic data exchange management library (DDEML) functionality, so it contains a number of DDE functions that were not included in USER16.

Single-Instance Applications

One difference that developers will notice between Win16 and Win32 is that all Win32 applications run as single instances, even if another instance of the application is running. This is because all applications have separate address spaces under Win32. As a result, the hPrevInstance parameter passed to an application's WinMain procedure will always be NULL.

Generally, this makes life easier for the application developer. In Win16, the developer had to be aware of whether multiple instances of the application were necessary and take precautions to ensure that the application would work correctly when these instances shared the application's code segments. In Win32, the developer doesn't need to worry about multiple instances. No precautions are necessary to ensure that the application runs correctly.

Actually, the above statement is not completely true. No precautions are necessary for memory access, USER32, or GDI32, but the developer must still be cautious. For example, if the application accesses a data file, the developer must ensure that two instances of the application will be able to access the file concurrently or use serialized access. To avoid this effort, the developer can use the FindWindow function to ensure that only one instance of the application is running:

if (hWndApp = FindWindow(szAppMainWindowClassName, NULL)) {
   hWndPopup = GetLastActivePopup(hWndApp);

   BringWindowToTop(hWndApp);
   if ( IsIconic(hWndPopup) )
      ShowWindow(hWndPopup, SW_RESTORE);
   else
      SetForegroundWindow(hWndPopup);

   return FALSE;
}

This works fine for Win32s applications, but Windows NT introduces an additional problem. In Windows NT, an application can be launched multiple times, in rapid succession. For example, if you start an application from a batch file using the start command, Windows NT starts the processes one after another, but so quickly that one process starts before the previous process has finished initializing. In this case, the FindWindow function will not find another occurrence of the window class, although another instance of the application exists. To avoid this, the application must use a WIN32 synchronization object such as Mutex, as follows:

hMutex = CreateMutex(NULL, FALSE, "ThreadSampleMutex" );
if ( WaitForSingleObject(hMutex, 10000) == WAIT_TIMEOUT ) {
    //
    // There is another instance out there, but it is taking too long to
    // locate, just exit.
    return FALSE;
}

if (hWndApp = FindWindow(szAppMainWindowClassName, NULL)) {
   hWndPopup = GetLastActivePopup(hWndApp);

   BringWindowToTop(hWndApp);
   if ( IsIconic(hWndPopup) )
      ShowWindow(hWndPopup, SW_RESTORE);
   else
      SetForegroundWindow(hWndPopup);

   ReleaseMutex(hMutex);
   CloseHandle(hMutex);
   return FALSE;
}

if (!Init(hInstance, cmdShow)) {
   ReleaseMutex(hMutex);
   CloseHandle(hMutex);
   return 1;
}

ReleaseMutex(hMutex);
CloseHandle(hMutex);

Asynchronous Input Queues

One of the best features of USER32 is its input processing model. Unfortunately, this feature is currently available only in Windows NT.

The Bad Old Days

The 16-bit versions of Windows supplied their own multitasking capabilities because the underlying operating system, MS-DOS®, did not supply any. Two design decisions limited the capabilities of Windows:

  1. Windows was designed as a non-preemptive operating system. This means that Windows did not interrupt an application to allow another application to run.

  2. Windows had only one input queue. This single input queue held all the input from the user until an application removed the events from the queue.

While limiting the power of Windows, these decisions did make sense for the processors available when Windows was first released. At the time, the 8088 was the most common PC processor, and it was overwhelmed quickly. The non-preemptive environment eliminated the need for the processor to do a lot of overhead work, such as keeping track of which process should be running, when it should be interrupted, and switching between processes. Only a minimum amount of switching was necessary to support the interrupts generated by input devices such as the mouse and keyboard. Having a single input queue simplified the requirements for managing the input while another process was running. It also kept the overhead to a minimum and improved the performance of the system.

This design also made type-ahead functionality very easy to implement. Because all events were placed into the system queue as they occurred and were taken out of the queue by their target applications in the same order, a perfect type-ahead capability was always available.

Windows implemented multitasking by requiring applications to process messages by calling GetMessage or PeekMessage and to get input from the user as well as from other events. When Windows was executing GetMessage or PeekMessage, the system would perform any task-switching that was necessary. At any given time, only one application (the application that had a GetMessage or PeekMessage function return) would be running. All other applications were blocked until one of these functions returned.

Figure 1. Event Processing in Win16

The main problem with this design was its lack of robustness. If one application stopped processing messages, the whole system would grind to a halt. This could happen when an application initiated a long process such as reading a set of database records, printing, repaginating, saving a file, reading a file, or any number of other operations. The real problem was that when an application hung, it stopped processing messages, so the whole system hung. Later versions of Windows added the capability to detect applications that had stopped processing messages, but this was not always successful.

Another problem was that if events came in while an application was slow in processing messages, the queue could fill up and Windows could not store any new messages.

To prevent the system from being brought to its knees, applications had to be very careful when they handled long processes. The most common solution was to put a long process in a PeekMessage loop. PeekMessage would be called throughout the long process so that other applications and the system could have a chance to run and do their own processing. These loops were sometimes very troublesome to develop. Because of the development burden, some developers would just go ahead with their long processes and allow the system to be unresponsive to the user.

Happy Days Are Here Again

USER32 had two input-processing goals:

  1. No application should be able to interfere with USER32's ability to direct input to any application. This means that:
  2. The input processing semantics must be compatible with the Win16 semantics.

USER32 has achieved these goals, and Win32 is much more robust than Win16. An application cannot bring the whole system to a halt when it hangs or goes into a long process. To make this goal a reality, USER32 takes advantage of the preemptive features of Windows NT.

Windows NT was designed from the very beginning to include preemptive multitasking. The target processors for this operating system are much more powerful than the old 8088, so they can handle the background requirements of a preemptive system. In Windows NT, the basic processing unit is called a thread. Every process running on Windows NT has at least one thread. The way USER32 interacts with these threads allows the system to be more robust.

When a thread calls a USER32 or GDI32 function, the system creates an input queue for the thread. The thread may or may not use the input queue, but the overhead to create the input queue is incurred at this time. The advantage is that no overhead is incurred if the thread does not call USER32 or GDI32 functions.

USER32 also takes advantage of Windows NT's threads by creating a high-priority thread, known as the raw input thread, which is always running to process the user's keyboard and mouse input. The user's events are placed into a system queue by the device drivers, just as they are in Win16, and the raw input thread immediately transfers the events to the thread input queue for which the input was intended. Unlike in Win16, thread input queues are dynamically resized so they can store new messages for an application even if the application is currently processing messages. The number of messages that can be stored is limited only by the amount of available memory in the system. An application can process messages whenever convenient without affecting USER32's ability to direct input to another thread's input queue. The user can always switch away from an application that is hung or performing a long process and do other things. For example, a user can switch to a mail program and check mail while a spreadsheet is printing. This is possible regardless of how the spreadsheet is programmed. If an application hangs, the user simply switches to the task list and ends the task from there.

Figure 2. Input processing in Win32

Asynchronous input queues make the system robust, which is what they were intended to do. An application developer may never have to write another PeekMessage loop. No matter how an application is implemented, the system will continue to react to the user, other applications will be able to process, and everything will be great. A badly written application may, however, slow the system down considerably, leading the user to think that the application is hung. The robustness of Windows NT means that a badly written application will not stop the system from running, but it doesn't mean that the system will run well. The difference between badly written applications in Windows NT and Windows 3.1 is that the application's impact on the system is not fatal in Windows NT because asynchronous input queues prevent the system from hanging when an application hangs.

Applications must handle long processes correctly for two reasons:

Being responsive to the user

When an application does not process input from the user, the user may think that the application is hung. Users like to get constant feedback to ensure that their applications are still working. Even when an application displays its status (and the user knows that the application is working), the user needs a way to interrupt, cancel, or change the parameters of the process (for example, cancel a print job). For this reason, applications should always be able to accept some kind of user input, even if this merely means supporting a cancel button while printing a document.

Another problem that may occur when an application stops processing input is that the appearance of the application may get out of date. The user can switch to another application while a long process is being performed, making it possible for other windows to be drawn over the unresponsive application's window. As the user moves and closes other windows, areas of the unresponsive application's window will need repainting, but because the application is not processing messages, it cannot process the WM_PAINT messages it receives from Windows. This can make the screen appear very confusing. USER32 will eventually detect applications that are not processing messages and will repaint these applications' windows with a border and clean background, but without a menu or any other information. USER32's repainting of the window makes the screen appear less confused and draws the user's attention to the inactive application, but may make the unresponsive application look bad.

Allowing the system to share the processor

By default, Windows NT is configured so that all the threads belonging to the application that owns the foreground window (the window to which the user is directing input) have a higher priority than threads belonging to other applications. All of the threads that belong to the application that owns the foreground window are foreground threads. (Note that the Win32 SDK for Windows NT incorrectly defines the foreground thread as only the thread that owns the foreground window.) All other threads in the system are known as background threads.

The system can become most responsive to the user's input if the foreground threads' base priority is boosted from 7 to 9. The user can disable this feature by using the Control Panel's System application and changing the tasking options. I don't expect many users to change these tasking parameters because these changes are not very intuitive.

If a foreground thread goes into a long process and stops handling messages, the rest of the system will practically stop, because lower priority threads do not receive much processor time when a higher priority thread has processing to do. In fact, my results showed that background threads received a time slice only about once a minute. This certainly has an adverse effect on the way the system performs many tasks. To see how a foreground thread affects the system when it stops handling messages, see the THRD32 sample application that accompanies this technical article.

Start two instances of THRD32. Arrange the instances so you can see both windows at the same time. When you start THRD32, it is in its PeekMessage mode. This mode displays the number of milliseconds between PeekMessage returns as its bottom number and the longest time between PeekMessage returns as its top number. Click one of the THRD32 windows to make that window the foreground window. Now choose Hog the System from the State menu to set the state of that window. This causes THRD32 to go into a long process, displaying 1 to 500 in the top number and the time taken (in milliseconds) in the bottom number. The code it is running looks like this:

for (i=0; i < 500; i++ ) {
   sprintf(buf, "%7.0d", i);
   SetWindowText(hwndFilter, buf);

   NowTime = (double)timeGetTime();
   Interval = NowTime-LastTime;
   sprintf(buf, "%7.0lf", Interval);
   SetWindowText(hwndNoFilter, buf);

   NoFlickUpdate(hwndNoFilter);
   NoFlickUpdate(hwndFilter);
}

The NoFlickUpdate routine simply paints the current window text to the screen. At no point in this loop does THRD32 handle messages.

When running the hogging instance of THRD32, you will see that the timing instance stops displaying numbers. If you do not switch between applications, everything will wait either until the hogging THRD32 finishes its process or until about a minute has passed without any other thread getting processor time. The Windows NT scheduler will give lower priority threads some processor time after about a minute has passed without any, but the time will be short and this won't happen again for another minute. (I know this sounds confusing, but you'll understand what I mean when you run the hogging instance of THRD32.) When the hogging THRD32 finishes, the timing THRD32 will again display numbers, but the longest time between PeekMessages will be about the same as the hogging THRD32 took to complete its process, or about 60,000 milliseconds, whichever is less. Set the hogging THRD32 off again, but this time switch to the other THRD32 before the hog finishes. As soon as you switch, the timing THRD32 starts displaying numbers again because the timing THRD32 is now the foreground window. Notice, however, that the hogging THRD32 also displays numbers. This is because THRD32 is in a PeekMessage loop when timing PeekMessages, which allows other processes to run as they do in Win16. This is basically the same old PeekMessage loop used in Win16:

// I am taking some comments out of this fragment to make it easier to 
// read here. For the full source, look at MAIN.C in the THRD32 sample.
do {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT) break;
        if (!TranslateAccelerator(hwndMain, hAccTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

    } else {
     if ( nState == PEEKLOOP )  {
        double Interval;
        double NowTime;

        NowTime = (double) timeGetTime();
        Interval = NowTime-LastTime;

        sprintf(buf, "%7.0lf", Interval);
        SetWindowText(hwndNoFilter, buf);

        if ( Interval <= LastInterval ) {
          Interval = LastInterval;
        }

        sprintf(buf, "%7.0lf", Interval);
        SetWindowText(hwndFilter, buf);

        LastTime=NowTime;
        LastInterval = Interval;
     }
     else {
        WaitMessage();
     }
    }
} while (1);

Other ways to handle long processes

In USER32, using the PeekMessage loop as described above is not the only way to handle long processes. The code shown above works fine, which means that all applications that have gone to the trouble of being well behaved in Win16 will remain well behaved in Win32. For new code, however, there are other ways to handle the situation. The sections below describe three methods to handle a long process: using the Sleep function, adding a PeekMessage loop to the process itself, and using a thread.

Using Sleep

One way to handle a long process is simply to give other applications a chance to run. The easiest method I have seen for doing this uses the Sleep function. Calling this function with a value of 1 millisecond will give threads that are running with a lower priority some processor time. Try the hogging THRD32, but this time use the Hog with Sleep state. Notice that the timing THRD32 continues to display numbers, and the hogging THRD32 runs slower than it did in the Hog the System state. This is expected because the timing THRD32 now shares the processor with the other threads. Change the size of the hogging window to measure the effect of the Sleep function call on the performance of THRD32. Small windows run faster than large windows. If you make the window very large, you will see how a longer process might be affected by Sleep. The effect of Sleep is actually determined by how the function is called. In this case, THRD32 calls Sleep 500 times during its processing:

for (i=0; i < 500; i++ ) {
   sprintf(buf, "%7.0d", i);
   SetWindowText(hwndFilter, buf);

   NowTime = (double)timeGetTime();
   Interval = NowTime-LastTime;
   sprintf(buf, "%7.0lf", Interval);
   SetWindowText(hwndNoFilter, buf);

   NoFlickUpdate(hwndNoFilter);
   NoFlickUpdate(hwndFilter);
   Sleep(1);
}

The disadvantage of this technique is that the application that performs the process will not be responsive to the user. The application will still seem dead, which should be avoided if possible.

Using a PeekMessage loop

Another way to handle a long process is to add a PeekMessage loop to the process itself:

for (i=0; i < 500; i++ ) {
   if ( PeekMessage (&msg, NULL, 0, 0, PM_REMOVE) ) {
      TranslateMessage (&msg);
      DispatchMessage (&msg);
   }
   sprintf(buf, "%7.0d", i);
   SetWindowText(hwndFilter, buf);

   NowTime = (double)timeGetTime();
   Interval = NowTime-LastTime;
   sprintf(buf, "%7.0lf", Interval);
   SetWindowText(hwndNoFilter, buf);

   NoFlickUpdate(hwndNoFilter);
   NoFlickUpdate(hwndFilter);
}

This technique allows other processes to get processor time, and also lets the current application respond to user input. With the code above, however, you must make sure that the application does not exit until the long process is finished. This can happen if the application gets a WM_CLOSE message and calls DestroyWindow or DefWindowProc without checking to see if the long process is finished. The THRD32 sample keeps a variable, nState, which it checks before calling DestroyWindow. If nState indicates that a long process has not completed, THRD32 does not call DestroyWindow.

case WM_CLOSE:
    if ( nState >= HOG ) {
       MessageBeep(0);
       return FALSE;
    }
    else {
       DestroyWindow(hWnd);
    }
    break;

If the application exits before the code above completes its long process, the process will be left on the system (a) forever, (b) until the system is shut down, or (c) until the user uses the PVIEW application to kill the process. This is because the application exits from the PeekMessage loop in the long process, but does not exit from its main GetMessage or PeekMessage loop in WinMain. All of the windows belonging to the process are destroyed, but the process itself remains. This is actually not new to Win32; if the same situation occurred in Win16, the task would also be left running forever.

Using a thread

The last method I will describe for handling a long process involves using a thread to perform the task while the original thread continues to respond to the user. First, I tried to use CreateThread to create a new thread that would perform the long process while the original thread continued with user-interface processing. I made sure that the application did not exit while the thread was running. In fact, I used the same mechanism I described for the PeekMessage loop in the previous section. You might notice that although I had two threads accessing the same global data element, I did not use a synchronization object for it. In this case, this is OK because once the thread is started, only the thread will change the value of the data element. In your own application, you should always check to make sure that multiple threads can access global objects without any problems. Having coded all that, I went ahead and launched the application. When I started the hogging instance's Hog with Thread state, I was surprised to see that it had the same effect as not using a thread: The rest of the system stopped while the long process was running. I had thought that putting the processing into a background thread would solve my problem. Actually it does, but I was not using a background thread. Remember that the system raises the priority of all threads in the foreground process, so what I was starting was merely another foreground thread. This is important to remember: If your application uses a second thread to perform a long task, and that thread doesn't allow lower priority threads to run, the background threads will be starved for processor time.

To solve the problem, I could have added a call to Sleep as shown in the "Using Sleep" section above. I tried something different instead. I used SetThreadPriority to lower my thread's priority. This effectively made my thread a background thread, because it lost its foreground priority boost. I ran the application again, and this time things looked much better until I switched to another application. When I switched away from my application, all the threads in it lost their foreground boost. This gave my self-made background thread an even lower priority. As a result, it was starved for processor time. In some cases, this may be the desired behavior. For example, if the user is not entering text into a word-processing application, there is not much need to repaginate the document in the background. In many other instances, however, dropping into this much lower priority is not desired. To fix this, I added code to handle the WM_ACTIVATEAPP message and to set the priority of my background thread depending on whether my application was the foreground or a background application. Here is the final code that worked for THRD32:

   case WM_ACTIVATEAPP:
      if ( nState == HOGTHREAD ) {
         if ( uParam ) {
            SetThreadPriority(hThread, THREAD_PRIORITY_LOWEST );
         }
         else {
            SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL );
         }
      }
      else {
         return DefWindowProc(hWnd, msg, uParam, lParam);
      }
      break;

   case IDM_HOGTHREAD:
         if ( nState >= HOG ) {
            MessageBeep(0);
            break;
         }

         nState = HOGTHREAD;
         LastTime = (double)timeGetTime();
         LastInterval = 0.0;
         SetWindowText(hWnd,"Hog the System - Thread");
         hThread = CreateThread(NULL, 0, HogThread, hWnd, 0, &idThread);
      break;

LRESULT HogThread(HWND hWnd)
{
   int i;
   double Interval;
   double NowTime;

   SetThreadPriority(hThread, THREAD_PRIORITY_LOWEST );

   for (i=0; i < 500; i++ ) {
      sprintf(buf, "%7.0d", i);
      SetWindowText(hwndFilter, buf);

      NowTime = (double)timeGetTime();
      Interval = NowTime-LastTime;
      sprintf(buf, "%7.0lf", Interval);
      SetWindowText(hwndNoFilter, buf);

      NoFlickUpdate(hwndNoFilter);
      NoFlickUpdate(hwndFilter);
   }
   nState = 0;

   SetWindowText(hwndFilter, "Done");

   NowTime = (double)timeGetTime();
   Interval = NowTime-LastTime;
   sprintf(buf, "%7.0lf", Interval);
   SetWindowText(hwndNoFilter, buf);

   ExitThread(0);
   return 0;
}

A Comment on Asynchronous Input Queues

Asynchronous input queues are a great feature in Windows NT. They allow the system to deal cleanly with a badly written or hung application. They are not, however, a cure-all for badly written applications. Badly written applications will affect the overall performance of Windows NT just as they affected the performance of 16-bit versions of Windows. However, the effect will not be as catastrophic as before, and it will never be fatal.

Thread Localized Input States

In Windows NT, input from the user is assigned to the correct thread when the input is generated by the user. This means that there can be input available for more than one application at any point in time. In fact, multiple applications can process input simultaneously. Each application needs its input state, the focus window, keyboard state, mouse capture, and active window to reflect the state that existed when the input is generated. Win16 had only one input queue, so the input state functions reflected the state of that queue, effectively giving a global perspective of the input state. Now that Win32 has many input queues, the input state functions must work specifically for each thread instead of working for the whole system. This means that each input queue owned by an individual thread has its own focus state, keyboard state, mouse capture state, and active window state.

As a result, the input-state functions now work differently on Windows NT than they did on 16-bit versions of Windows:

Mouse Capture

In Win16, only one window in the system could set the mouse capture at any given time. In Win32, only one window in a thread can have the capture, but another thread can also capture the mouse. In Win16, all mouse events were sent to the capture window. In Win32, only mouse events that occur over the windows owned by the thread that set the mouse capture are sent to the capture window. In other words, an application cannot use SetCapture to get mouse messages meant for other applications.

16-Bit Applications in Windows NT

Asynchronous input queues are a foundation of USER32, and the Windows on Windows (WOW) box uses them like any other application. However, because the purpose of the WOW box is to emulate the behavior of Win16 on Windows NT, the WOW box does not present the asynchronous input queues to the 16-bit Windows applications it runs. Instead, it emulates the Win16 system queue using its thread input queue.

Figure 3. The Win16 system queue in Windows NT

Window Classes

Win16 has three types of classes: system global, application global, and application local. For compatibility, these classes appear to exist in Win32; however, all Win32 classes are actually local classes for the application. Even if an application uses a custom control that registers itself as an application global class, the class is really local to the application. This is because each process has its own address space and any DLLs the application uses are mapped into that address space.

For the most part, this discrepancy will not affect applications because classes will work exactly as they did in Win16. There is one important difference, however. In Win16, if you subclassed a system global class, all windows created from that class would be subclassed regardless of the application that created them. This is not the case in Win32. Subclassing a system global class—for example, the EDIT class—affects the edit controls for the application only. This can be a very handy capability in Win32. Let's assume that you want to add some capability to every edit control in your application. In Win16, you need to keep track of where each edit control was created or create a superclass of the edit control. You could also locally superclass the edit control, but that is not recommended. In Win32, you can simply subclass the class itself, and the problem is solved.

Subclassing Window Instances

Subclassing a window instance works as it did in Win16 with one major exception: You cannot subclass windows that do not belong to your application because of the separate address spaces Windows NT uses. There are a few ways to get around this; they all involve getting a procedure in the address space of the other process, for example, by using a DLL with a systemwide hook. The address of your subclass procedure will have no meaning in the address space of another application, and the SetWindowLong function will not allow the subclassing to take place.

Windows Hooks

Win32 hooks incorporate a number of changes, including the following:

For more information on how hooks work in Win32, see the "Win32 Hooks" article on the Microsoft Developer Network CD.

What Else Is New?

This article just touched on the most important new features of USER32. In upcoming articles, I will go into more detail on some of these features, such as using threads with USER32, and will introduce additional new USER32 features such as Unicode™.