C ++ WinApi Fillrect()崩溃(多个rects)

时间:2014-01-30 15:16:49

标签: c++ winapi crash

我正在尝试习惯WinApi,并决定为我编程的数独发生器制作GUI。 它应该动态调整到用户选择的窗口大小。 到目前为止,所有内容都是有意义的,但如果在短时间内发送WM_PAINT-msg(例如更改窗口大小),程序就会崩溃。

    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    stringstream ss; //not used
    RECT rect;
    int w;
    int h;
    HBRUSH coluns=CreateSolidBrush(RGB(50,120,180));
    HBRUSH colsel=CreateSolidBrush(RGB(80,150,220));
    HBRUSH colmso=CreateSolidBrush(RGB(50,70,190));
    switch (message)
        {
        case WM_SIZE:            //
            {
                GetWindowRect(hwnd,&rect);
                menu.wndw=rect.right-rect.left;        //menu is a class to store important information
                menu.wndh=rect.bottom-rect.top;
                h=menu.wndh;
                w=menu.wndw;
                for(int i=1;i<10;i++)
                {
                    for(int j=1;j<10;j++)
                    {
                        menu.feld[i][j].SetSpace((w/4)+((i-1)*(w/20))+i+(2*((i-1)/3)),(h/4)+((j-1)*(h/20))+j+(2*((j-1)/3)),(w/4)+((i)*(w/20))+i+(2*((i-1)/3)),(h/4)+((j)*(h/20))+j+(2*((j-1)/3)));
                    }     
                }           //feld is a class wich exists in a 10x10 array with the 0s not being used
                InvalidateRect(hwnd,NULL, TRUE);
            }
            break;
        case WM_PAINT:
            {
                RECT re;
                w=menu.wndw;
                h=menu.wndh;
                hdc = BeginPaint(hwnd,&ps);
                re.left=(w/4)-4;
                re.top=(h/4)-4;
                re.right=(w/4)+9*(w/20)+18;
                re.bottom=(h/4)+9*(h/20)+18;
                FillRect(hdc,&re,CreateSolidBrush(RGB(0,0,0)));
                for(int i=1;i<10;i++)
                {
                    for(int j=1;j<10;j++)
                    {
                        re=menu.feld[i][j].GetSpace();
                        if(menu.feld[i][j].GetSelect()==uns)
                            if(FillRect(hdc,&re,coluns)==0)
                                MessageBox(hwnd, "fail","fail",0);
                        if(menu.feld[i][j].GetSelect()==mso)
                            if(FillRect(hdc,&re,colmso)==0)
                                MessageBox(hwnd, "fail","fail",0);
                        if(menu.feld[i][j].GetSelect()==sel)
                            if(FillRect(hdc,&re,colsel)==0)
                                MessageBox(hwnd, "fail","fail",0);

                    }
                }
                EndPaint(hwnd, &ps);
            }
            break;

http://www.pic-upload.de/view-22113118/Unbenannt.png.html 这是执行程序的样子。

现在如前所述,如果你用很多小步骤改变windowsize,程序就会崩溃。在调用MW_PAINT消息约10次后,窗口将冻结,其中1个是白色而不是所需的颜色(随机的,每次不同)。 我的假设是我需要释放某种资源,因为mby堆栈会溢出或smth,但我真的不知道我的程序在哪里可以泄漏。

如果有人能帮助我,我将非常感激。

2 个答案:

答案 0 :(得分:4)

每次执行窗口过程时,都会创建三个画笔句柄。这些手柄永远不会整理。然后在WM_PAINT处理程序中,创建一个传递给FillRect的画笔,因此永远不会销毁它。

因此,每次窗口过程执行时都会泄漏三个句柄(这种情况会发生很多),每次处理WM_PAINT时都会泄漏一个句柄。简而言之,您的程序就像筛子一样泄漏!

您应该考虑在创建窗口时创建这些画笔,并在窗口被销毁时销毁它们。或者也许在WM_PAINT处理程序中创建它们,并在完成使用后立即销毁它们。但由于它们具有恒定的颜色,因此最好一次性创建4个刷子,一劳永逸。

答案 1 :(得分:1)

您正在泄露GDI资源 David Heffernan 成员。

Here是如何在应用程序中正确使用画笔的示例 - 请注意该示例中的WM_COMMAND处理程序。

如果您不使用库存GDI对象,则必须在使用它们后将其删除。

以下是使用WM_PAINT处理程序中的红色笔刷填充窗口的简单示例:

case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPain( hdc, &ps );
        HBRUSH hbrRedBrush = CreateSolidBrush( RGB( 255, 0, 0 ) );
        RECT r;
        GetClientRect( hWnd, &r );
        FillRect( hdc, &r, hbrRedBrush );
        DeleteObject( hbrRedBrush ); //you must delete GDI object!
        EndPaint( hWnd, &ps );
    }
    return 0L;

在你的情况下,我会制作4个static画笔并稍微修改我的代码,在WM_CLOSE处理程序中添加适当的清理。以下是建议的更改:

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, 
    WPARAM wParam, LPARAM lParam)
{
    // add static before HBRUSH
    static HBRUSH coluns=CreateSolidBrush(RGB(50,120,180));
    static HBRUSH colsel=CreateSolidBrush(RGB(80,150,220));
    static HBRUSH colmso=CreateSolidBrush(RGB(50,70,190));
    static HBRUSH BlackBrush = CreateSolidBrush(RGB(0,0,0));

    switch (message)
    {
    // this is the problematic handler
    case WM_PAINT:
        {
            //the changed part
            FillRect( hdc, &re, BlackBrush );

        }
        break;

    case WM_CLOSE:
        {
            DeleteObject( BlackBrush );
            DeleteObject( coluns );
            DeleteObject( colsel );
            DeleteObject( colmso );
            // other clean up code
        }
        break;

重要说明:

这次您使用了FillRect API,但下次您可能会将需要的位图和其他内容加载到HDC恢复到原始状态你完成绘画后。

你这样做:

HBITMAP bmpOld = (HBITMAP)SelectObject( hdc, myBitmap );
// bmpOld stores the original state of the device context
// you do something with myBitmap
// then you return device context into original state 
// by selecting the original value, bmpOld, back into device context 
SelectObject( hdc, oldBmp ); 
DeleteObject( myBitmap );

再次注意上面WM_COMMAND示例中的MSDN处理程序,看看他们是如何做到的。

Here是指向初学者的精彩Win32 API教程的链接 - 随意使用。

最后,我建议您this tool检测GDI泄漏。

如果您有其他问题,请发表评论,我会尽快回复。

祝你好运!