当前位置:C++技术网 > 精选软件 > WM_PAINT消息处理中的资源泄露内存泄露问题分析验证

WM_PAINT消息处理中的资源泄露内存泄露问题分析验证

更新时间:2015-12-06 19:10:04浏览次数:1+次

    在win32编程中,WM_PAINT消息可是一开始就接触到的了。然而,对于这里面涉及到的一些细节问题,却又很多不为人知的秘密哦。那么我们这里就对此做详细的分析,一探究竟。其中一个问题就是资源泄露内存泄露问题,我们会通过代码来验证。
    假如我们不想处理这个消息,就不要写这个消息的处理代码就可以了,让这个消息处理流入默认处理函数里处理。默认处理WM_PAINT消息代码就只是简单的执行了两句代码,即:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
    当然,你也可以写这两句代码作为你的默认处理,当然这有点多余了。这样如果不考虑后面添加代码,就让默认处理就好了,省的写代码。而可能你以为不处理就是直接返回0,其实这样是错误的,会引来问题。这个是消息系统的消息处理,所以才会出现问题。对于自定义的功能函数,当然返回0这样的写法不会有问题,这就是我们要将思维转入Win32编程中。在脑子里时时刻刻有一个消息流动的概念,而这里简单的返回0会出错就是因为消息流动的问题。那么你可以再看看一篇文章《Win32消息处理后必须返回0,窗口过程中必须有默认消息处理函数》。
    也正是我们这些错误的认为一个不处理或者一个默认无动作的处理恰恰就是问题的所在,现在就来仔细分析一下。
    看看资源泄露问题。这个问题比较好理解。这里的资源指的是设备描述表句柄,即HDC类型的变量,这个变量引用了系统资源,这个资源就是用来画图等使用的。我们使用BeginPaint来获取这个资源,然后使用EndPaint来释放资源。所以,这里你应该猜到了可能的问题了。
    如果我们通过BeginPaint获取了资源,然后没有养成习惯,就没有使用EndPaint来释放资源。虽然这个资源微不足道,但是积少成多的威力也是不容小觑的。况且,WM_PAINT消息是非常频繁的,所以,这个是一个很容易就积累出大问题的,而问题就是程序占用的内存越来越大,也就导致程序长时间运行后,越来越卡顿了。
    我们可以来测试一下,就是不写EndPaint,只有BeginPaint,然后我们不停的让窗口产生客户区重绘消息BWM_PAINT,这样就会不断的重绘了。而测试的方法就是不断的调整窗口大小或者最大化最小化切换等,然后在任务管理器里观察开始占用的内容和重绘一定次数之后,程序占用的内存,而且占用的内存是不会被释放的,来观察一下效果。同时使用一个整数i来统计刷新的次数。
    我生成的exe的名称为win32-1,那么在任务管理器中可以找到这个名称的进程,观察到所占的内存为8940KB,因为截图的时机等情况,每次截图显示的内存可能不一样,这个是正常的,因为在这个过程中会激发一些客户区重绘消息了,影响不大。
    然后我们不停的制造重绘的发生,进行一定的次数,再来观察。再不停的制造客户区重绘的过程中,发现内存占用以4K为单位在不停的增加,然后一路飙升,一直到了9004KB。我们检查一下统计的刷新的次数i的值,为232次。当然这里是粗略的统计了一下。而在232次客户区重绘过程中,就泄露了64KB的资源,也就造成内存泄露了64KB。而这232次算是小的可怜的次数,在一般的程序中,重回次数会很大,则造成的泄露则很严重了。虽然不至于很快让程序死掉,但是会不断的降低程序运行的性能。因为泄露的内存将不会主动释放,一直到程序退出才会被系统释放,在程序运行过程中是被程序占用的,所以这是很严重的问题哦。前后的内存占用对比图如下所示:

  开始运行程序是占用的内存数

开始运行程序占用的内存数

 客户区重绘一定232次后占用的内存数

客户区重绘一定232次后占用的内存数

    
   诸如此类的问题,比如GetDC之后,一定要ReleaseDC。C语言的new和delete的成对使用,都是这类问题。所以,在涉及到这种语句的使用时,一定要惦记着后面这句配套的语句哦。如果你发现程序运行一段时间后卡顿,就可能是这个问题,你可以检查这种成对的语句。
    验证的完整代码如下:
#include "windows.h"
#include <tchar.h>
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    PAINTSTRUCT ps;
    static int i=0;
    switch (message)
    {
     case WM_PAINT:
        BeginPaint(hwnd,&ps);
        //EndPaint(hwnd,&ps);// - 内存泄露
         i++;
        return 0;
     case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
     default:
         return DefWindowProc(hwnd, message, wParam, lParam);
    }
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title1[] = _T("C++技术网http://www.cjjjs.com");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(GRAY_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClass(&wndClass))
    {
        return 0;
    }

    HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,0,0,440,400,NULL,NULL,hInstance,NULL);
    ShowWindow(hwnd,SW_SHOWNORMAL);

    MSG msg;
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}