Thread: Windows Messages Queuing Up Whilst Looping

  1. #1
    Registered User
    Join Date
    Jan 2003
    Posts
    21

    Windows Messages Queuing Up Whilst Looping

    Hello,

    Im pretty convinced that I know the cause of my problem but wanted to clarify it with you guys and hope you could offer some advice as to a fix..

    Im writing a little RPG based on a version I did console based a while back. Only this time its Windows Based so I could display charater stats, inventory, etc. Ive designed my dialog window and got everything working to display my window and when I click fight it runs through a standard RPG combat loop like "Blah hits you for 5 points of damage etc", these results are outputted into a Multi Line Edit Box which ive made read only.

    My Problem is while this loop is going on, and even though the results are still appearing in my outbox box the whole window is frozen, you cannot press any of the other buttons in the window or type anything in another edit field until the combat is finished.

    Now ive figured out this is because of my code is laid out like (cut down)

    Code:
    #include <windows.h>
    #include "resource.h"
    
    LRESULT CALLBACK ClientDlgProc(HWND DialogWindow, 
      UINT Message, WPARAM wParam, LPARAM lParam)
    {
      switch(Message) {
    
        case WM_INITDIALOG:
          return FALSE;
        break;
    
        case WM_COMMAND:
          switch (wParam) {
            case ID_QUIT:
            EndDialog(DialogWindow, FALSE);
            break;
    
            case ID_COMBAT:
            do {
               combat loop();
            } while someone is still alive;
            break;
    
            default:
            break;
          }
           return FALSE;
        break;
    
        default:
           return FALSE;
        break;
      }
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE 
    hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
      DialogBox((HINSTANCE) hInstance, MAKEINTRESOURCE
        (IDD_DIALOG), NULL, (DLGPROC) ClientDlgProc);
      return 0;
    }
    So while this loop is going on other messages are just getting queued up until it breaks out of the loop. (as once the combat is finished all the messages ive been sending like "window movement", "minimise", etc all come through at once)

    So my question is pretty much is there anyway I can get around this by perhaps sending my loop into the background so I can still get the windows messages or I am experiencing this problem because im doing everything in a Dialog Box? Is it because ive not setup the callback correctly and so therefore queuing up the messages instead ?

    If anyone could point out where im going wrong or what I need to read about to get around this id appreciate it.

    Im using Visual C++ 6.0, Non-MFC, and using C.
    Thank You

    Mr Pickle.

  2. #2
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    You are correct - your windows message loop is unable to process windows messages whilst it is stuck within a message handler within itself.

    There are several ways of doing this, some easier to understand, some more efficient.

    The easiest to understand method is to implement your "loop" as a state machine. It does a small amount of work then saves it's position and releases the CPU. When the Windows loop gets back to the state machine, it reloads it's last saved state and does a bit more, then saves and releases again. This is effective for releatively simple tasks.

    The better way to do it is to use a seperate worker thread to process the loop leaving the Windows processing loop in the main thread, that way, both loops can run "at the same time". To do it this way, you would need to start a thread with "combat loop" as a function. The thread will run until that function returns. The simplest way to start a thread is with the beginthread() API routine. Multithreading is a very big area, with a lot of concepts and gotcha's - to big to cover in a single message - my multithreading tutorial on my old site had 11 chapters, and still was only the basics really.
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  3. #3
    Programming is fun, mkay?
    Join Date
    Oct 2001
    Posts
    490

    Cool I Agree ... :)

    I agree with adrianxw. The "beginthread()" solution would work. It's because it's in a "do ... while" loop, which WILL cause an application to freeze if it's not in a thread, and if it lasts long enough. So, I will suggest "beginthread()". Just thought I would add my thoughts.
    Website(s): http://www16.brinkster.com/trifaze/

    E-mail: [email protected]

    ---------------------------------
    C++ Environment: MSVC++ 6.0; Dev-C++ 4.0/4.1
    DirectX Version: 9.0b
    DX SDK: DirectX 8.1 SDK

  4. #4
    Registered User
    Join Date
    Oct 2003
    Posts
    12
    Let me point out that if the combat loop has been started and you enter combat again you could have serveral combat loops running, so you need some sort of thread sycronisation. The easiest way to do it for what your doing is this:
    The first thing the combat loop dose is set a global variable to true (something like bInCombat) before your main loop calls beginthread it checks the variable and only enters the combat loop if its not allready running.
    That is just one Option and it depends on your program.
    Last edited by ENF; 12-15-2003 at 10:43 PM.

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    >>so you need some sort of thread sycronisation.

    Critical sections.


    Another way is

    Start the combat loop,

    Run the loop until it writes one line of text to the edit or finishes a round.

    Then send your app a message (SendMessage()) User defined message is good for this. This will get processed in order (ie other msg's will get processed)

    Stop the combat loop.

    When the app catches up and process the message you sent, start the combat loop again.

    Repeat until the combat loop is finished.
    "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

  6. #6
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    >>>
    The first thing the combat loop dose is set a global variable to true (something like bInCombat) before your main loop calls beginthread it checks the variable and only enters the combat loop if its not allready running.
    <<<

    That will not always work. A thread, any thread, can be pre-empted at any point in it's execution, that means part way through the execution of a line of code - remember, the code you see has been compiled.

    Possible scenario:

    Thread reads global variable, sees it is available, so tries to set it to say it is running, however, it gets pre-empted before it writes the value back.

    Second thread reads the variable, sees it is available sets it, and starts running.

    First thread runs again, completes writing the variable and also starts running.

    ... You now have two threads running both thinking they are the only ones there - disaster in the making.

    You need to use OS level objects for thread synchronisation. Critical sections are the easiest to understand.
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  7. #7
    Registered User
    Join Date
    Jan 2003
    Posts
    21
    Thanks for all the responses, I looked into what you had all said and went with the _beginthread method, further below is my code which now works as whilst in the loop you can press a North button which displays a message in the the outbox box while the combat function is running.

    I do have a couple of questions though if you can spare an answer..

    at the moment I can only figure out how to provide 1 parameter to my function because I have to pass the parameters in the _beginthread function. If i had a prototype like void func(int a, int b) how would i specify to pass 2 arguments in beginthread ? ive tried various forms of syntax but keep getting incorrect parameter errors when trying to compile.

    Also im aware that im opening a new thread but do you need to close this and free up the memory afterwards ?

    Anyway thanks very much for your help.

    Mr Pickle.

    Heres my code :

    Code:
    #include <windows.h>
    #include <process.h>
    #include "resource.h"
    #define Display(s) SendDlgItemMessage(DialogWindow,\
      IDC_OUTPUT,EM_REPLACESEL,0,(LPARAM)((LPSTR)s))
    
    VOID CombatLoop(HWND DialogWindow)
    {
      int counter;
      char Temp[100];
      for(counter=0;counter<=10;counter++) {
    	wsprintf(Temp, "In Loop Number %d\r\n", counter);
    	Display(Temp);
    	Sleep(1000);
      }
      wsprintf(Temp, "Thread Has Finished\r\n");
      Display(Temp);
    }
    
    LRESULT CALLBACK ClientDlgProc(HWND DialogWindow, 
      UINT Message, 
      WPARAM wParam, 
      LPARAM lParam)
    {
      switch(Message) {
        case WM_INITDIALOG:
          Display("Started Up..\r\n\r\n");
          return FALSE;
        break;
    
        case WM_COMMAND:
           switch (wParam) {
             case ID_QUIT:
               EndDialog(DialogWindow, FALSE);
             break;
    
             case ID_START:
               _beginthread (CombatLoop, 0, DialogWindow);
             break;
    
             case ID_NORTH:
                Display("North Button Pressed\r\n");
             break;
    
             default:
             break;
          }
        break;
    
        default:
        break;
      }
      return FALSE;
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance, 
                                       HINSTANCE hPrevInstance, 
                                       LPSTR lpCmdLine, 
                                       int nCmdShow)
    {
        DialogBox((HINSTANCE) hInstance, 
        MAKEINTRESOURCE(IDD_DIALOG), 
        NULL, (DLGPROC) ClientDlgProc);
        return 0;
    }

  8. #8
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    The easiest way to pass multiple parameters is to create a structure containing the values and pass a pointer to that structure to your thread function.

    You should always free up dynamically allocated memory. If you do that, you do not need to do anything special because it is in a seperate thread.

    If you use beginthread() you do not need to do anything other than execute a return from the thread function - it does everything for you, (that's why I suggested it).

    If you use other thread routines to begin a thread, beginthreadex() for example, that is not the case. Things like the thread handle remain open and must be manually closed if you use this function for example.
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    You need to use OS level objects for thread synchronisation. Critical sections are the easiest to understand.

    I'm probably wrong, but in this situation it doesn't seem you need OS thread synchronisation.

    Code:
    // In main thread...
    if (!bInThread) {
    	bInThread = TRUE;
    	beginthread(...);     // Should check return value.
    }
    
    void ThreadProc(...) {
    
    	...
    
    	bInThread = FALSE;
    }

  10. #10
    Skunkmeister Stoned_Coder's Avatar
    Join Date
    Aug 2001
    Posts
    2,572
    adrian already explained why that wouldnt work faultlessly.
    Imagine what happens after the test but before the assignment if your thread gets preempted. You may be able to get away with a double checked locking pattern to cut out some of the overhead but some sort of synchronization will be necessary.
    Free the weed!! Class B to class C is not good enough!!
    And the FAQ is here :- http://faq.cprogramming.com/cgi-bin/smartfaq.cgi

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    adrian already explained why that wouldnt work faultlessly.

    I think Mr xw was describing a more complex scenario.

    Imagine what happens after the test but before the assignment if your thread gets preempted.

    Nothing as far as I can see. If bThread == FALSE then there is no second thread (Well technically a thread could still be terminating, but nothing that worries us).

  12. #12
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    As SC said - the problem occurs if the thread spinner gets pre-empted...

    Code:
    if (!bInThread) {
            // ... here...            
    	bInThread = TRUE;
    	beginthread(...);     // Should check return value.
    }
    ... the test has been made and seen to be FALSE, however, it has not set it TRUE yet. Anything could happen, including another thread making the same test and suceeding in setting the flag TRUE, before this thread gets a chance to run again. When this one does, it sets the flag TRUE, (it may now already be so, but that doesn't matter), and spins a thread, when in fact, it should not have done.
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  13. #13
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    ... the test has been made and seen to be FALSE, however, it has not set it TRUE yet. Anything could happen, including another thread making the same test and suceeding in setting the flag TRUE, before this thread gets a chance to run again.

    There is no other thread in the context of Mr Pickle's question or my reply. This code is placed in the window procedure. The window procedure, as you know, only runs in the one thread.

    Therefore, it is entirely impossible to reach // ...here..., in more than one thread in the same process.

    The scenario you are describing is multiple threads spinning threads. This is far more complex than Mr Pickle's application. Even in more complex applications I think it would be generally preferred to have one controller thread spinning multiple threads rather than multiple threads spinning their own threads.

    Multiple threads spinning threads... I can't even make sense describing it. ;-',

    I still think my code is perfectly safe in the context of Mr Pickle's application but I look forward to continuing the discussion.

    Here is the revised code:
    Code:
    #include <windows.h>
    #include <process.h>
    #include "resource.h"
    #define Display(s) SendDlgItemMessage(DialogWindow,\
      IDC_OUTPUT,EM_REPLACESEL,0,(LPARAM)((LPSTR)s))
    
    static BOOL bInThread;
    
    VOID CombatLoop(HWND DialogWindow)
    {
      int counter;
      char Temp[100];
      for(counter=0;counter<=10;counter++) {
    	wsprintf(Temp, "In Loop Number %d\r\n", counter);
    	Display(Temp);
    	Sleep(1000);
      }
      wsprintf(Temp, "Thread Has Finished\r\n");
      Display(Temp);
    
      bInThread = FALSE;
    }
    
    LRESULT CALLBACK ClientDlgProc(HWND DialogWindow, 
      UINT Message, 
      WPARAM wParam, 
      LPARAM lParam)
    {
      switch(Message) {
        case WM_INITDIALOG:
          Display("Started Up..\r\n\r\n");
          return FALSE;
        break;
    
        case WM_COMMAND:
           switch (wParam) {
             case ID_QUIT:
               EndDialog(DialogWindow, FALSE);
             break;
    
             case ID_START:
    
             if (!bInThread) {
                      bInThread = TRUE;
                      _beginthread (CombatLoop, 0, DialogWindow);
             }
             break;
    
             case ID_NORTH:
                Display("North Button Pressed\r\n");
             break;
    
             default:
             break;
          }
        break;
    
        default:
        break;
      }
      return FALSE;
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance, 
                                       HINSTANCE hPrevInstance, 
                                       LPSTR lpCmdLine, 
                                       int nCmdShow)
    {
        DialogBox((HINSTANCE) hInstance, 
        MAKEINTRESOURCE(IDD_DIALOG), 
        NULL, (DLGPROC) ClientDlgProc);
        return 0;
    }
    Last edited by anonytmouse; 12-16-2003 at 03:27 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Windows Messages help
    By The-Guy in forum C++ Programming
    Replies: 2
    Last Post: 07-07-2002, 07:13 PM
  2. OpenGL and Windows
    By sean345 in forum Game Programming
    Replies: 5
    Last Post: 06-24-2002, 10:14 PM
  3. Information: Exiting Windows
    By Okiesmokie in forum C++ Programming
    Replies: 2
    Last Post: 05-12-2002, 09:42 AM
  4. messages from other windows
    By canine in forum Windows Programming
    Replies: 2
    Last Post: 03-12-2002, 04:19 AM
  5. shutting down windows
    By DavidP in forum A Brief History of Cprogramming.com
    Replies: 3
    Last Post: 01-02-2002, 12:28 PM