Never ending WM_PAINT loop with ATL CWindowImpl - windows

I have a very simple Win32 application that uses CAtlExeModuleT. The module simply creates a class CTestWindow derived from CWindowImpl. It just has a single message handler for WM_PAINT. After I create the window and display it, the OnPaint method (WM_PAINT message) is called infinitely and there by consumes 100% CPU.
The code that creates the window is very simple:
m_pMainWnd = new CTestWindow();
if(NULL == m_pMainWnd->Create(NULL, CWindow::rcDefault, _T("Test Window"), WS_OVERLAPPEDWINDOW, 0, hMenu)){
DWORD dwErr = GetLastError();
return E_FAIL;
}
m_pMainWnd->ShowWindow(nShowCmd);
The OnPaint message handler is very simple as well (it doesn't do anything):
LRESULT CTestWindow::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
// TODO: Add your message handler code here and/or call default
return 0;
}

My guess is that you are not validating the window in your paint handler.
An application must call BeginPaint
and EndPaint in response to WM_PAINT
messages, or pass the message to the
DefWindowProc function to validate the
window. DefWindowProc validates the
update region; it can send the
WM_ERASEBKGND message if the window
background needs to be erased.
This would mean the OS will think the window still needs to be painted, and call you again.

Related

Proper way of destroying window resources

This is excerpt from my code based on win32 api:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void __fastcall TMyThread::Execute(void)
{
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = class_name.c_str();
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return;
}
hwnd = CreateWindowEx(0, class_name.c_str(), NULL, 0, 0, 0, 100, 100, HWND_MESSAGE, NULL, wc.hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return;
}
MSG msg;
BOOL ret;
while ((ret = GetMessage(&msg, 0, 0, 0)) != 0)
{
if (ret != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
My questions:
Is it required to do some resource cleanup after quitting message loop (like CloseHandle for example)? Quite often i am seeing code samples without any such things. Is it correct?
Can newly created window receive messages into thread queue before first call of GetMessage function (we suppose that window was successfully created i.e. function CreateWindowEx returned without errors)?
Please take in mind that TMyThread is not main thread of application. So it can be created and destroyed many times in one application run. Please don't pay attention to quite simplified window creation. This particular window is not meant to be visible on screen. It is created solely for purpose of receiving messages from another application. This is highlighted by using HWND_MESSAGE value of hWndParent parameter when calling CreateWindowEx function.
By creating, running and destroying thread in the above example again and again I have found out that two methods needs to be called after quitting from message loop. First is DestroyWindow and second is UnregisterClass. In regular application DestroyWindow function should be called in WM_CLOSE handler after the user confirms that he really wants to close the application. DestroyWindow function then sends WM_DESTROY and WM_NCDESTROY messages to window. In response to WM_DESTROY message application should call PostQuitMessage(0) function which results to immediately quitting message loop. So this part of code is not neccessary in all scenarios. I needed to call DestroyWindow function explicitly because I exit message loop by simply sending WM_QUIT message. When not doing it sometimes I received error 1412 (ERROR_CLASS_HAS_WINDOWS) when trying to unregister window class.
if (hwnd != NULL)
{
ret = DestroyWindow(hwnd);
if (ret == 0)
{
str.printf(L"Window destroying failed (GetLastError = %d)!", GetLastError());
ShowError(str);
}
hwnd = NULL;
}
ret = UnregisterClass(class_name.c_str(), wc.hInstance);
if (ret == 0)
{
str.printf(L"Window class unregistration failed (GetLastError = %d)!", GetLastError());
ShowError(str);
}
You must be very careful with resources in Win32. Make sure you've looked at the documentation carefully to determine what Windows itself will unload for you and what you have to unload yourself.
For examples, HWNDs will get destroyed as parent HWNDs get destroyed.
The best trick is to attempt to unload everything you've personally created. If you get an error returned from a particular function on unload, you likely should not unload it as it's been unloaded by Windows or some related resource.
It is important not to unload things when it is not necessary, as that could cause a crash.
So for example, an icon created with a window that's directly from a resource likely shouldn't be unloaded. But an HBITMAP you create that you draw to other windows, should most definitely be unloaded.
Your section question can be determined by a quick test with breakpoints. I do not know off the top of my head.

SetWindowsHookEx HCBT_CREATEWND GetWindowText

Anyboby knows how to get text of a created window? I set hook on CreateWindow, but GetWindowText returns empty string.
hCBTHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) &CBTHook, g_appInstance, 0);
LRESULT CALLBACK CBTHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode == HCBT_CREATEWND)
{
HWND hwnd = (HWND)wParam;
CHAR buf[256];
GetWindowText(hwnd, buf, 256);
}
return CallNextHookEx(hCBTHook, nCode, wParam, lParam);
}
"The system calls the hook procedure before sending the WM_CREATE or WM_NCCREATE message to the window."
The window may not have text yet, your callback is invoked very early in the process of window creation. The name of the window gets passed in as part of the CREATESTRUCT message sent with WM_CREATE which would be the earliest the window could do something about having text. Even to hard code something, it would have to be in WM_NCCREATE, which still hasn't happened yet when your callback is called.
However, the callback itself also gets passed the the CREATESTRUCT. If the name that was passed to CreateWindow is what you are after, that's available to you as well.

Why does not calling `ValidateRect` cause a DirectX application to slow down tremendously?

I asked a related question earlier and realised that not calling ValidateRect in the application as response to WM_PAINT causes tremendous slowdown.
Why is this? How could this affect a DirectX application in such a manner?
// ----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) {
switch(msg) {
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
case WM_PAINT:
Render();
ValidateRect( hWnd, NULL );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
Your application is slowing down because it is rendering far more than necessary. It is receiving a furious stream of WM_PAINT messages.
Windows sends a WM_PAINT message to a window when it thinks the window should be painted. It makes this determination based on whether a window has been invalidated. Once you have rendered the window completely, you must tell Windows that this is the case by validating the window’s client area. As long as there is some invalidated area, Windows will continue to send your window WM_PAINT messages.
Before DirectX, application windows were validated when they called EndPaint. You can still call BeginPaint and EndPaint, wrapping whatever DirectX rendering you’re doing, or you can simply call ValidateRect when you’re done rendering.

(Windows API) WM_PAINT Mouse problems

I created a window with the following flags to overlay a d3d application:
WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TRANSPARENT | WS_EX_LAYERED
I proceeded with colorkeying the window for transperacy and all worked well.
However once I began drawing on it using GDI an unforeseen problem occurred:
For some reason the mouse events (especially movement) are not passed correctly through the window when WM_PAINT is in progress, and so it appears as though the mouse and the keyboard for that matter lag. the FPS is fine, this is some API problem, I suspect that for some reason the keyboard/mouse messages are not handled as they should while the WM_PAINT is in progress, because the slower the timer is set to the less jerking there is.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
{
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
break;
}
case WM_CREATE:
{
SetTimer(hwnd, ID_TIMER, 10, NULL);
break;
}
case WM_TIMER:
{
InvalidateRect(hwnd, 0, 1);
break;
}
case WM_PAINT:
{
paint(hwnd);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
and
void paint (HWND hwnd)
{
PAINTSTRUCT Ps;
HDC hdc = BeginPaint(hwnd, &Ps);
SetBkColor(hdc, RGB(0,0,0));
SetBkMode(hdc, TRANSPARENT);
LOGBRUSH log_brush;
log_brush.lbStyle = BS_NULL;
HBRUSH handle_brush = CreateBrushIndirect(&log_brush);
SelectObject(hdc, handle_brush);
..........................................
DeleteObject(font);
DeleteObject(pen);
DeleteObject(handle_brush);
EndPaint(hwnd, &Ps);
}
Thank you for any help you may be able to give.
WM_PAINT messages are never delivered to your window unless someone calls UpdateWindow or there are no keyboard or mouse messages in your input queue.
Once you begin processing WM_PAINT, if a keyboard or mouse message arrives, it just sits in your queue until you are done with WM_PAINT. So What you are describing isn't possible.
If your WM_PAINT code takes a long time to execute, that could cause jerkiness, but you say that's not a problem so perhaps it's your handling of WM_ERASEBKGND? I don't see that code, but I do see that when you InvalidateRect, you are passing TRUE as the last parameter which means that you want the background to be erased.
If you don't handle WM_ERASEBKGND, then DefWindowProc will do it for you erasing your entire window with the brush from from your window class. This could result in windows thinking that no part of your window is transparent.
If you want mouse messages to pass through your window, a more reliable way is to handle the WM_NCHITTEST message and return HTTRANSPARENT where you want the mouse to pass through.
This is basically what how the WS_EX_TRANSPARENT style works. like this
case WM_NCHITTEST:
{
lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (HTCLIENT == lRet)
lRet = HTTRANSPARENT;
}
If your window has no non-client area, then you can skip the call to DefWindowProc.
WndProc() is not always re-entrant. I believe with the main message pump, the mouse and keyboard messages are queued up and waiting for you to finish the prior WM_PAINT message. Conversely, if you were to call SendMessage() from WndProc(), then you are looking at re-entrace. The other case is PostMessage() which would add the message to the queue. Maybe look at using DirectInput for mouse and keyboard input if this is an issue. Otherwise, look for ways to speed up your drawing.

Overlay Window not drawing correctly when hooking

The requirement is to draw my information in side of another application's window.
To take care of z order and so forth hooking WH_GETMESSAGE and draw on WM_PAINT seem good.
However some WM_PAINT are intended for the window area of my concern, but other WM_PAINT are for something completely different, like a context menu or button.
Example Notepad is hooked with an overlay writing "Hello" into the Notepad screen. This works fine. However when right clicking Notepad the context menu gets overlay with Hello. Basically the context menu is destroyed.
Is there an elegant way of determining what WM_PAINT is context menu?
LRESULT CALLBACK overlayHook(int code, WPARAM wParam, LPARAM lParam) {
//Try and be the LAST responder to WM_PAINT messages;
LRESULT retCode = CallNextHookEx(hhk, code, wParam, lParam);
//Per GetMsgProc documentation, don't do anything fancy
if(code < 0) {
return retCode;
}
//Assumes that target application only draws when WM_PAINT message is
//removed from input queue.
if(wParam == PM_NOREMOVE) {
return retCode;
}
MSG* message = (MSG*)lParam;
if(message->message != WM_PAINT) {
//Ignore everything that isn't a paint request
return retCode;
}
PAINTSTRUCT psPaint;
BeginPaint(message->hwnd, &psPaint);
HDC hdc = psPaint.hdc;
RECT r = psPaint.rcPaint;
TextOut(hdc, 10, 10, "Hello", 4);
EndPaint(message->hwnd, &psPaint);
return retCode;
}
It is not enough to test for the draw update region, because the context menu could be anywhere and contain the area of my concern.
I don't know of any elegant way to do this, but you could use GetWindowLong() to get the window's style or GetClassName() to get its class name, and then base your filtering decisions on those values.

Resources