Thread: message handler return value

  1. #1
    Registered User
    Join Date
    Mar 2011
    Posts
    596

    message handler return value

    Typically, a window procedure should return TRUE if a window message is handled.

    What constitutes handling? If my program sometimes takes no action in response to a window message (aside frome examining it), is the message still considered to have been handled?

    Also, what does the operating system (Windows) do differently in response to the state of the window procedure return value?

    -

  2. #2
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Typically, a window procedure should return TRUE if a window message is handled.
    That is wrong. Window Procedures do not have 'typical' return values. The value that must be returned is specified in the documentation for that message. For example, read the documentation on WM_CREATE very, very carefully. Zero can be returned in some instances, and -1 in others. Then read the documentation on WM_CTLCOLORSTATIC. In handling that message an HBRUSH must be returned. Every message is different, and the value that must be returned is, as I said, specified in the documentation for that message.

  3. #3
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    What constitutes handling? If my program sometimes takes no action in response to a window message (aside frome examining it), is the message still considered to have been handled?

    Also, what does the operating system (Windows) do differently in response to the state of the window procedure return value?
    Windows performs default handling for every message. The specifics of how it does that are proprietary. With your return value you can sometimes clue the operating system into taking no further action, or at least inform it that you've taken all the action that you specifically need to take regarding your application.

  4. #4
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Quote Originally Posted by freddie View Post
    That is wrong. Window Procedures do not have 'typical' return values. The value that must be returned is specified in the documentation for that message. For example, read the documentation on WM_CREATE very, very carefully. Zero can be returned in some instances, and -1 in others. Then read the documentation on WM_CTLCOLORSTATIC. In handling that message an HBRUSH must be returned. Every message is different, and the value that must be returned is, as I said, specified in the documentation for that message.
    Sorry, that wasn't stated well. I was referring to commands that I typically handle, and that should have been zero for the return value.

    Quote Originally Posted by freddie View Post
    Windows performs default handling for every message. The specifics of how it does that are proprietary. With your return value you can sometimes clue the operating system into taking no further action, or at least inform it that you've taken all the action that you specifically need to take regarding your application.
    That is more to my actual question.

    So if I have received a message and acted it upon it or not, as the program required, I should return a "message handled" value?

    -

  5. #5
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    So if I have received a message and acted it upon it or not, as the program required, I should return a "message handled" value?
    Yes. The question is really whether or not to pass the parameters received in the Window Procedure to DefaultWindowProc(). And there are two somewhat different ways of structuring a switch logic construct (if that's what you are using) within the Window Procedure to satisfactorily accomplish this. Beginners frequently mess this up, I've noted (I did myself when I was learning). If you mess it up Windows will sometimes do wierd things and you get into an undefined behavior situation. The key to getting it right is to carefully read the documentation for each message, i.e., look up WM_CREATE, WM_CTLCOLORSTATIC, etc., and then to look at your logic and see that you are either returning the correct number, or letting the Window Procedure execute a DefaultWindowProc() call. Then things get a bit more interesting when one gets into 'window subclassing', which is another topic. Then several more options and issues present themselves.

  6. #6
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Here are my interpretations of correct usages of the Window Procedure with regard to the handling of messages, return values, and calling DefaultWindowProc(). I'll present three interpretations of the same example which handles WM_CREATE, WM_PAINT, and WM_DESTROY. The examples open an "Output.txt" log file which provides information on which procedures or messages are being called/handled. First would be this...

    Code:
    // cl MegaFiddle4.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.lib
    #define UNICODE   // 64,000 Bytes With C Standard Library Loaded (LIBCMT.LIB) 
    #define _UNICODE       
    #include <windows.h>
    #include <cstdio>
    #include <tchar.h>
    FILE* fp=NULL;
    
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     switch(msg)
     {
        case WM_CREATE:
        {
             CREATESTRUCT* pCreateStruct=NULL;
             fp=fopen("Output.txt","w");
             fprintf(fp,"Entering fnWndProc() : case WM_CREATE\n");
             pCreateStruct = (CREATESTRUCT*)(lParam);
             fprintf(fp,"  pCreateStruct->cx = %d\n",pCreateStruct->cx);
             fprintf(fp,"  pCreateStruct->cy = %d\n",pCreateStruct->cy);
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             fprintf(fp,"Leaving fnWndProc() : case WM_CREATE\n\n");
             return 0;
        }    
        case WM_PAINT:
        {
             PAINTSTRUCT ps;
             HDC hDC=NULL;
             fprintf(fp,"Entering fnWndProc() : case WM_PAINT\n");
             hDC=BeginPaint(hwnd,&ps);
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             fprintf(fp,"  ps.rcPaint.right  = %d\n",ps.rcPaint.right);
             fprintf(fp,"  ps.rcPaint.bottom = %d\n",ps.rcPaint.bottom);
             fprintf(fp,"Leaving fnWndProc() : case WM_PAINT\n\n");
             EndPaint(hwnd,&ps);
             return 0;
        }    
        case WM_DESTROY:
        {
             fprintf(fp,"Entering fnWndProc() : case WM_DESTROY\n");
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             PostQuitMessage(0);
             fprintf(fp,"Leaving fnWndProc() : case WM_DESTROY\n\n");
             fclose(fp);
             return 0;
        }     
     }
    
     return (DefWindowProc(hwnd, msg, wParam, lParam));
    }
    
    
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
    {
     WNDCLASSEX wc={0};
     MSG messages;
     HWND hWnd;
    
     wc.lpszClassName = _T("Form1");
     wc.lpfnWndProc   = fnWndProc;
     wc.cbSize        = sizeof(WNDCLASSEX);
     wc.hInstance     = hInstance;
     wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
     RegisterClassEx(&wc);
     hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    
    #if 0
    Entering fnWndProc() : case WM_CREATE
      pCreateStruct->cx = 325
      pCreateStruct->cy = 300
      hwnd              = 00000000000904B2
    Leaving fnWndProc() : case WM_CREATE
    
    Entering fnWndProc() : case WM_PAINT
      hwnd              = 00000000000904B2
      ps.rcPaint.right  = 309
      ps.rcPaint.bottom = 262
    Leaving fnWndProc() : case WM_PAINT
    
    Entering fnWndProc() : case WM_DESTROY
      hwnd              = 00000000000904B2
    Leaving fnWndProc() : case WM_DESTROY
    #endif
    The way the above Window Procedure works is that a switch logic construct is looking for WM_CREATE, WM_PAINT, or WM_DESTROY. There is no default clause, and if the message isn't one of the above no code within the switch construct runs, and the message gets passed on to DefaultWindowProc() through the return statement of the procedure. If one of those messages is intercepted message handling code for that message is run and the procedure exits with a return of zero. If I'm going to use a switch logic construct to map messages to the code which I want to run for that particular message, that's the way I do it.

    There is another way to do it however, which I seem to see more of than what I do above. The alternate way goes something like so for the same example...

    Code:
    // cl MegaFiddle5.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.lib
    #define UNICODE   // 64,000 Bytes With C Standard Library Loaded (LIBCMT.LIB)
    #define _UNICODE       
    #include <windows.h>
    #include <cstdio>
    #include <tchar.h>
    FILE* fp=NULL;
    
    
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     switch(msg)
     {
        case WM_CREATE:
        {
             CREATESTRUCT* pCreateStruct=NULL;
             fp=fopen("Output.txt","w");
             fprintf(fp,"Entering fnWndProc() : case WM_CREATE\n");
             pCreateStruct = (CREATESTRUCT*)(lParam);
             fprintf(fp,"  pCreateStruct->cx = %d\n",pCreateStruct->cx);
             fprintf(fp,"  pCreateStruct->cy = %d\n",pCreateStruct->cy);
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             fprintf(fp,"Leaving fnWndProc() : case WM_CREATE\n\n");
             break;
        }    
        case WM_PAINT:
        {
             PAINTSTRUCT ps;
             HDC hDC=NULL;
             fprintf(fp,"Entering fnWndProc() : case WM_PAINT\n");
             hDC=BeginPaint(hwnd,&ps);
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             fprintf(fp,"  ps.rcPaint.right  = %d\n",ps.rcPaint.right);
             fprintf(fp,"  ps.rcPaint.bottom = %d\n",ps.rcPaint.bottom);
             fprintf(fp,"Leaving fnWndProc() : case WM_PAINT\n\n");
             EndPaint(hwnd,&ps);
             break;
        }    
        case WM_DESTROY:
        {
             fprintf(fp,"Entering fnWndProc() : case WM_DESTROY\n");
             fprintf(fp,"  hwnd              = %p\n",hwnd);
             PostQuitMessage(0);
             fprintf(fp,"Leaving fnWndProc() : case WM_DESTROY\n\n");
             fclose(fp);
             break;
        }     
        default:
        {
             return (DefWindowProc(hwnd, msg, wParam, lParam));  
        }    
     }
    
     return (0);
    }
    
    
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
    {
     WNDCLASSEX wc={0};
     MSG messages;
     HWND hWnd;
    
     wc.lpszClassName = _T("Form1");
     wc.lpfnWndProc   = fnWndProc;
     wc.cbSize        = sizeof(WNDCLASSEX);
     wc.hInstance     = hInstance;
     wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
     RegisterClassEx(&wc);
     hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    
    #if 0
    Entering fnWndProc() : case WM_CREATE
      pCreateStruct->cx = 325
      pCreateStruct->cy = 300
      hwnd              = 00000000000904B2
    Leaving fnWndProc() : case WM_CREATE
    
    Entering fnWndProc() : case WM_PAINT
      hwnd              = 00000000000904B2
      ps.rcPaint.right  = 309
      ps.rcPaint.bottom = 262
    Leaving fnWndProc() : case WM_PAINT
    
    Entering fnWndProc() : case WM_DESTROY
      hwnd              = 00000000000904B2
    Leaving fnWndProc() : case WM_DESTROY
    #endif
    Where this one differs from the first is that there is a default: clause to the switch construct, and if the message received isn't WM_CREATE, WM_PAINT, or WM_DESTROY, then the default is executed and DefaultWindowProc() is called in the return statement within the switch construct. If the message is one of the above the code for that message executes, and then a break statement is used to exit the switch. In that case execution proceeds to the return statement of the procedure, where a zero is returned. Note that there are unusual messages such as WM_CTLCOLORSTATIC, and others, where something other than zero is returned to indicate that the message has been handled. In such cases a return statement would be needed within the switch to return the correct entity. For reasons such as this, if I'm going to use the switch construct to map messages to message handling code, I use my first shown technique over this one, as at least in my mind it is clearer.

    However, except for posting example code such as the above in various Windows Programming Forums, I don't use the switch construct at all to map incoming messages to the code that handles a message. I use rather a for loop construct to do this and I use separate message/event handling functions. To folks just starting out I'm sure it looks much more complicated, but it really is the best way to do it. Here is what I do.

    A message such as WM_CREATE is an integral value (one (1) in the case of WM_CREATE), and if one were to create a separate message handling function to handle the WM_CREATE message such as this...

    Code:
    LRESULT fnWndProc_OnCreate(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam) 
    {
     CREATESTRUCT* pCreateStruct=NULL;
     
     fp=fopen("Output.txt","w");
     fprintf(fp,"Entering fnWndProc_OnCreate()\n");
     pCreateStruct = (CREATESTRUCT*)(lParam);
     fprintf(fp,"  WM_CREATE         = %d\n",WM_CREATE);
     fprintf(fp,"  pCreateStruct->cx = %d\n",pCreateStruct->cx);
     fprintf(fp,"  pCreateStruct->cy = %d\n",pCreateStruct->cy);
     fprintf(fp,"  hwnd              = %p\n",hwnd);
     fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");
    
     return 0;  
    
    }
    ...then the virtual address of that function represented by the symbol fnWndProc_OnCreate is also an integral value similar in every way to the WNDCLASS::lpfnWndProc as set down usually in WinMain. So since these are both integral values, i.e., an equate/define such as WM_CREATE, and the virtual address of a message handling function, then they can be combined into a struct like so...

    Code:
    struct            EVENTHANDLER
    {
     unsigned int     iMsg;
     LRESULT          (*fnPtr)(HWND, unsigned int, WPARAM, LPARAM);
    };
    So what you have in that struct/object is a relationship between the const value of a message as defined in the headers, and the runtime address of the event/message handling function that handles that message. Next step would be to create an array of these EVENTHANDLER objects like so...

    Code:
    LRESULT fnWndProc_OnCreate       (HWND, unsigned int, WPARAM, LPARAM);
    LRESULT fnWndProc_OnPaint        (HWND, unsigned int, WPARAM, LPARAM);
    LRESULT fnWndProc_OnDestroy      (HWND, unsigned int, WPARAM, LPARAM);
    
    const EVENTHANDLER               MainEventHandler[] =
    {
     {WM_CREATE,                     fnWndProc_OnCreate},
     {WM_PAINT,                      fnWndProc_OnPaint},
     {WM_DESTROY,                    fnWndProc_OnDestroy}
    };
    Constructed like so, a Window Procedure will never amount to more than this, no matter how many messages are handled, and no matter how many thousands of lines of code might be involved in any message handling function...

    Code:
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     for(size_t i=0; i<dim(MainEventHandler) ;i++)
     {
         if(MainEventHandler[i].iMsg==msg)
            return (*MainEventHandler[i].fnPtr)(hwnd, msg, wParam, lParam);
     }
    
     return (DefWindowProc(hwnd,msg,wParam,lParam));
    }
    ...where the dim macro is defined like so...

    Code:
    #define  dim(x)   (sizeof(x) / sizeof(x[0]))
    So what the above code is doing is using a for loop to iterate through the array of EVENTHANDLER objects trying to make a match on the EVENTHANDLER::iMsg member. If a match is made the event handling function is called through the function pointer member EVENTHANDLER::fnPtr(). The only refinement I've made to the above is I amalgamate the parameters to the Window Procedure into a WndEventArgs object like so...

    Code:
    struct          WndEventArgs
    {
     HWND           hwnd;
     HINSTANCE      hInst;
     WPARAM         wParam;
     LPARAM         lParam;
    };
    ....and my Window Procedures look like this...

    Code:
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     for(size_t i=0; i<dim(MainEventHandler) ;i++)
     {
         if(MainEventHandler[i].iMsg==msg)
         {
            WndEventArgs Wea;
            Wea.hwnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
            return (*MainEventHandler[i].fnPtr)(Wea);
         }
     }
    
     return (DefWindowProc(hwnd,msg,wParam,lParam));
    }
    This isn't really too much different from what all the various Class Frameworks do. Only I do it a bit more efficiently, I think. For example, in .NET the OnPaint message handling function has an arguement named PaintEventArgs. And the whole .NET concept of 'delegates' is roughly analogous to what I've done above with my function pointer setup. Anyway, here is my third iteration of the full program above illustrating this third technique...

    Code:
    // MegaFiddle6.h
    #ifndef MegaFiddle6_h
    #define MegaFiddle6_h
    
    #define  dim(x)                  (sizeof(x) / sizeof(x[0]))
    
    struct                           WndEventArgs
    {
     HWND                            hwnd;
     HINSTANCE                       hInst;
     WPARAM                          wParam;
     LPARAM                          lParam;
    };
    
    struct                           EVENTHANDLER
    {
     unsigned int                    iMsg;
     LRESULT                         (*fnPtr)(WndEventArgs&);
    };
    
    LRESULT fnWndProc_OnCreate       (WndEventArgs& wea);
    LRESULT fnWndProc_OnPaint        (WndEventArgs& wea);
    LRESULT fnWndProc_OnDestroy      (WndEventArgs& wea);
    
    const EVENTHANDLER               MainEventHandler[] =
    {
     {WM_CREATE,                     fnWndProc_OnCreate},
     {WM_PAINT,                      fnWndProc_OnPaint},
     {WM_DESTROY,                    fnWndProc_OnDestroy}
    };
    
    #endif
    Code:
    // cl MegaFiddle6.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.lib
    #define UNICODE    // 64,000 Bytes With C Standard Library Loaded (LIBCMT.LIB)    
    #define _UNICODE       
    #include <windows.h>
    #include <cstdio>
    #include <tchar.h>
    #include "MegaFiddle6.h"  // Note Include <<<  !!!
    FILE* fp=NULL;
    
    
    LRESULT fnWndProc_OnCreate(WndEventArgs& wea)
    {
     CREATESTRUCT* pCreateStruct=NULL;
     
     fp=fopen("Output.txt","w");
     fprintf(fp,"Entering fnWndProc_OnCreate()\n");
     pCreateStruct = (CREATESTRUCT*)(wea.lParam);
     fprintf(fp,"  WM_CREATE         = %d\n",WM_CREATE);
     fprintf(fp,"  pCreateStruct->cx = %d\n",pCreateStruct->cx);
     fprintf(fp,"  pCreateStruct->cy = %d\n",pCreateStruct->cy);
     fprintf(fp,"  wea.hwnd          = %p\n",wea.hwnd);
     fprintf(fp,"Leaving fnWndProc_OnCreate()\n\n");
    
     return 0; 
    }
        
    
    LRESULT fnWndProc_OnPaint(WndEventArgs& wea)
    {
     PAINTSTRUCT ps;
     HDC hDC=NULL;
     
     fprintf(fp,"Entering fnWndProc_OnPaint()\n");
     hDC=BeginPaint(wea.hwnd,&ps);
     fprintf(fp,"  wea.hwnd          = %p\n",wea.hwnd);
     fprintf(fp,"  ps.rcPaint.right  = %d\n",ps.rcPaint.right);
     fprintf(fp,"  ps.rcPaint.bottom = %d\n",ps.rcPaint.bottom);
     fprintf(fp,"Leaving fnWndProc_OnPaint()\n\n");
     EndPaint(wea.hwnd,&ps);
     
     return 0; 
    }
        
    
    LRESULT fnWndProc_OnDestroy(WndEventArgs& wea)
    {
     fprintf(fp,"Entering fnWndProc_OnDestroy()\n");
     fprintf(fp,"  wea.hwnd          = %p\n",wea.hwnd);
     PostQuitMessage(0);
     fprintf(fp,"Leaving fnWndProc_OnDestroy()\n\n");
     fclose(fp);
     
     return 0; 
    }
    
        
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     for(size_t i=0; i<dim(MainEventHandler) ;i++)
     {
         if(MainEventHandler[i].iMsg==msg)
         {
            WndEventArgs Wea;
            Wea.hwnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;
            return (*MainEventHandler[i].fnPtr)(Wea);
         }
     }
    
     return (DefWindowProc(hwnd,msg,wParam,lParam));
    }
    
    
    int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
    {
     WNDCLASSEX wc={0};
     MSG messages;
     HWND hWnd;
    
     wc.lpszClassName = _T("Form1");
     wc.lpfnWndProc   = fnWndProc;
     wc.cbSize        = sizeof(WNDCLASSEX);
     wc.hInstance     = hInstance;
     wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
     RegisterClassEx(&wc);
     hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    
    #if 0
    Entering fnWndProc_OnCreate()
      pCreateStruct->cx = 325
      pCreateStruct->cy = 300
      wea.hwnd          = 00000000001F01D6
    Leaving fnWndProc_OnCreate()
    
    Entering fnWndProc_OnPaint()
      wea.hwnd          = 00000000001F01D6
      ps.rcPaint.right  = 309
      ps.rcPaint.bottom = 262
    Leaving fnWndProc_OnPaint()
    
    Entering fnWndProc_OnDestroy()
      wea.hwnd          = 00000000001F01D6
    Leaving fnWndProc_OnDestroy()
    #endif
    And if you look at the way the abbreviated Window Procedure works you'll see its functioning exactly like the first instance of the program in terms of the calling of DefaultWindowProc() and returning the correct number from handled messages.

  7. #7
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Ok, thanks. Interesting way af handling messages.

    I use either of the first two examples. I keep the window procedure simple by passing off the message parameters to sub functions if further breakdown is needed. This way I avoid nested switches, especially nested switches that run on for more than a couple pages.

    -

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help with interrupt handler
    By afflictedd2 in forum C Programming
    Replies: 2
    Last Post: 02-18-2010, 10:35 AM
  2. Default Message Handler in a Class
    By Epo in forum Windows Programming
    Replies: 2
    Last Post: 07-23-2005, 09:28 PM
  3. Skeleton Window: Message Handler in a Class?
    By Epo in forum Windows Programming
    Replies: 8
    Last Post: 12-29-2003, 03:30 PM
  4. Memory handler
    By Dr. Bebop in forum C Programming
    Replies: 7
    Last Post: 09-15-2002, 04:14 PM
  5. ???Handler???
    By Garfield in forum Windows Programming
    Replies: 6
    Last Post: 09-15-2001, 07:20 PM

Tags for this Thread