Related
We've noticed an issue with ScrollWindowEx on our Windows app. Because there are a lot of child windows, if you hold down the scrollbar and move around, it causes a huge stall in DesktopWindowManager.
This is documented here
Why is my MFC application hanging after interacting with both scroll-bars?
So, I've taken the call to ScrollWindowEx out and now I can move my scrollbar with no issues, however the window and all its children only get drawn at the new position when I let go of the scrollbar.
It seems that my entire program stalls and gets stuck in the message loop when the scroll bar is held down. This isnt a problem if we can force the window that the scrollbar is attached to to update. But how do I do that ?
I've tried this
UpdateWindow(pPane);
RedrawWindow(pPane,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
And it does update and redraw - you can see the glitching - BUT it redraws it at the position that it was last left at. Not where the new position of the scroll bar would set it.
Im calling
SetScrollInfo(...)
with the correct parameters, otherwise it wouldn't work normally
How can I get it to redraw the window and its children with the correct parameters, whilst the scroll bar is held down?
This is my scroll message handler
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_LINEUP:
if(vsi.nPos>vsi.nMin)
vsi.nPos=vsi.nPos-10;
break;
case SB_PAGEUP:
vsi.nPos = vsi.nPos - vsi.nPage;
break;
case SB_LINEDOWN:
if(vsi.nPos<vsi.nMax)
vsi.nPos=vsi.nPos+10;
break;
case SB_PAGEDOWN:
vsi.nPos = vsi.nPos + vsi.nPage;
break;
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=pOW->dialogysize;
vsi.nPage=sy;
SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hWnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
RedrawWindow(hWnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
}
The window redraws as you move the scroll bar, but in the same place it was when you held the mouse down on the scrollbar. It onlyupdates position when you let go of the mouse button.
Thanks
Shaun
EDIT - Ive created a sample that reproduces the behaviour. Note we have a separate window class to hold the multiple edit windows. I havent handled the WM_SIZE and WM_MOVE messages in this demo but the code should work with the window as it is.
IF USESCROLL is defined in and you move the scrollbar up and down it casues a lockup in DesktopWindowManager
IF USESCROLL is not commented in, then the trackPos in the structure still gets updated and I call reDrawWindow, but the child edit windows dont move as I scroll the scrollbar.
As I dont want to have the lockup, how can I get them to move please ?
Thanks again
Shaun
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Class";
RegisterClass(&wc);
wc.lpfnWndProc = PaneProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Pane";
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Class", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW| WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SYSMENU, // Window style
200,200,400,400,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
HWND hwndPane = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Pane", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL, // Window style
220,220,400,400,
hwnd,NULL,hInstance,NULL);
if (hwndPane == NULL)
{
return 0;
}
for(int i=0;i<200;i++)
{
HWND eb = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), NULL,ES_AUTOHSCROLL | WS_CHILD| WS_VISIBLE, 16, 16+24*i, 64, 24, hwndPane, (HMENU)i+10000, hInstance, NULL);
char tmp[64];
sprintf(tmp,"%d",i);
SetWindowText(eb,tmp);
}
ShowWindow(hwnd, nCmdShow);
ShowWindow(hwndPane, nCmdShow);
// Run the message loop.
while(1)
{
MSG msg = { };
while (PeekMessage(&msg, NULL, 0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
float ScrollAmtY=-(vsi.nPos - iVScrollPos);
#define USESCROLL
#ifdef USESCROLL
int ok=ScrollWindowEx(hwnd ,0,ScrollAmtY,NULL,NULL,NULL,NULL,SW_INVALIDATE|SW_ERASE|SW_SCROLLCHILDREN);
#else
UpdateWindow(hwnd);
RedrawWindow(hwnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
#endif
}
return 0;
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
I have a problem handling a Windows window, even though I did it this way once before and it worked fine. After reading through the most common suggestions for this problem, it still resides. Could someone tell me why Input-handling is broken?
Intended Behaviour:
create a window titled 'FirstTry'
Make its background black, using PatBlt
Show a message box when the main loop is entered the first time, and after pressing 'w'.
Close the window when pressing either Alt+F4, Escape, or the Close-button, displaying a closing message.
Observed behaviour:
as intended
as intended
MessageBox shows up the first time, but is not retriggerable with 'w'
Window it not closable, except with TaskManager (one time it showed the 'closing Application'-MessageBox as intended, but only one time)
window draggable until the first 'entered loop'-MessageBox is closed, after that its fixed
little blue 'busy'-circle of windows10 is shown full-time, after the first MessageBox
Conclusion: The Message-handling is broken.
And i cannot figure out why...
System:
Windows 10, Version 1803 (Build 17134.81), 64-bit
Compiler from VS 2017 Community Edition:
vcvarsall.bat amd64
cl -MTd -nologo -FC -Zi -W4 -WX -wd4100 -wd4312 FirstTry.cpp /link User32.lib Gdi32.lib
#include "windows.h"
static bool bAppIsRunning = false;
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam){
LRESULT result = 0;
switch(msg){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown)
{
switch (vKeyCode)
{
case VK_ESCAPE:{
bAppIsRunning = false;
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
}
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
return result;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow){
WNDCLASSA wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = (LPCSTR)"WindowClass";
if(RegisterClassA(&wndCLass)){
HWND wnd = CreateWindowExA(
0, wndCLass.lpszClassName, (LPCSTR)"FirstTry",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
1240, 720,
0, 0, HInstance, 0);
if(wnd){
bAppIsRunning = true;
HDC DeviceContext = GetDC(wnd);
PatBlt(DeviceContext, 0, 0, 1240, 720, BLACKNESS);
ReleaseDC(wnd, DeviceContext);
while(bAppIsRunning){
if(!bMessageAlreadyShown){
MessageBoxA(NULL, (LPCSTR)"Successfully entered loop.", (LPCSTR)"Success!", MB_ICONINFORMATION | MB_OK);
bMessageAlreadyShown = true;
}
MSG msg;
while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)){
switch(msg.message){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = msg.wParam;
bool bWasDown = ((msg.lParam & (1<<30)) != 0);
bool bIsDown = ((msg.lParam & (1<<31)) != 0);
if(bIsDown != bWasDown){
switch(vKeyCode){
case 'W':{
bMessageAlreadyShown = false;
}break;
default:{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}break;
}
}
}
}
}
}
MessageBoxA(NULL, (LPCSTR)"Closing Application.", (LPCSTR)"Bye bye!", MB_ICONINFORMATION | MB_OK);
}
}
return ERROR_SUCCESS;
}
The main problem with your code is that you are calling TranslateMessage() and DispatchMessage() only when you receive certain key press messages. You need to call them in your main message loop for ALL messages instead. And you should be processing ALL of the messages in your WndProc callback.
You are also using TCHAR-based APIs, but are misusing LPCTSTR typecasts. You need to use the TEXT() macro instead when casting string/char literals to TCHAR.
Try something more like this instead:
#include <windows.h>
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP: {
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown) {
switch (vKeyCode) {
case 'W':
case VK_ESCAPE:
DestroyWindow(wnd);
return 0;
}
}
break;
}
case WM_ERASEBKGND:
PatBlt((HDC)wParam, 0, 0, 1240, 720, BLACKNESS);
return 0;
}
return DefWindowProc(wnd, msg, wParam, lParam);;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow) {
WNDCLASS wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = TEXT("WindowClass");
if (RegisterClass(&wndCLass)) {
HWND wnd = CreateWindowEx( 0, wndCLass.lpszClassName, TEXT("FirstTry"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1240, 720, 0, 0, HInstance, 0);
if (wnd) {
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (!bMessageAlreadyShown) {
bMessageAlreadyShown = true;
MessageBox(NULL, TEXT("Successfully entered loop."), TEXT("Success!"), MB_ICONINFORMATION | MB_OK);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
MessageBox(NULL, TEXT("Closing Application."), TEXT("Bye bye!"), MB_ICONINFORMATION | MB_OK);
return ERROR_SUCCESS;
}
Note that I removed your bAppIsRunning variable, as it has beecome redundant once the message loop processes the WM_QUIT message instead.
I also removed handling of ALT-F4, as the OS handles that for you automatically. It closes the window, triggering a WM_CLOSE message. By default, DefWindowProc() handles WM_CLOSE by destroying the window, which triggers a WM_DESTROY message.
I also added handling for WM_ERASEBKGND to draw a background on the window. Drawing from outside of the message loop is wrong. As soon as the window needs to be refreshed onscreen, any drawing you do is lost, so you have to redraw everything in response to WM_ERASEBKGND and WM_PAINT.
I want a window without title bar but with resizable frames and shadow.
This can easily be achieved by removing WS_CAPTION and adding WS_THICKFRAME, however, since Windows 10, there's a 6px white non-client area.
With the following code I create a window and paint all the client area with black, the window gets a left, right and bottom 6px transparent margins, however the top margin is white.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"", // Window text
0,
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
ShowWindow(hwnd, nCmdShow);
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle |= WS_THICKFRAME;
lStyle = lStyle & ~WS_CAPTION;
SetWindowLong(hwnd, GWL_STYLE, lStyle);
SetWindowPos(hwnd, NULL, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Paint everything black
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOWTEXT));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Renders:
How can I remove the white stripe ?
I also found this related Qt bug report QTBUG-47543 which was closed as not being a Qt problem, because it can be reproduced with win32 api.
That's not a bug. In Windows 10 the borders on left/right/bottom are transparent. The top border is not transparent. You should leave it as is. Probably nobody will complain.
To change it, you must modify the non-client area. This is rather difficult in Windows Vista and above. See Custom Window Frame Using DWM for reference.
Find border thickness
Use DwmExtendFrameIntoClientArea to get access to non-client area
Use BeginBufferedPaint to draw opaque color over non-client area
Windows 10 example:
(See the next example for compatibility with Windows Vista, 7, 8)
//requires Dwmapi.lib and UxTheme.lib
#include <Windows.h>
#include <Dwmapi.h>
void my_paint(HDC hdc, RECT rc)
{
HBRUSH brush = CreateSolidBrush(RGB(0, 128, 0));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static RECT border_thickness;
switch (uMsg)
{
case WM_CREATE:
{
//find border thickness
SetRectEmpty(&border_thickness);
if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
{
SetRect(&border_thickness, 1, 1, 1, 1);
}
MARGINS margins = { 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
HDC memdc;
HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, ¶ms, &memdc);
my_paint(memdc, rc);
BufferedPaintSetAlpha(hbuffer, &rc, 255);
EndBufferedPaint(hbuffer, TRUE);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCACTIVATE:
return 0;
case WM_NCCALCSIZE:
if (lParam)
{
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam;
sz->rgrc[0].left += border_thickness.left;
sz->rgrc[0].right -= border_thickness.right;
sz->rgrc[0].bottom -= border_thickness.bottom;
return 0;
}
break;
case WM_NCHITTEST:
{
//do default processing, but allow resizing from top-border
LRESULT result = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (result == HTCLIENT)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hwnd, &pt);
if (pt.y < border_thickness.top) return HTTOP;
}
return result;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
CreateWindowEx(0, CLASS_NAME, NULL,
WS_VISIBLE | WS_THICKFRAME | WS_POPUP,
10, 10, 600, 400, NULL, NULL, hInstance, NULL);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
For compatibility with Windows Vista/7/8 use this procedure instead. This will paint over left/top/bottom borders as well as top border. This window will appear as a simple rectangle, with resizing borders:
//for Windows Vista, 7, 8, 10
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static RECT border_thickness;
switch (uMsg)
{
case WM_CREATE:
{
//find border thickness
SetRectEmpty(&border_thickness);
if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
{
SetRect(&border_thickness, 1, 1, 1, 1);
}
MARGINS margins = { 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
HDC memdc;
HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, ¶ms, &memdc);
my_paint(memdc, rc);
BufferedPaintSetAlpha(hbuffer, &rc, 255);
EndBufferedPaint(hbuffer, TRUE);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCACTIVATE:
return 0;
case WM_NCCALCSIZE:
if (lParam)
return 0;
case WM_NCHITTEST:
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hwnd, &pt);
RECT rc;
GetClientRect(hwnd, &rc);
enum {left=1, top=2, right=4, bottom=8};
int hit = 0;
if (pt.x < border_thickness.left) hit |= left;
if (pt.x > rc.right - border_thickness.right) hit |= right;
if (pt.y < border_thickness.top) hit |= top;
if (pt.y > rc.bottom - border_thickness.bottom) hit |= bottom;
if (hit & top && hit & left) return HTTOPLEFT;
if (hit & top && hit & right) return HTTOPRIGHT;
if (hit & bottom && hit & left) return HTBOTTOMLEFT;
if (hit & bottom && hit & right) return HTBOTTOMRIGHT;
if (hit & left) return HTLEFT;
if (hit & top) return HTTOP;
if (hit & right) return HTRIGHT;
if (hit & bottom) return HTBOTTOM;
return HTCLIENT;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Just to expand on this a little; in order to remove the white stripe one just has to remove the corresponding value from the first rect in NCCALCSIZE. pywin32 code would be:
if msg == WM_NCCALCSIZE:
if wParam:
res = CallWindowProc(
wndProc, hWnd, msg, wParam, lParam
)
sz = NCCALCSIZE_PARAMS.from_address(lParam)
sz.rgrc[0].top -= 6 # remove 6px top border!
return res
I think we don't need to work with DWM to remove this border. This white top resize border belongs to the non-client area of a window. So for removing it you should handle window messages related to the resizing and activating of non-client area of a window like below: ( tested only on Win 10 )
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* When we have a custom titlebar in the window, we don't need the non-client area of a normal window
* to be painted. In order to achieve this, we handle the "WM_NCCALCSIZE" which is responsible for the
* size of non-client area of a window and set the return value to 0. Also we have to tell the
* application to not paint this area on activate and deactivation events so we also handle
* "WM_NCACTIVATE" message. */
switch( nMsg )
{
case WM_NCACTIVATE:
{
/* Returning 0 from this message disable the window from receiving activate events which is not
desirable. However When a visual style is not active (?) for this window, "lParam" is a handle to an
optional update region for the nonclient area of the window. If this parameter is set to -1,
DefWindowProc does not repaint the nonclient area to reflect the state change. */
lParam = -1;
break;
}
/* To remove the standard window frame, you must handle the WM_NCCALCSIZE message, specifically when
its wParam value is TRUE and the return value is 0 */
case WM_NCCALCSIZE:
if( wParam )
{
/* Detect whether window is maximized or not. We don't need to change the resize border when win is
* maximized because all resize borders are gone automatically */
WINDOWPLACEMENT wPos;
// GetWindowPlacement fail if this member is not set correctly.
wPos.length = sizeof( wPos );
GetWindowPlacement( hWnd, &wPos );
if( wPos.showCmd != SW_SHOWMAXIMIZED )
{
RECT borderThickness;
SetRectEmpty( &borderThickness );
AdjustWindowRectEx( &borderThickness,
GetWindowLongPtr( hWnd, GWL_STYLE ) & ~WS_CAPTION, FALSE, NULL );
borderThickness.left *= -1;
borderThickness.top *= -1;
NCCALCSIZE_PARAMS* sz = reinterpret_cast< NCCALCSIZE_PARAMS* >( lParam );
// Add 1 pixel to the top border to make the window resizable from the top border
sz->rgrc[ 0 ].top += 1;
sz->rgrc[ 0 ].left += borderThickness.left;
sz->rgrc[ 0 ].right -= borderThickness.right;
sz->rgrc[ 0 ].bottom -= borderThickness.bottom;
return 0;
}
}
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
case WM_NCCALCSIZE: {
// This must always be last.
NCCALCSIZE_PARAMS* sz = reinterpret_cast<NCCALCSIZE_PARAMS*>(lparam);
// Add 8 pixel to the top border when maximized so the app isn't cut off
// on windows 10, if set to 0, there's a white line at the top
// of the app and I've yet to find a way to remove that.
sz->rgrc[0].top += 1;
sz->rgrc[0].right -= 8;
sz->rgrc[0].bottom -= 8;
sz->rgrc[0].left -= -8;
// Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't
// actually break anything so I've set it to 0. Unless someone pointed a
// problem in the future.
return 0;
}
Change the style of dialog.
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle |= WS_THICKFRAME; // 6px white stripe cause of this.
lStyle = lStyle & ~WS_CAPTION;
I'd like to put a button in a foreign windows' title bar, much like Teamviewer does with the Quickconnect feature, or like Chrome has one in the top-right for switching users.
I know this is a repeat of How is Teamviewers Quickconnect button accomplished?
I'm just wondering if it would be possible to get a working example or a link to an open-source program that implements this. The answers given there are rather advanced for me. As in, how am I supposed to "hook" and "intercept" WM_NCPAINT message and so on.
This is the most simple example i can develop:
You need Visual Studio, add 2 project to the solution:
first project (HookDLL) is a dll project, second (Running app) is a win32 console project
in main.cpp (at project Running app) add this:
__declspec(dllimport) void RunHook();
int _tmain(int argc, _TCHAR* argv[])
{
RunHook();
return 0;
}
in dllmain button.cpp (at HookDLL project) add this code:
#include <Windows.h>
#include <stdio.h>
HINSTANCE hinstDLL;
HHOOK hhook_wndproc;
HWND b = NULL;
HBRUSH blue_brush = NULL, yellow_brush, red_brush;
int button_status = 0;
LRESULT CALLBACK DefaultWindowProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_CREATE:
if(!blue_brush)
{
blue_brush = CreateSolidBrush(RGB(0, 0, 255));
yellow_brush = CreateSolidBrush(RGB(255, 255, 0));
red_brush = CreateSolidBrush(RGB(255, 0, 0));
}
break;
case WM_PAINT:
{
HBRUSH b;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
switch(button_status)
{
case 0:
b = blue_brush;
break;
case 1:
b = yellow_brush;
break;
default:
b = red_brush;
}
FillRect(hdc, &ps.rcPaint, b);
EndPaint(hwnd, &ps);
}
return 0;
case WM_MOUSEMOVE:
if(button_status == 0)
{
SetTimer(hwnd, 1, 100, NULL);
button_status = 1;
InvalidateRect(hwnd, NULL, false);
}
return 0;
case WM_TIMER:
{
POINT pt;
GetCursorPos(&pt);
if(button_status == 1 && WindowFromPoint(pt) != hwnd)
{
KillTimer(hwnd, 1);
button_status = 0;
InvalidateRect(hwnd, NULL, false);
}
}
return 0;
case WM_MOUSELEAVE:
button_status = 0;
InvalidateRect(hwnd, NULL, false);
return 0;
case WM_LBUTTONDOWN:
button_status = 2;
InvalidateRect(hwnd, NULL, false);
return 0;
case WM_LBUTTONUP:
if(button_status == 2) MessageBox(GetParent(hwnd), "teamviewer like button clicked", "Message", MB_OK);
button_status = 1;
InvalidateRect(hwnd, NULL, false);
return 0;
}
return DefWindowProc(hwnd, Msg, wParam, lParam);
}
void InitButton(HWND parent, int xPos, int yPos)
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = DefaultWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "DEFAULT_CLASS";
RegisterClass(&wc);
b = CreateWindowEx(WS_EX_TOOLWINDOW, "DEFAULT_CLASS", NULL, WS_BORDER | WS_POPUP | WS_VISIBLE, xPos, yPos, 20, 20, parent, NULL, hinstDLL, NULL);
}
LRESULT WINAPI HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode >= 0 && lParam != 0)
{
CWPRETSTRUCT *msg = (CWPRETSTRUCT*)lParam;
if(!IsWindow(msg->hwnd) || (GetWindowLong(msg->hwnd, GWL_STYLE) & WS_CHILD) != 0) return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
switch(msg->message)
{
case WM_SHOWWINDOW:
if(!b && msg->wParam != 0)
{
b = (HWND)1;// see NOTES 5
RECT a;
GetWindowRect(msg->hwnd, &a);
InitButton(msg->hwnd, a.right - 150, a.top);
}
break;
case WM_SIZE:
if(GetParent(b) == msg->hwnd)
{
RECT a;
GetWindowRect(msg->hwnd, &a);
SetWindowPos(b, 0, a.right - 150, a.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
break;
case WM_SIZING:
case WM_MOVING:
if(GetParent(b) == msg->hwnd)
{
RECT* lprc = (LPRECT) msg->lParam;
SetWindowPos(b, 0, lprc->right - 150, lprc->top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}
}
return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
}
__declspec(dllexport) void RunHook()
{
hhook_wndproc = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProc, hinstDLL, 0);
char aux[10];
gets_s(aux);
UnhookWindowsHookEx(hhook_wndproc);
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hinstDLL = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Now, make dll project depedent of running app project in project->project dependencies:
NOTES:
1) i dont use NC paint code, because not always works, if windows buffer non client region, erases customized NC paint buttons
2) in 64 bits enviroment, you need to run a 32 bits hook for 32 bits apps, and other hook for 64 bits apps
3) YOU CAN NOT DEBUG YOUR HOOK WHEN IS CONNECTED TO ANOTHER PROCCESS, i suggest you debug it with a windows in your app and thread, and test it late in another proccess when is working
4) i use a button like approach for simplicity
5) this line
b = (HWND)1;
I use it for "solve" a multi thread problem, i suggest you make better code (syncronization) this case
HOW THIS WORKS:
run the app
when it start install a hook
open any other app (same 32/64 bits, see NOTE 2)
you must see a blue button at left side of title bar
click it and see a message box
for finish hook: just press ENTER at console window
CODE FLOW:
Running app just calls RunHook() procedure in dll, and dll do the work
RunHook() in dll starts a hook HookCallWndProc (global)
HookCallWndProc captures required message and creates the window button using InitButton() procedure
DefaultWindowProc handles button message
I have a program that is supposed to display a count that increments every second. The counter is in a separate thread. I call WindowUpdate when a change has occurred but the count in the window does not update unless I hover the mouse pointer over the window or resize the window. I have tried InvalidateRect and RedrawWindow but they don't work either.
Why won't the counter updates display?
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
BOOL Updated;
int Counter;
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(hInstance, IDC_ARROW);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = ::CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
pPayload->Updated = FALSE;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread(
NULL,
0,
Counter,
pPayload,
0,
&dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
if (pPayload->Updated == TRUE)
{
// InvalidateRect(hWnd, NULL, FALSE);
// RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
::UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
return 0;
break;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
pTransfer->Updated = TRUE;
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
(Disclaimer: This answer was written by the seat of my pants. Please correct me if I made an error somewhere.)
You are not doing what you are trying to do properly.
First, as Jonathan Potter has noted, UpdateWindow() by itself will not update the window unless it has an invalid region. The InvalidateRect() call will invalidate a rectangle. So you need both...
...but in reality you don't really want to call UpdateWindow() directly, because it bypasses the Windows drawing model. After calling InvalidateRect(), if there are no pending messages next time GetMessage() is called, and Windows itself decides it's time to refresh the screen contents with new data, you'll get a WM_PAINT message. The system knows when it's best to paint; you'll make your life easier by using it. Only use UpdateWindow() when it is vital that you want the window to redraw right now.
(The point of the invalid region is also due to the Windows drawing model: drawing can be very expensive, so Windows tries to optimize drawing by only redrawing what is needed. Invalidate only the part of your window that needs to be updated, and you'll get better performance.)
But there is a deeper issue: you do not implement multithreading properly.
Your worker thread is generating data every second and overwriting a shared structure. Your window is reading from that shared structure. There is nothing in place to ensure that only one thread is accessing that shared structure at a time. As a result, you'll wind up with ptential mixed state if your worker thread ever grows from a simple integer to a large and complex data structure.
You need to synchronize your data accesses. Communicate between threads.
How? The obvious method is to use a synchronization object, like a mutex. And you can totally do that.
But there's a better way: since one of your threads has a window, just use a window message! Window messages sent using SendMessage() will be received on the window's thread (with the calling thread blocked); messages posted using PostMessage() will be placed on the window's thread's message queue.
Plus you can pass not one but two pieces of information in that message! Both wParam and lParam are pointer-sized integers, so just stuff a pointer in one of them and use SendMessage() or PostMessage()!
But what message do you use? Every* message in the range [WM_USER, WM_APP) is available to the window class to decide what to use it for, and every message in the range [WM_APP, 0xC000) is for the application to decide what to use it for. So just pick one and use it!
Just remember how SendMessage() and PostMessage() work. If the data is allocated on the heap, and each piece of data is allocated separately, then it doesn't matter; if the data is a pointer to a local variable, SendMessage() is the correct answer; if the data is a numeric value that can fit in a pointer-sized integer, either will work. If you need a response, your worker thread will either need a window of its own or the use of SendMessage() to get a value back from the window.
There are lots of ways to do this. It all depends on what data you need to communicate. As the Go programming language's designers say (altered somewhat): don't communicate by sharing data; share data by communicating.
*until you call IsDialogMessage(), at which point you lose WM_USER and WM_USER + 1 (and possibly WM_USER + 2 as well?). But that's only three messages out of over 32,000.
Your problem is in your message loop:
while (1)
{
if (pPayload->Updated == TRUE) {
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage is synchronous, and doesn't return until a message is available for retrieval. The code sits there, waiting for a message to arrive most of the time, and will not evaluate pPayload->Updated. Consequently, no updates happen, until a message arrives. But even then, nothing happens, since UpdateWindow sends "a WM_PAINT message to the window if the window's update region is not empty." UpdateWindow by itself doesn't do anything useful.1)
You could solve this by reworking your message loop. The following issues need to be addressed:
A call to InvalidateRect must precede the call to UpdateWindow.
Access to the shared data must be synchronized.
The message loop needs to terminate, when GetMessage returns 0 or -1.
None of this is necessary, though. You don't need a background thread. A single-threaded application will do just fine. Just use a Timer to trigger updates to the stored value, and the visual representation. Here is a rough sketch for a window procedure implementing a timer-based solution:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static unsigned __int64 startTime = ::GetTickCount64();
const UINT_PTR timerId = 1;
switch (uMsg)
{
case WM_CREATE:
// Start timer when window is created
::SetTimer(hWnd, timerId, 1000, nullptr);
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
case WM_TIMER: {
unsigned __int64 currTime = ::GetTickCount64();
Counter = (currTime - startTime) / 1000;
// Value changed -> initiate window update
::InvalidateRect(hWnd, nullptr, FALSE);
// Re-start timer
::SetTimer(hWnd, timerId, 1000, nullptr);
}
break;
case WM_DESTROY:
::KillTimer(hWnd, timerId);
::PostQuitMessage(0);
break;
case WM_PAINT: {
PAINTSTRUCT paintStruct = {0};
HDC hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
}
return 0;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
A few notes on the implementation:
The timer uses a fixed time-out value of 1000 milliseconds. This will occasionally skip an update. The error doesn't accumulate, though, as the Counter is re-evaluated whenever the timer expires. If you more steady updates, calculate the remaining time-out value based on the Counter, startTime, and currTime.
Counter is the variable that holds the current time in seconds. It is a replacement for TRANSFER::Counter, that needs to be accessible from both the window procedure and the rendering code.
1) There is another issue with your message loop: It never terminates, and neither does your process. The GUI may disappear, but the process will still show up in Task Manager.
i marked my alterations with comment <<updated
instead of setting flag Updated, i changed it to be hWnd, and then update the window instantly and directly from the thread
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
//BOOL Updated; // << updated
int Counter;
HWND hWnd; // << updated
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //<< updated
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
//pPayload->Updated = FALSE; //<< updated
pPayload->hWnd=hWnd;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread( NULL, 0, Counter, pPayload, 0, &dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
// ____[ updated ]_____
/*
if (pPayload->Updated == TRUE)
{
UpdateWindow(hWnd);
pPayload->Updated=FALSE;
}
*/
int ret=GetMessage(&msg, NULL, 0, 0);//<< updated
if(ret==0 || ret==-1)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
InvalidateRect(pTransfer->hWnd,NULL,1);
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}