Post Message from Thread to Main Window

Hi,
I am programming a Win32 Program in Visual c++ 2010. It is one of my first not console programs. Since I have a lot of computations to make, I needed a thread so that I don't block the program.
Now I want the thread to tell the main window to redraw as soon as it has finished the computation. Right now the window will only redraw, if I force a WM_PAINT message by resizing the window with the mouse.

To start with, I took the code my compiler already had for a win32 application.

to create the thread I am using this code:
1
2
3
DWORD WINAPI ProgramMain(LPVOID vpParam);
		DWORD qThreadID1;
		HANDLE hThread1 = CreateThread(0, 0,ProgramMain, NULL, 0, &qThreadID1);


And at the end of the file is the thread function.

1
2
3
4
5
6
7
8
DWORD WINAPI ProgramMain(LPVOID vpParam){
	
//Some Code here	

//Here I want to update the window.

	return 0;
}


Sending a WM_PAINT message to the window, should be the solution, right?

I tried doing this by having a global HWND Pointer assigned to 0. Right after the definition of hWnd, the Pointer is set to Point to hWnd:
MainHWND=&hWnd

In the thread where I want to update my Window I set this:
int Test=PostMessage(*MainHWND,WM_PAINT,NULL,NULL);
but I get the error, that *MainHWND is an invalid handle. (ERROR 1400)

Just to test, I set the following line by WM_RBUTTONUP:
int Test=PostMessage(hWnd,WM_PAINT,NULL,NULL);
I don't get an error, but the screen doesn't get updated either.

What am I doing wrong?

Thank you,
Daylen
You should NOT send WM_PAINT message directly.

The WM_PAINT message is generated by the system and should not be sent by an application. To force a window to draw into a specific device context, use the WM_PRINT or WM_PRINTCLIENT message. Note that this requires the target window to support the WM_PRINTCLIENT message. Most common controls support the WM_PRINTCLIENT message.


http://msdn.microsoft.com/en-us/library/windows/desktop/dd145213%28v=vs.85%29.aspx
Thanks!

I tried out using the WM_PRINT instead of WM_PAINT now. If I try to use PostMessage in the WndPoc() function directly with hWnd it works, but when I use it in the thread with the pointer, I still get the same error: 1400 invalid window handle.

The problem must be somewhere with the Pointer.

Here is the code related with the pointer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

...
...

HWND* MainHWND=0;

...
...

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   MainHWND=&hWnd;
   hInst = hInstance;

...
...

int Test=PostMessage(*MainHWND,WM_PRINT,NULL,NULL);
int error=GetLastError();


I just can't find out what is wrong!

Thank you,
Daylen
Last edited on
If you want a window to redraw, simply use:

InvalidateRect(hwnd, &rect, fBool);

in which the second parameter can be set to NULL and the third to TRUE if you want to redraw the whole window.

And this

1
2
HWND hWnd;
   MainHWND=&hWnd;


You declared hWnd, but didnt initiate it, so in the second statement MainHWND got the wrong value. For correcting, you assign MainHWND to hWnd after

hwnd = CreateWindow(...);

statement, look in you code for it

Hope that help you :)
As modoran has already said, you should never send WM_PAINT directly; you should use InvalidateRect (you can also use UpdateWindow if you need an immediate repaint, but this should be used very sparingly.)

And neither WM_PRINT nor WM_PRINTCLIENT are approriate here, as you don't want to ask another window to draw into your device context.

The other part of the puzzle is that InvalidateRect should be called from the window's thread, not the worker thread (you should not call GUI-related API calls like InvalidateRect, FindWindow, etc. from worker threads.)

The way you do this is to define a custom message, e.g.

#define WM_USER_INVALRECT (WM_USER + 100)

And then post this message to the main window where you have a handler which invalidates the window in response to it.

You can either use PostMessage, if you provide the thread with the HWND for the main windows, or PostThreadMessage, if you provide the thread ID rather than the HWND.

To avoid an evil global, you can pass the HWND (or thread ID) to the thread via the start parameter, e.g.

DWORD WINAPI ProgramMain(LPVOID pvParam);

Assumed to be in WM_CREATE message handler or similar?

1
2
    DWORD qThreadID1 = 0;
    HANDLE hThread1 = CreateThread(0, 0, ProgramMain, (LPVOID)hWnd, 0, &qThreadID1);


And

1
2
3
4
5
6
7
8
9
DWORD WINAPI ProgramMain(LPVOID pvParam) {
    HWND hWnd = (HWND)pvParam;
	
    // Some Code here	

    PostMessage(hWnd, WM_USER_INVALRECT, 0, 0);

    return 0;
}


Andy

PS You should use _beginthreadex rather than CreateThread if you use any CRT calls in your thread routine.
Last edited on
Thank you!
Passing hWnd with pvParam worked fine now.

I found out, what the mistake was now:

my MainHWND was a Pointer to the hWnd wich is requiered in WndProc(). hWnd fell out of scope because WndProc was not being used. The Pointer Pointed to nothing.

I am new to not simple console programming, so let me ask one more question:
the handle to the window can never change, right? so by keeping and using the value of the HWND even if WndProc is not Running is OK, Right? I do not need to update my MainHWND evry time WndProc gets called.

Thank you for Helping. I really appreciate it! :-)

Daylen
the handle to the window can never change, right?

Correct; the HWND value is fixed thoughout the lifetime of a window.

Andy
Last edited on
Thank You!

Daylen :-)
Topic archived. No new replies allowed.