C Winapi FillRect()崩溃(多个rects)

C++ WinApi Fillrect() crashes (multiple rects)

本文关键字:多个 rects 崩溃 Winapi FillRect      更新时间:2023-10-16

我试图习惯于winapi,并决定为我编程的sudoku-generator制作GUI。它应该动态调整到用户选择的Windowsize。到目前为止,一切都按照直觉的方式工作,但是如果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 MSG之后,窗口将窗口冻结,其中1个是白色的,而不是所需的颜色(随机的颜色,每次不同)。我的假设是我需要发布某种资源,因为MBY堆栈会溢出或SMTH,但是我真的不知道我的程序泄漏了。

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

您每次执行窗口过程都会创建三个刷子。这些手柄永远不会整理。然后在WM_PAINT处理程序内部创建一个将您传递给FillRect的刷子,因此永远无法销毁它。

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

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

您正在泄露GDI资源作为成员 David Heffernan 所说。

这是如何在该示例中对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 );

再次,请注意上述MSDN中的WM_COMMAND处理程序,以查看他们是如何做到的。

这是一个出色的Win32 API教程的链接。

最后,我建议您此工具用于检测GDI泄漏。

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

最好的问候和祝你好运!