[Win32] Yet another unresponsive window

Started by
10 comments, last by Skizz 17 years, 2 months ago
Hello! I've just completed the windowing part of my engine and now, as every time I did a windowing part of some engine, I get an unresponsive window. The window shows up and renders (using ogl) correctly. It processes some window messages (well, some messages go through it's procedure), but it never receives a WM_SIZE, WM_MOVE, WM_CLOSE but I do receive WM_ACTIVATE messages. I cannot drag around or resize the window, neither can I close it using the [X] button. My window style is as follows : DWORD style = WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MAXIMIZEBOX; Any ideas on what might be causing this?
Advertisement
Most likely you have some blocking code somewhere that is not allowing the program to continue. The easiest thing to do is just break into the code and see where it is hanging out, most likely it will break in the same spot every time.

theTroll
The main loop runs normally and the window is updated (translate and dispatch messages) at each iteration. Nothing breaks the loop (If I set a breakpoint, it loops to it normally).

However, I've noticed, and I don't know if it is normal, that when I pause the application while it's running, it always pauses at this line :

virtual CRYO_bool flipBuffers() { return ::SwapBuffers(m_DC) == TRUE; }

I don't know if this means anything because ::SwapBuffers does return TRUE, but it still seems strange to me.

I've simplified the main loops to it's minimum, now it runs the following in a tight loop :
CRYO_void Win32Context::update_impl(){	MSG msg;	while(::PeekMessage(&msg, m_Window, 0, 0, PM_REMOVE))	{		::TranslateMessage(&msg);		::DispatchMessage(&msg);	}}

... But the window still acts the same way (however now the backbuffer isn't cleared now so we "see through the window").

Any other options?
In PeekMessage(), unless you have multiple windows and you explicitly want to only handle messages for the one represented by m_Window, replace m_Window with NULL. It's not necessary to filter window messages by HWND unless you really mean to.

Can we see your window procedure?
You have to pass on all messages to DefWindowProc unless you completely handle them yourself.

There is NO valid return code for all unhandled messages. The following snippet is dead wrong:

LRESULT WINAPI MyWindowProc( ... ){  switch ( message )  {    case WM_MOUSEMOVE:      // do something      break;    default:      return DefWindowProc( .. )  }  return 0;}


Move the DefWindowProc outside the switch. Keeping it inside default you can't handle the message yourself AND pass it on (unless you copy the DefWindowProc call).

Change it to something like this:

LRESULT WINAPI MyWindowProc( ... ){  switch ( message )  {    case WM_MOUSEMOVE:      // do something      break;  }  return DefWindowProc( .. )}


May the fool that put up the tutorial with the DefWindowProc call inside the default case be sporked in the face. Repeatedly. (SarcasmTM)

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Quote:Original post by Endurion
You have to pass on all messages to DefWindowProc unless you completely handle them yourself.
...snip...


I prefer the following:
LRESULT WINAPI MyWindowProc( ... ){  LRESULT result=0;  bool call_default_proc=false;  switch ( message )  {    case WM_MOUSEMOVE:      // do something      result = //some value      break;    case WM_some other message      // do something else      call_default_proc=true;      break;    default:      call_default_proc=true;      break;  }  return call_default_proc ? DefWindowProc (...) : result;}


Skizz
Quote:Original post by Skizz
I prefer the following:
...    case WM_MOUSEMOVE:      // do something      result = //some value      break;    case WM_some other message      // do something else      call_default_proc=true;      break;...  return call_default_proc ? DefWindowProc (...) : result;


Skizz


Why not simply place the return inside the case statement for WM_MOUSEMOVE?

...    case WM_MOUSEMOVE:      // do something      return //some value    case WM_some other message      // do something else      break;...  return DefWindowProc (...);

Mainly because I like "single entry point, single exit point" code. There's nothing wrong with using multiple return like that. Also I usually use a map rather than a switch:
LRESULT Proc (...){  args.use_default = false;  args.result = 0;  args.w_param = w_param;  args.l_param = l_param;  if (map has message)  {    call map function (args);  }  else  {    args.use_default = true;  }  result args.use_default ? DefWindowProc (...) : args.result;}


Skizz
Okay, I'm back. Thanks for your feedback. I will post my window procedure code.

There's only one thing that I should mention : The WNDCLASS structure is filled with ::DefWindowProc as the window procedure. HOWEVER, after window creation, this is changed to my own window procedure like this :

::SetWindowLongPtr(in_window, GWL_WNDPROC, (LONG_PTR)(void*)StaticWindowProc);
::SetWindowLongPtr(in_window, GWL_USERDATA, (LONG_PTR)this);

This change works because my window precedure does receive messages, it just doesn't receive all of them. E.g. I receive WM_ACTIVATE but not WM_MOVE or WM_SIZE. The actual window procedure is as follows :

LRESULT CALLBACK Win32Context::StaticWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){	// Change this to use std::map thing!	Win32Context * context = (Win32Context*)::GetWindowLongPtr(hwnd, GWL_USERDATA);	if(context != NULL && context->m_Window == hwnd)	{		return context->WindowProc(hwnd, msg, wparam, lparam);	}	return ::DefWindowProc(hwnd, msg, wparam, lparam);}LRESULT CALLBACK Win32Context::WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){	CRYO_bool forwardtodefwindowproc = CRYO_true;	switch(msg)	{		// If the window gets activated or deactivated		case WM_ACTIVATE:		{			if(!HIWORD(wparam))			{				for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)				{					m_Listeners->context_onActivate(*this);				}			}			else			{				for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)				{					m_Listeners->context_onDeactivate(*this);				}			}			forwardtodefwindowproc = CRYO_false;			break;		}		// This makes the screensaver unable to boot		case WM_SYSCOMMAND:		{			forwardtodefwindowproc = CRYO_false;			break;		}		// If we are told to close		case WM_CLOSE:		{			for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)			{				m_Listeners->context_onClose(*this);			}			forwardtodefwindowproc = CRYO_true;			break;		}		// If the window is resized		case WM_SIZE:		{			RECT temp;			::GetClientRect(hwnd, &temp);			Size newsize(temp.right - temp.left, temp.bottom - temp.top);			for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)			{				m_Listeners->context_onResize(*this, newsize);			}			// Make renderer recompute it's projection type			Render::ProjectionType projtype = m_Renderer->getProjType();			m_Renderer->setProjType(Render::PROJTYPE_IDENTITY);			m_Renderer->setProjType(projtype);			forwardtodefwindowproc = CRYO_true;			break;		}		// If the window is moved		case WM_MOVE:		{			Point newposition(LOWORD(lparam), HIWORD(lparam));			for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)			{				m_Listeners->context_onMove(*this, newposition);			}			forwardtodefwindowproc = CRYO_true;			break;		}		// If the window is shown or hid		case WM_SHOWWINDOW:		{			if(wparam == TRUE)			{				for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)				{					m_Listeners->context_onShow(*this);				}			}			else			{				for(CRYO_uint i = 0 ; i < m_Listeners.size() ; ++i)				{					m_Listeners->context_onHide(*this);				}			}						forwardtodefwindowproc = CRYO_true;			break;		}	}	if(forwardtodefwindowproc) return ::DefWindowProc(hwnd, msg, wparam, lparam);	return 0;}


But I don't think the problem is in there because I don't receive WM_MOVE and WM_SIZE messages at all, they don't go through my procedure.

Any ideas?
That is a very odd way to specify the window procedure when creating a window. You're effectively sub-classing the window. Normally, to do what you're trying to do, you'd specify the 'this' pointer as the lpParam parameter to CreateWindowEx. This is then passed to the window procedure as the lpCreateParams value in the CREATESTRUCT pointed to by the lParam of the WM_CREATE/WM_NCCREATE messages.
class WindowWrapper{  WindowWrapper ()  {     WNCLASSEX window_class;     window_class.lpfbWndProc = WProc; // need to cast correctly     // set up other window_class members     RegisterClassEx (&window_class);     m_window = CreateWindowEx (...., reinterpret_cast <LPVOID> (this));  }  static LRESULT WProc (HWND window, UINT message, WPARAM wparam, LPARAM lparam)  {    LRESULT result = 0;    bool use_default = false;    if (message == WM_CREATE)    {      CREATESTRUCT *create = reinterpret_cast <CREATESTRUCT *> (lparam);      SetWindowLongPtr (window, GWL_USERDATA, create->lpCreateParam);    }    WindowWrapper *me = cast <> (GetWindowLongPtr (window, GWL_USERDATA));    if (me)    {      me->ProcessMessage (message, wparam, lparam, result, use_default);    }    else    {      use_default = true;    }    return use_default ? DefWindowProc () : result;  }  void ProcessMessage (UINT message, WPARAM wparam, LPARAM lparam, LRESULT &result, bool &use_default)  {     switch (message) // better to use a map I think     {         // handlers     }  }    HWND m_window;};


Skizz

This topic is closed to new replies.

Advertisement