Freedom911
Freedom911

Reputation: 620

program for keyboard in windows

I am writing a program for displaying all the information that the windows sends the window procedure for 8 different keyboard messages. Here is the code

/*--------------------------------------------------------
   KEYVIEW1.C -- Displays Keyboard and Character Messages
                 (c) Charles Petzold, 1998
  --------------------------------------------------------*/   

#include <windows.h>

    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("KeyView1") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;

         wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;

         if (!RegisterClass (&wndclass))
         {
              MessageBox (NULL, TEXT ("This program requires Windows NT!"), 
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }

         hwnd = CreateWindow (szAppName, TEXT ("Keyboard Message Viewer #1"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              NULL, NULL, hInstance, NULL) ;

         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;

         while (GetMessage (&msg, NULL, 0, 0))
         {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
         }
         return msg.wParam ;
    }

    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static int   cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar ;
         static int   cLinesMax, cLines ;
         static PMSG  pmsg ;
         static RECT  rectScroll ;
         static TCHAR szTop[] = TEXT ("Message        Key       Char     ")
                                TEXT ("Repeat Scan Ext ALT Prev Tran") ;
         static TCHAR szUnd[] = TEXT ("_______        ___       ____     ")
                                TEXT ("______ ____ ___ ___ ____ ____") ;

         static TCHAR * szFormat[2] = { 

                   TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
                   TEXT ("%-13s            0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;

         static TCHAR * szYes  = TEXT ("Yes") ;
         static TCHAR * szNo   = TEXT ("No") ;
         static TCHAR * szDown = TEXT ("Down") ;
         static TCHAR * szUp   = TEXT ("Up") ;

         static TCHAR * szMessage [] = { 
                             TEXT ("WM_KEYDOWN"),    TEXT ("WM_KEYUP"), 
                             TEXT ("WM_CHAR"),       TEXT ("WM_DEADCHAR"), 
                             TEXT ("WM_SYSKEYDOWN"), TEXT ("WM_SYSKEYUP"), 
                             TEXT ("WM_SYSCHAR"),    TEXT ("WM_SYSDEADCHAR") } ;
         HDC          hdc ;
         int          i, iType ;
         PAINTSTRUCT  ps ;
         TCHAR        szBuffer[128], szKeyName [32] ;
         TEXTMETRIC   tm ;

         switch (message)
         {
         case WM_CREATE:
         case WM_DISPLAYCHANGE:

                   // Get maximum size of client area

              cxClientMax = GetSystemMetrics (SM_CXMAXIMIZED) ;
              cyClientMax = GetSystemMetrics (SM_CYMAXIMIZED) ;

                  // Get character size for fixed-pitch font

              hdc = GetDC (hwnd) ;

              SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
              GetTextMetrics (hdc, &tm) ;
              cxChar = tm.tmAveCharWidth ;
              cyChar = tm.tmHeight ;

              ReleaseDC (hwnd, hdc) ;

                   // Allocate memory for display lines

              if (pmsg)
                   free (pmsg) ;

              cLinesMax = cyClientMax / cyChar ;
              pmsg = (PMSG)malloc (cLinesMax * sizeof (MSG)) ;
              cLines = 0 ;
                                       // fall through
         case WM_SIZE:
              if (message == WM_SIZE)
              {
                   cxClient = LOWORD (lParam) ;
                   cyClient = HIWORD (lParam) ;
              }
                   // Calculate scrolling rectangle

              rectScroll.left   = 0 ;
              rectScroll.right  = cxClient ;
              rectScroll.top    = cyChar ;
              rectScroll.bottom = cyChar * (cyClient / cyChar) ;

              InvalidateRect (hwnd, NULL, TRUE) ;
              return 0 ;

         case WM_KEYDOWN:
         case WM_KEYUP:
         case WM_CHAR:
         case WM_DEADCHAR:
         case WM_SYSKEYDOWN:
         case WM_SYSKEYUP:
         case WM_SYSCHAR:
         case WM_SYSDEADCHAR: 

                   // Rearrange storage array

              for (i = cLinesMax - 1 ; i > 0 ; i--)
              {
                   pmsg[i] = pmsg[i - 1] ;
              }
                   // Store new message

              pmsg[0].hwnd = hwnd ;
              pmsg[0].message = message ;
              pmsg[0].wParam = wParam ;
              pmsg[0].lParam = lParam ;

              cLines = min (cLines + 1, cLinesMax) ;

                   // Scroll up the display

              ScrollWindow (hwnd, 0, -cyChar, &rectScroll, &rectScroll) ;

              break ;        // ie, call DefWindowProc so Sys messages work

         case WM_PAINT:
              hdc = BeginPaint (hwnd, &ps) ;

              SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
              SetBkMode (hdc, TRANSPARENT) ;
              TextOut (hdc, 0, 0, szTop, lstrlen (szTop)) ;
              TextOut (hdc, 0, 0, szUnd, lstrlen (szUnd)) ;

              for (i = 1 ; i < max (cLines, cyClient / cyChar - 1) ; i++)
              {
                   iType = pmsg[i].message == WM_CHAR ||
                           pmsg[i].message == WM_SYSCHAR ||
                           pmsg[i].message == WM_DEADCHAR ||
                           pmsg[i].message == WM_SYSDEADCHAR ;

                   GetKeyNameText (pmsg[i].lParam, szKeyName, 
                                   sizeof (szKeyName) / sizeof (TCHAR)) ;

                   TextOut (hdc, 0, (cyClient / cyChar -  i) * cyChar, szBuffer,
                            wsprintf (szBuffer, szFormat [iType],
                                 szMessage [pmsg[i].message - WM_KEYFIRST],                   
                                 pmsg[i].wParam,
                                 (PTSTR) (iType!=0 ? TEXT (" ") : szKeyName),
                                 (TCHAR) (iType ? pmsg[i].wParam : ' '),
                                 LOWORD (pmsg[i].lParam),
                                 HIWORD (pmsg[i].lParam) & 0xFF,
                                0x01000000 & pmsg[i].lParam ? szYes  : szNo,,// for extended key flag

                                 0x20000000 & pmsg[i].lParam ? szYes  : szNo,
                                 0x40000000 & pmsg[i].lParam ? szDown : szUp,//Previous key state
                                 0x80000000 & pmsg[i].lParam ? szUp   : szDown)) ;//transition state
              }
              EndPaint (hwnd, &ps) ;
              return 0 ;

         case WM_DESTROY:
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

My problem is that I can't understand the code.I tried my best .

In the line

static TCHAR * szFormat[2] = {

               TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
               TEXT ("%-13s            0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;

what is happening ?I understood the first line but not the second one.

in TextOut function

what does szMessage [pmsg[i].message - WM_KEYFIRST], means
and what the codes used 0x01000000
and why we used specifically &0XFF with HIWORD (pmsg[i].lParam) & 0xFF,

I know these are lot of questions but none of them were explained in the book .Please could anyone answer atleast one of the questions. Any help would be appreciated

Upvotes: 0

Views: 379

Answers (1)

Cody Gray
Cody Gray

Reputation: 244772

In the line

static TCHAR * szFormat[2] = {                
             TEXT ("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
             TEXT ("%-13s            0x%04X%1s%c %6u %4d %3s %3s %4s %4s") } ;

what is happening? I understood the first line but not the second one.

This is not really Windows-specific. It is just an array of C-style strings, which are themselves pointers to a nul-terminated array of characters.

  • It is declared static so that it is only initialized once when the function is first called, and maintains its value across subsequent calls.

  • TCHAR is a Windows typedef for either char or wchar_t, depending on whether or not you're building a Unicode application. So TCHAR * is equivalent to either char * or wchar_t *.

  • The name szFormat is systems hungarian notation, common in Win32 programming and used extensively by Petzold. The sz means it is a nul (zero)-terminated string.

  • The [2] part just makes it an array that contains two elements. An array that itself contains arrays.

  • TEXT is a function-like macro that is used to declare string literals appropriately, depending on the build configuration. In Unicode builds, an L is appended to the beginning of the string literal to make it a wide string. In non-Unicode builds, nothing is appended.

  • The strings themselves are just format strings, like you might use with the printf function in standard C. Except that Petzold's code is using it with wsprintf, the Windows version of this C function. Basically, all of the % parts are placeholders that are filled in with the values of variables you specify. They have special decorations to provide more extensive control over the formatting of the output string.

case WM_SIZE:

       if (message == WM_SIZE)
       {
             cxClient = LOWORD (lParam) ;
             cyClient = HIWORD (lParam) ;
        }

What does message == WM_SIZE mean?

I have no idea. That looks like a bug to me, actually. Inside of the WM_SIZE case, you already know that message is WM_SIZE. It is therefore pointless to check it again. Just pretend it isn't there.

what does szMessage [pmsg[i].message - WM_KEYFIRST], mean?

From the name and the variable declaration at the top, you know that szMessage is an array of C-style strings, just like we saw with szFormat. It contains the names of the window messages he's interested in, like WM_KEYDOWN and WM_KEYUP.

The stuff in brackets is an indexer into the array. It is just as if he had written szMessage[1] to access the second element in the array, except he's calculating the index of the item to be accessed dynamically using an expression.

The expression is built so that it matches the message name in the array up with the message ID known at runtime. You get the message ID from pmsg[i].message, and then subtract it from WM_KEYFIRST because that is the ID of the first keyboard message. That makes the message ID sync up the order of the message names in the string array.

and what the codes used 0x01000000
and why we used specifically &0XFF with HIWORD (pmsg[i].lParam) & 0xFF,

Those are just bit masks. Combining them with a bit value using the & operator (bitwise AND) masks off the specified bits.

As for their meaning, that is all covered in the documentation for the particular window message. For example, in the WM_KEYDOWN docs, it tells you the meaning of specific bits in the lParam value. By masking off those bits, you can isolate them to get at the individual pieces of data.

Petzold is taking advantage of the fact that all of the keyboard-related messages he's monitoring (listed in the szMessage array we just talked about) all use the same formatting for the lParam value. This is verifiable by looking at the respective documentation for each of those messages. For example, WM_CHAR is the same as we just saw for WM_KEYDOWN. That allows the code to be vastly simplified.

The purpose for this, of course, is to display this information about keyboard events to the user each time the window gets painted.

Upvotes: 5

Related Questions