Jump to content

PeekMessage(...) and WM_CLOSE


cppman
 Share

Recommended Posts

Hi, I'm back with another C++ question.

I am having problems with the following switch statement. For some reason, the WM_CLOSE case label isn't being reached when I click the exit button on my window.

Here is the code I am using.

(updated code)

bool VideoDriver::Run()
 {
      if (pDirect3DDevice == NULL) {
          this->SetLastError(S_FAIL);
          return false;
      }
 
      MSG msg;
      if (PeekMessage(&msg, hDeviceWindow, 0, 0, PM_REMOVE))
      {
          ScreenToClient(hDeviceWindow, &msg.pt);
          this->MousePosition.x = (float)msg.pt.x;
          this->MousePosition.y = (float)msg.pt.y;
 
          switch(msg.message)
          {
          case WM_CLOSE:
              return false;
              break;
          case WM_KEYDOWN:
              if ((BYTE)msg.wParam == VK_ESCAPE)
                  return false;
              break;
          default:
              DefWindowProc(hDeviceWindow, msg.message, msg.wParam, msg.lParam);
              break;
          };
      }
 
      return true;
 }

And here is how I am calling it.

(updated code)

VideoDriver *pDriver = new VideoDriver();
 pDriver->CreateDevice();
 
 while(pDriver->Run())
 {
     pDriver->StartScene(COLOR_ARGB(0xFF, 0x33, 0x66, 0x99));
     pDriver->StopScene();
 }
 
 delete pDriver;

The pDriver->Run() call should return false when the users exits the window, but it continues to return true.

Any ideas?

Edited by chris95219
Link to comment
Share on other sites

Well I have zero knowledge of windows programming, so take my advice with a dumptruck of salt.

If I were you I would look at what exactly the PeekMessage function is doing to the msg object and what values it is returning.

Im guessing youve already visited this page, but heres a msdn link that may be of help.

http://msdn2.microsoft.com/en-us/library/m...928(VS.85).aspx

looks liek this link might have the answer... a guy asking the exact same q on a diff forum

http://www.gamedev.net/community/forums/to...topic_id=193302

Edited by Wus
Link to comment
Share on other sites

Thanks for your reply.

I have checked the value of the msg.message variable, and it seems to return 161 when I click the exit button, and 513 when I click anywhere else in the window.

513 is WM_LBUTTONDOWN - Occurs when the mouse clicks anywhere in the client area of the window.

161 is WM_NCLBUTTONDOWN - Occurs when the mouse clicks anywhere not in the client area of the window.

Note: I changed my code above, however I still have the same problem.

Edited by chris95219
Link to comment
Share on other sites

Alright, I just read through that post. I tried everything that was mentioned there, and still no luck. My old way of doing this was to create a global callback that could be called from the normal window procedure, but I'd prefer not to do that - it will make things "unorganized" in the user's code and they'd have to keep everything global if they wanted to release objects in the close event callback.

Ok thx again for all the replies.

It has given me some valuable new ideas on how to approach the problem.

It doesn't seem like any of the suggestions solved his problem; it only gave him new ideas. Edited by chris95219
Link to comment
Share on other sites

I got it working! I was playing around with it and came to realize something that was said in the thread you showed me. I simply checked the WM_CLOSE message in the Window Procedure, then I posted back into to the message queue so it tricks PeekMessage into picking it up.

Thanks for your help! :)

Edited by chris95219
Link to comment
Share on other sites

Why don't you subclass the window? That would be much easier and less error-prone. The code you show is pretty strange. First, you peek but you use the remove flag so you're just doing GetMessage(). The sequence of events is okay when you handle WM_CLOSE but for every other message you first call DefWindowProc() on it, then pass it along to it's real WNDPROC. This is just flat out wrong. For any non-trivial window, this will have a performance impact and could produce other issues when the default message handler should never be run at all.

Sub-classing is what you need to do.

Link to comment
Share on other sites

Yeah, I noticed that with the flags he was using the Peek function was behaving very similarly to the Get function. Just for completeness' sake... I found this quote on the MSDN forums.

Unlike GetMessage, the PeekMessage function does not wait for a message to be posted before returning.

So to me that reads that PeekMessage is non-blocking whereas GetMessage is blocking.
Link to comment
Share on other sites

Why don't you subclass the window? That would be much easier and less error-prone. The code you show is pretty strange. First, you peek but you use the remove flag so you're just doing GetMessage(). The sequence of events is okay when you handle WM_CLOSE but for every other message you first call DefWindowProc() on it, then pass it along to it's real WNDPROC. This is just flat out wrong. For any non-trivial window, this will have a performance impact and could produce other issues when the default message handler should never be run at all.

Sub-classing is what you need to do.

It isn't the same as using GetMessage. Like Wus said, PeekMessage doesn't wait for the message to be posted, which is very important in my situation -while I still need the message to be taken out of the queue. The code above is old(as of now), and the two function calls below the switch statement are no longer called. So, whatever isn't handled by my switch statement, DefWindowProc takes over(as far as I know). As for the real WNDPROC being called, I have no control over that, unless DefWindowProc is indirectly calling it.

By looking at MSDN, subclassing would mean creating another function to intercept the messages. Unless I am completely misunderstanding this, it isn't what I want. I need it to be in a method, and I need the messages to get processed as fast as possible. This is why I used PeekMessage over GetMessage.

Below is the new working code:

bool VideoDriver::Run()
 {
     if (pDirect3DDevice == NULL) {
         this->SetLastError(S_FAIL);
         return false;
     }
 
     MSG msg;
     if (PeekMessage(&msg, hDeviceWindow, 0, 0, PM_REMOVE))
     {
         ScreenToClient(hDeviceWindow, &msg.pt);
         this->MousePosition.x = (float)msg.pt.x;
         this->MousePosition.y = (float)msg.pt.y;
 
         switch(msg.message)
         {
         case WM_CLOSE:
             return false;
             break;
         case WM_KEYDOWN:
             if ((BYTE)msg.wParam == VK_ESCAPE)
                 return false;
             break;
         default:
             DefWindowProc(hDeviceWindow, msg.message, msg.wParam, msg.lParam);
             break;
         };
     }
 
     return true;
 }
Edited by chris95219
Link to comment
Share on other sites

I guess it is strange compared to a normal Windows application, however this is a game engine. It should try to process its messages (within the class) before each frame - whether there are messages or not, it needs to return as soon as possible. I don't see any other way to do this other than using Run as an indirect message handler.

Edited by chris95219
Link to comment
Share on other sites

I realize that. However, if you are in control of the WNDPROC, then write it like this:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
         case WM_CLOSE:
             return FALSE;
             break;
         case WM_KEYDOWN:
             if ((BYTE)wParam == VK_ESCAPE)
                 return FALSE;
             break;
         default:
             DefWindowProc(hwnd, uMsg, wParam, lParam);
             break;
    }
    return TRUE;
}

Then your Run() becomes:

bool VideoDriver::Run()
{
     if (pDirect3DDevice == NULL) {
         this->SetLastError(S_FAIL);
         return false;
     }

     MSG msg;
     if (PeekMessage(&msg, hDeviceWindow, 0, 0, PM_REMOVE))
     {
         ScreenToClient(hDeviceWindow, &msg.pt);
         this->MousePosition.x = (float)msg.pt.x;
         this->MousePosition.y = (float)msg.pt.y;

         TranslateMessage(&msg);
         return DispatchMessage(&msg) == TRUE;
     }
     return true;
}

DispatchMessage() returns the value that the WNDPROC returns.

Or, if you have to sub-class, then the WindowProc function is virtually identical except instead of calling DefWindowProc() you would call the original WNDPROC. There would also be the addition of the sub-classing code.

The thing I find "wrong" with your code is that you completely circumvent the WNDPROC for the window. If you are in control of the WNDPROC, then you can just write it like I show above. If you are not in control of it, then sub-class it.

Link to comment
Share on other sites

Does WM_CLOSE not get posted to the queue? Does it get sent straight to the WNDPROC? Using my code, it catches it inside the WNDPROC (even when it isn't being directly called) but not in my PeekMessage statement. So, if what I asked is true, then posting it to the queue(PostMessage) would force PeekMessage to pick it up(it does). So with that said, I am apparently not "completely circumventing the WNDPROC", otherwise what I am doing wouldn't work either. The WNDPROC is being called somehow, somewhere.

Edited by chris95219
Link to comment
Share on other sites

Care to elaborate? That doesn't exactly explain why your code isn't working, or why my code would be wrong - unless I am missing something.

I don't want to "thunk" the window(it is a single window - that would be overkill and will present problems further down the road). I would also prefer not to use a "messenger variable" between the WNDPROC and my class. I simply would like to do what your code should have done in theory.

Maybe I am missing your point.

Edited by chris95219
Link to comment
Share on other sites

Alright, the problem is this. WM_CLOSE doesn't always make it into the message queue (according to that thread and your experience). According to that thread, and this was why I posted it, sometimes the WNDPROC is called directly. Thus, it's never going to be in the message pump. So, I see why my code doesn't work. While it does correctly return the value when it handles WM_CLOSE, it does not return it via DispatchMessage() because the WNDPROC was called directly.

There are a number of ways around this that don't involve posting a message back to the queue. You can associate a class object with the window (demonstrated in that thread). You can use a global variable. You could even post a *user-defined message* back to the window. If you post WM_CLOSE then it *looks like* a recursive situation so it's probably best not to post it but rather just use a user-message. The most ideal scenario would be to make the WNDPROC part of your class and use a member variable that's set when WM_CLOSE is handled.

Welcome to the wonderful world of Windows programming.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...