WM_PAINT消息 - 可以通过2个函数调用它吗? [英] WM_PAINT message - can 2 functions call it?

查看:112
本文介绍了WM_PAINT消息 - 可以通过2个函数调用它吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,



我的查询与用C ++编写的WIN32程序中的WM_PAINT函数有关。



首先,对于那些感兴趣的人,有一点历史:



我:

在Uni学习基础C编程的电气工程师,自学那里的基本C ++。还涉及JAVA和一些机器代码(任何人都记得Xilinx ??)。我写的大部分程序都是在Uni期间,是C ++中的控制台应用程序,大多是简单的计算/数学类型程序(大约10年前)。还偶尔在ColdFusion的朋友网站上工作。



当前项目:

未编程5年以上,我决定尝试在WIN32中编写程序;虽然我不会泄露太多细节,但前提基本上是一个程序,它根据正在进行的算法在同一个窗口上显示和删除某些形状(一次最多30个)。



所以,到目前为止我得到的是一段非常简单的代码,它调用一个名为TEST的函数,该函数又调用RECT1(这个矩形显示)。 TEST然后也调用RECT2,这是我丢失的地方,因为这个矩形没有显示...



我已经通过functionx网站阅读了介绍/教程,以及代码项目Paul M Watt以及其他人。



代码如下 - 我已经放入Sleep()函数来测试代码是否进入函数,你看到'忙'光标(原始,我知道 - 我愿意接受更好的方法)。代码确实进入了RECT2功能,但没有绘制矩形。





所以问题基本上是,可以两个或更多单独的函数调用WM_PAINT消息?如果它可以被称为我的多个功能,它使用的HWND和HDC如何受到影响?正如您在代码中看到的,我已将'hWnd'变量传递给每个函数。我试图为'hdc'做同样的事情,但我得到错误(未显示)...基本上我必须在每个函数中创建一个'hdc'的HDC变量 - 或者我认为?





感谢您愿意提供的任何帮助。



谢谢



Hi all,

My query relates to the WM_PAINT function in WIN32 programs written in C++.

First, for those interested, a little history:

Me:
Electrical engineer who studied basic C programming at Uni, taught myself basic C++ from there. Also dabbled in JAVA and some machine code (anyone remember Xilinx??). Most programs I wrote were during Uni, were console apps in C++, mostly simple calculation / mathematics type programs (which was around 10 years ago). Also occasionally worked on a friends website written in ColdFusion.

Current project:
Having not programmed for 5+ years, I decided to try write a program in WIN32; while I won't give away too many details, the premise is basically a program which shows and deletes certain shapes (upto 30 at one time) on the same window, based on an ongoing algorithm.

So, what I have got so far is a very simple piece of code which calls a function called TEST, which in turn calls RECT1 (this rectangle DOES show). TEST then also calls RECT2, which is where I am lost, as this rectangle DOES NOT show...

I have read through intro / tutorials by the functionx site, as well as Code Projects Paul M Watt, as well as others.

Code is below - I have put in Sleep() functions to test whether the code enters the function, where you see 'busy' cursor (primative, I know - I am open to better ways to do this). Code does in fact enter the RECT2 function, but does not carry out drawing the rectangle.


So the question basically is, can two or more separate functions call the WM_PAINT message? If it can be called my more than 1 function, how is the HWND and HDC it uses affected? As you can see in the code, I have passed the 'hWnd' variable onto each function. I have tried to do the same for 'hdc' but I get errors (not shown)...basically I have to create a HDC variable of 'hdc' in each function - or so I think??


Appreciate any help you are willing to offer.

Thanks

#include <windows.h>

/*  Make the class name into a global variable  */

LPCTSTR ClassName = "BasicApp";
LPCTSTR WindowName = "A Simple Window";


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
VOID TEST (HWND);
VOID RECT1 (HWND);
VOID RECT2 (HWND);

INT WINAPI WinMain (
                   HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow
                   )                
                
                    
{
    HWND hWindow;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX WndClsEx;

	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WindowProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.lpszClassName = ClassName;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);


    /* Register the window class, and if it fails quit the program */
	RegisterClassEx(&WndClsEx);


    /* The class is registered, let's create the program*/
    hWindow = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           ClassName,         /* Classname */
           WindowName,       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           CW_USEDEFAULT,                 /* The programs width */
           CW_USEDEFAULT,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

// Find out if the window was created
	if( !hWindow ) // If the window was not created,
		return 0; // stop the application


    /* Make the window visible on the screen */
    ShowWindow (hWindow, SW_SHOWNORMAL);
    UpdateWindow(hWindow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
} //int WINAPI WinMain



LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{

    switch(Msg)
    {
    case WM_PAINT: 

         TEST(hWnd);
         break;
         
    case WM_DESTROY:
        PostQuitMessage(WM_QUIT);
        break;

   default:
        return DefWindowProc(hWnd, Msg, wParam, lParam);

    }

}



VOID TEST (HWND hWnd)
{
     RECT1 (hWnd);
//     Sleep(2000);
     RECT2 (hWnd);
     return;    
}


VOID RECT1 (HWND hWnd)
{
    HDC	hdc;
    PAINTSTRUCT ps;
	
    hdc = BeginPaint(hWnd, &ps);
	Rectangle (hdc, 10, 10, 50, 50);
	EndPaint(hWnd, &ps);
	
	return;	
}



VOID RECT2 (HWND hWnd)
{
//    Sleep(2000); 
    HDC	hdc;
    PAINTSTRUCT ps;
	
    hdc = BeginPaint(hWnd, &ps);
	Rectangle (hdc, 110, 110, 150, 150);
	EndPaint(hWnd, &ps);
    Sleep(2000);	
	return;	
}

推荐答案

我可以看到很多问题,我会逐一解决。



首先,Rectangle函数不仅绘制边框,而且还使用当前画笔_fills_ rect,在这种情况下是背景画笔。



接下来,每个WM_PAINT消息只调用一次BeginPaint / EndPaint。



你需要注意预期的回报如果您处理消息的值,则无法执行此操作可能意味着在系统等待通知已处理消息时反复发送消息。对于WM_PAINT,你应该返回0.



我修复了这些疏忽并添加了一个新功能,它同时接受HWND和HDC。这旨在从客户区域的边缘绘制10个像素的边框。你可以看到,如果你把它放在onPaint函数中的其他两个之后,它看起来好像没有用 - 这只是因为它超越了它们的顶部。



给你:

I can see a number of issues, I'll address them one by one.

Firstly, the Rectangle function doesn't just draw a border, but it also _fills_ the rect, using the current brush, which in this case is the background brush.

Next, you only call BeginPaint/EndPaint once per WM_PAINT message.

You need to pay attention to the expected return values if you process messages, failing to do so can mean that the message is sent over and over while the system waits to be informed that it has been handled. For WM_PAINT, you should return 0.

I've fixed these oversights and added a new function, which takes both the HWND and the HDC. This is designed to draw a border 10 pixels in from the edge of the client area. You can see that if you put it after the other two in the onPaint function, it appears as though they haven't worked - this is simply because it draws over the top of them.

Here you go:
#include <windows.h>

/*  Make the class name into a global variable  */
LPCTSTR ClassName = "BasicApp";
LPCTSTR WindowName = "A Simple Window";

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

void onPaint(HWND hwnd);
void myRect1(HDC hdc);
void myRect2(HDC hdc);
void myRect3(HWND hwnd, HDC hdc);


INT WINAPI WinMain (
                   HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow
                   )


{
    HWND hWindow;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX WndClsEx;

	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WindowProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.lpszClassName = ClassName;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);


    /* Register the window class, and if it fails quit the program */
	RegisterClassEx(&WndClsEx);


    /* The class is registered, let's create the program*/
    hWindow = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           ClassName,         /* Classname */
           WindowName,       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           CW_USEDEFAULT,                 /* The programs width */
           CW_USEDEFAULT,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

// Find out if the window was created
	if( !hWindow ) // If the window was not created,
		return 0; // stop the application


    /* Make the window visible on the screen */
    ShowWindow (hWindow, SW_SHOWNORMAL);
    UpdateWindow(hWindow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
} //int WINAPI WinMain



LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{

    switch(Msg)
    {
        case WM_PAINT:
            onPaint(hWnd);
            return 0;
             //TEST(hWnd);
             //break;

        case WM_DESTROY:
            PostQuitMessage(WM_QUIT);
            return 0;
        //break;
       default:
            return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
}

void onPaint(HWND hwnd)
{
    HDC hdc;
    PAINTSTRUCT ps;

    hdc = BeginPaint(hwnd, &ps);

        myRect3(hwnd, hdc);
        myRect1(hdc);
        myRect2(hdc);

    EndPaint(hwnd, &ps);
}

void myRect1(HDC hdc)
{
    Rectangle(hdc, 10, 10, 50, 50);
}

void myRect2(HDC hdc)
{
	Rectangle (hdc, 110, 110, 150, 150);
}

void myRect3(HWND hwnd, HDC hdc)
{
    RECT clientRect;
    GetClientRect(hwnd, &clientRect);
    InflateRect(&clientRect, -10, -10);
    Rectangle(hdc, clientRect.left, clientRect.top, clientRect.right, clientRect.bottom);
}


请参阅我对该问题的评论。不仅没有呼叫消息这样的东西,你不应该直接发送它。事情并非如此。



触发此消息是一件非常复杂的事情。您可以看到,当任何其他窗口覆盖的窗口的任何部分暴露出先前覆盖的表面的一部分时,将触发您的方法。所以它发生在一些窗口的运动上,在Alt + TAB上,在刷新&很多;案例。



现在,如何自动触发?这对于任何类型的动画都非常重要。现在,WM_PAINT有什么特别之处?这个事件的处理非常狡猾。它不会通过常规Windows消息队列。相反,它是高度优化的。想象一下,在重新渲染(通过WM_PAINT事件处理程序)尚未完成之前,有两个无效状态一个接一个地进入,第二个被调用。你认为你的事件处理程序被调用了两次吗?没门!消息将在优化过程中合并为一个,并且无效区域将被一起进行OR运算。有趣的机制,不是吗?



基本上,你的处理程序应该在描述图形的一些数据上渲染一些图形库。它可能是一些矢量图形模型,纯数据。更改数据时,您必须执行其他失效,以通知系统并导致渲染。您不必重新渲染整个场景,只需使某些矩形或区域无效即可提高性能。具体如下:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd145005%28v=vs.85%29.aspx [ ^ ],

https://msdn.microsoft.com/en- us / library / windows / desktop / dd145167%28v = vs.85%29.aspx [ ^ ],

https://msdn.microsoft.com/en-us/library/windows/desktop/dd145002%28v= vs.85%29.aspx [ ^ ],

https: //msdn.microsoft.com/en-us/library/windows/desktop/dd145195%28v=vs.85%29.aspx [ ^ ]。



-SA
Please see my comment to the question. Not only there is no such thing as "call a message", you should not send it directly. This is not how things work.

The triggering of this message is quite a complex thing. You can see that you method will be fired when any part of your window covered by any other window exposes part of the previously covered surface. So it happens on motion of some windows, on Alt+TAB, on refresh &many; cases.

Now, how to trigger it automatically? This is very important for any kind of animation. Now, what's so special about WM_PAINT? The processing of this event is very cunning. It does not go through a regular Windows message queue. Instead, it is highly optimized. Imagine you have two invalidates coming one after another, with second one called before your re-rendering (through the WM_PAINT event handler) is not yet complete. Do you think your event handler is called twice? No way! The messages will be merged together in one in the optimization process, and there invalidated regions will be ORed together. Interesting mechanism, isn't it?

Basically, your handler should render some graphics bases on some data describing graphics. It could be some vector graphics model, pure data. When data is changed, you have to do additional invalidation, to notify the system and cause rendering. You don't have to re-render the whole scene, you can improve performance by invalidating only some rectangle or region. This is how:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145005%28v=vs.85%29.aspx[^],
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145167%28v=vs.85%29.aspx[^],
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145002%28v=vs.85%29.aspx[^],
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145195%28v=vs.85%29.aspx[^].

—SA


这篇关于WM_PAINT消息 - 可以通过2个函数调用它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆