Thread: Coordinating the window procedure with the program

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

    Coordinating the window procedure with the program

    This is supposed to draw a 1000 x 750 image in 10 horizontal sections. The purpose is to provide
    an indication that that something is in progress. (it takes a couple seconds to complete)

    As shown, it hangs up. It draws the first 1/10 section, then shows the complete image when it's been
    completed. Then it hangs.

    If I remove the UpdateWindow(hWnd) statement, it doesn't hang up. But it doesn't draw 10 horizontal
    sections either. It completes the image internally, then displays it all at once.

    Is it not possible to send multiple InvalidatRect() as shown? Should there be only one per window message?

    ifields[] is an array of RECT type structures storing the 10 horizontal sections.

    Code:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {	
    	switch(msg)
    	{	case WM_KEYDOWN:
    			keyinterpret(wParam);
    			break;
    
    		case WM_LBUTTONDOWN :
    			xyinterpret(lParam);
    			break;
    
    		case WM_PAINT:
    			updateclient(hWnd);
    			break;
    
    		case WM_CLOSE:
    			//if(MessageBox(hWnd, "Exit Pogram?", "", MB_YESNO | MB_DEFBUTTON2) == IDNO)
    				//return 1;
    			//else
    				terminate();	
    				DestroyWindow(hWnd);
    				return 0;
    			
    		case WM_DESTROY:
    			PostQuitMessage(0);
    			break;
    
    		default:
    			return DefWindowProc(hWnd, msg, wParam, lParam);
    	}
    
    	updatefields();
    	update = 0;
    	return 0;
    }
    
    
    void updateclient(HWND hWnd)
    {
    	HDC hDcPaint;
    	PAINTSTRUCT ps;
    
    	hDcPaint = BeginPaint(hWnd, &ps);	
    	
    	BitBlt(hDcPaint, ps.rcPaint.left, ps.rcPaint.top, 
    		ps.rcPaint.right-ps.rcPaint.left,  ps.rcPaint.bottom-ps.rcPaint.top, 
    		hMemdc, ps.rcPaint.left,  ps.rcPaint.top, SRCCOPY);
    
    	EndPaint(hWnd, &ps);
    }
    
    void drawimage(void)
    {	int i, n, x, y, y1, y2;
    	
    	if(imagestat)
    	{	n = mapsizey/10;
    		for(i = 0; i < 10; i++)
    		{	y1 = i*n;
    			y2 = y1+n;
    			for(y = y1; y < y2; y++)
    				for(x = 0; x < mapsizex; x++)
    					SetPixel(hMemdc, x+ifield.left, y+ifield.top, spectrum[map[x][y]]);
    			
    			InvalidateRect(hWnd, &ifields[i], 0);
    			UpdateWindow(hWnd);
    		}
    	}
    }
    Last edited by megafiddle; 10-07-2011 at 11:59 PM.

  2. #2
    Registered User
    Join Date
    Mar 2005
    Location
    Juneda
    Posts
    291
    I'm not sure, but maybe you can use a thread to check the loading status and display the image, so you won't depend on the messages: simply be sure that no other part of your program access to the display area while loading and make an independant procedure that informs what is it happening. I have no code to illustrate it

  3. #3
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    The code on lines 32, 33 and 34 will never be executed and your compiler should be warning you about unreachable code.

  4. #4
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Well, except for when there's a left mouse click, key press, paint or destroy message.

  5. #5
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Well, I found the problem.

    This part was always being called:

    updatefields();
    update = 0;
    return 0;

    updatefields() was being called after each WM_Paint message came in. This in turn
    called drawimage() which caused a WM_PAINT message to come in, after which
    updatefields was called, etc, etc.

    It was a convenient place to put it as almost all commands require an update of
    various combinations of fields, and all the command handlers just needed to set
    switches in 'update'.

    It was strange because it never should have gotten passed the 1st section of the
    image, recalling drawimage() over and over like that.
    But it seemed to accumulate all 10 sections and then draw them all at once.

    It doesn't look as good as I expected, though. Kind of choppy looking. Smoothing
    it out would require up to 750 bitblts though.

    Thanks for looking at it. I found the problem when I added a messagebox to each
    call to InvalidadeRect and saw the group of 10 calls repeated enlessly.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Ok... now, despite people who will advise you to the contrary... you now know why I use returns instead of breaks in my switch statements.

    Take all the break statements out of your switch and put returns in their place.

    Code:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {	
    	switch(msg)
    	{	case WM_KEYDOWN:
    			keyinterpret(wParam);
    			return 0;
    
    		case WM_LBUTTONDOWN :
    			xyinterpret(lParam);
    			return 0;
    
    		case WM_PAINT:
    			updateclient(hWnd);
    			return 0;
    
    		case WM_CLOSE:
    			//if(MessageBox(hWnd, "Exit Pogram?", "", MB_YESNO | MB_DEFBUTTON2) == IDNO)
    				//return 1;
    			//else
    				terminate();	
    				DestroyWindow(hWnd);
    				return 0;
    			
    		case WM_DESTROY:
    			PostQuitMessage(0);
    			return 0;
    
    		default:
    			return DefWindowProc(hWnd, msg, wParam, lParam);
    	}
    }

  7. #7
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    I did in fact fix the problem by adding "return 0:" to the WM_PAINT case.

    My updatefields() really belongs at the end of each of the keyinterpret() and
    xyinterpret() functions, I think.

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Then put it there...

    Code:
    case WM_KEYDOWN :
       keyinterpret();
       updatefields();
       return 0;
    case ...
    If it needs to run after only certain keys... move the call right into keyinterpret and fire it only when needed.

    *Alternatively* ... use an accellerator table to convert only needed keystrokes into WM_COMMAND messages and process the keys that way. You will have to add TranslateAccellerators() to your dispatcher loop, but that's no big deal.

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Take all the break statements out of your switch and put returns in their place.
    Not necessary.

    Code:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
    {
        LRESULT result = 0;        
        switch(msg)     
        {   
            case WM_KEYDOWN:             
                keyinterpret(wParam);             
                break;           
            case WM_LBUTTONDOWN :             
                xyinterpret(lParam);             
                break;           
            case WM_PAINT:             
                updateclient(hWnd);             
                break;           
            case WM_CLOSE:             
                //if(MessageBox(hWnd, "Exit Pogram?", "", MB_YESNO |     MB_DEFBUTTON2) == IDNO)                 
                //return 1;             
                //else                 
                terminate();                     
                DestroyWindow(hWnd);                 
                break;                       
            case WM_DESTROY:             
                PostQuitMessage(0);             
                break;           
            default:             
                result = DefWindowProc(hWnd, msg, wParam, lParam);     
        }
    
        return result; 
    }

  10. #10
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Quote Originally Posted by VirtualAce View Post
    Not necessary.

    Code:
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
    {
        LRESULT result = 0;        
        switch(msg)     
        {   
            case WM_KEYDOWN:             
                keyinterpret(wParam);             
                break;           
            case WM_LBUTTONDOWN :             
                xyinterpret(lParam);             
                break;           
            case WM_PAINT:             
                updateclient(hWnd);             
                break;           
            case WM_CLOSE:             
                //if(MessageBox(hWnd, "Exit Pogram?", "", MB_YESNO |     MB_DEFBUTTON2) == IDNO)                 
                //return 1;             
                //else                 
                terminate();                     
                DestroyWindow(hWnd);                 
                break;                       
            case WM_DESTROY:             
                PostQuitMessage(0);             
                break;           
            default:             
                result = DefWindowProc(hWnd, msg, wParam, lParam);     
        }
    
        return result; 
    }
    That works.

    How did you get small tabs in the code? My tabs are small in the Pelles editor,
    but large when printed here.

    Quote Originally Posted by CommonTater View Post
    Then put it there...

    Code:
    case WM_KEYDOWN :
       keyinterpret();
       updatefields();
       return 0;
    case ...
    If it needs to run after only certain keys... move the call right into keyinterpret and fire it only when needed.

    *Alternatively* ... use an accellerator table to convert only needed keystrokes into WM_COMMAND messages and process the keys that way. You will have to add TranslateAccellerators() to your dispatcher loop, but that's no big deal.
    Ok, it's been put.

    The key commands are only temporary, and will eventually be menu items or on screen buttons.
    Their main purpose was for rapid operating and exiting for testing purposes. That's why my "exit program"
    messagebox is commented out.

    Quote Originally Posted by adeyblue View Post
    Well, except for when there's a left mouse click, key press, paint or destroy message.
    Looked like a good place for it. As intended, it would have done nothing unless the update
    switches were set. But it turned into a strange recursive loop.

    Quote Originally Posted by Niara View Post
    I'm not sure, but maybe you can use a thread to check the loading status and display the image, so you won't depend on the messages: simply be sure that no other part of your program access to the display area while loading and make an independant procedure that informs what is it happening. I have no code to illustrate it
    As it turned out, it was just a bug in my code, the updatefields() at the end of the WindProc.

    A thread checking on it gave me an idea, though. I was drawing and bitblitting one segment at a time. I just need to
    check the progress and bitblt when time for the next segment. Much simpler.

  11. #11
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by megafiddle View Post
    If I remove the UpdateWindow(hWnd) statement, it doesn't hang up. But it doesn't draw 10 horizontal
    sections either. It completes the image internally, then displays it all at once.
    To explain this....WM_PAINT is the only msg that can be processed in the msg queue and is the lowest priority (just below WM_TIMER) so will not usually be removed if other msgs are in the queue.

    UpdateWindow() bypasses the OS msg queue and posts the WM_PAINT directly to the callback (so you see the partial paints)

    Without the UpdateWindow() mutliple paints start accumulating in the queue.
    Get/PeekMessage() picks these paint msgs up, concatinates them into the smallest invalid region to cover all the paints and drops one paint msg back into the bottom of the msg queue.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    How did you get small tabs in the code? My tabs are small in the Pelles editor,
    but large when printed here.
    Set your IDE or editor to insert spaces for tabs instead of using the tab character.

  13. #13
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Quote Originally Posted by novacain View Post
    To explain this....WM_PAINT is the only msg that can be processed in the msg queue and is the lowest priority (just below WM_TIMER) so will not usually be removed if other msgs are in the queue.

    UpdateWindow() bypasses the OS msg queue and posts the WM_PAINT directly to the callback (so you see the partial paints)

    Without the UpdateWindow() mutliple paints start accumulating in the queue.
    Get/PeekMessage() picks these paint msgs up, concatinates them into the smallest invalid region to cover all the paints and drops one paint msg back into the bottom of the msg queue.
    So with UpdateWindow, WM_PAINT's kept coming in immediately and then recalling my updatefields function.

    Without the UpdateWindow, my segment drawing loop had a chance to finish, with the WM_PAINTS's accumulating.

    Is WM_TIMER not good for precise timing then? Only good for setting a minimum time interval?

    Quote Originally Posted by VirtualAce View Post
    Set your IDE or editor to insert spaces for tabs instead of using the tab character.
    Ok, thanks. But you quoted my code with the tabs in it. Quoting it doesn't remove the tabs, does it?

  14. #14
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by megafiddle View Post
    Is WM_TIMER not good for precise timing then? Only good for setting a minimum time interval?
    Depends on the resolution and accuraccy you require from the timer, and WM_TIMER msgs are not the most accurate timing method available (ie QueryPerformanceTimer() is better)

    Anything under 50msec or with less than a +/- 50Msec accuracy is unrealistic on most MS OS's (except CE, where you can get sub 1 ms).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Generating Window Procedure mouse WPARAM from Hooks
    By seaders in forum Windows Programming
    Replies: 0
    Last Post: 03-24-2009, 09:43 AM
  2. Procedure
    By Brandy22 in forum C Programming
    Replies: 6
    Last Post: 11-01-2005, 04:52 PM
  3. can the procedure in the program be hidden?
    By Jasonymk in forum C++ Programming
    Replies: 7
    Last Post: 07-24-2003, 04:44 PM
  4. Default Procedure for MDI Child Window?
    By SeanMSimonsen in forum Windows Programming
    Replies: 3
    Last Post: 04-05-2003, 05:43 PM
  5. Window Procedure Question
    By shinobisot in forum Windows Programming
    Replies: 3
    Last Post: 01-15-2003, 11:38 AM