C++ Win32:HDC 和调试

C++ Win32: HDC's and debugging

我正在为俄罗斯方块游戏编写代码。这是早期的,现在它只显示一个片段(那个片段在那个时候是 "falling"),这是它应该做的。向上箭头允许您向前(特别是)循环通过随机生成的片段序列(使用 "bag" 方法)。并且,使用左右箭头,您可以旋转棋子。无论如何,它是在 win32 平台上编码的,我发现在一定数量的帧 (运行 WM_PAINT) 之后,主 HDC 变为空,一切都停止了。按住向右或向左箭头键所需的帧数大约是按住向上箭头键所用帧数的两倍。奇怪的是,在大约 1000 帧之后,我正在主动绘制的控制台区域(一个 600x600 像素的帧)将变黑(hdc 尚未被取消),并且只有当向上箭头被按下时hdc 无效。我怀疑问题是按下向上箭头键时调用的方法将 hdc 作为参数传递给 in-class 方法(告诉我这是不好的做法还是我应该做的事情反而)。我认为 hdc 可能正在损坏或其他原因(老实说我不知道​​)。左右箭头键不直接调用以 HDC 作为参数的方法。由于 case 标签和 win32 模板的设计方式,我必须存储一个范围不在任何 case-label 范围内的 HDC,这样我就可以在 paint case 之外访问 window 的句柄(我觉得像这样是不好的做法,但我想在我竭尽全力寻找新方法之前了解原因)。我将 post 代码,存储的 HDC 称为 mainHDC,它就在 main case 语句之前定义。为了给您提供线索,我将概述一下代码结构:

包含基本 win32 程序的主 .cpp 文件调用 WM_PAINT 中的俄罗斯方块 class 构造函数并像 mainHDC 一样将其普遍存储,并且在出现提示时 "next" 方法(带来下一个棋子),"turnPiece" 方法(根据参数顺时针或逆时针旋转棋子),以及更新屏幕的 "paint" 方法,重新绘制当前片。在俄罗斯方块 class(在它自己的头文件中)中,有一个名为 "pieces" 的子 class,它包含有关其对象的信息,这些对象由另一个级别的子定义classes 以棋子形状描绘的单个字符命名。碎片的形状、颜色和大小存储在二维指针数组中(使用 COLORREF)。 "Pieces" 包含它自己的 "DrawObject" 方法,该方法绘制调用它的对象(与所有 drawing/painting 方法一样,它有一个 HDC 作为参数)。还有一种旋转形状的方法,称为 "turnTet"("turnPiece" 方法将调用从主 .cpp 文件中继到 "pieces")。唯一其他适用的方法是在 "tetris" class 中找到的方法,它们是 "paint" 和 "next"(它们最终绘制对象)。 WM_KEY 个案例,不包括 VK_UP 个案例,不使用保存的 hdc,而是使用 InvalidateRect()。

这是 .cpp 文件的适用部分

int tempt = 2;
int tempvar = 0;
tetris *mainOBJ;
HDC mainHDC;
HDC testHDC;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_KEYDOWN:
        switch (wParam) {
        case VK_LEFT:
            mainOBJ->turnPiece(false);
            InvalidateRect(hWnd, 0, FALSE);
            break;
        case VK_RIGHT:
            mainOBJ->turnPiece(true);
            InvalidateRect(hWnd, 0, FALSE);
            break;
        case VK_UP:
            mainOBJ->next(mainHDC);
            InvalidateRect(hWnd, 0, FALSE);
            break;
        }
        break;
    case WM_COMMAND:
        //Non-applicable & has not been changed
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);

            mainHDC = hdc;

            HPEN oldP;
            HPEN newP;
            COLORREF qLC;
            qLC = RGB(0, 0, 0);
            newP = CreatePen(PS_SOLID, 1, qLC);
            oldP = (HPEN) SelectObject(hdc, newP);


            tempt++;
        //USED FOR COUNTING FRAMES WITH DEBUGGER
            if (tempt % 1 == 0) {

                if (tempvar == 0) {
            //CONSTRUCTOR CALL
                    mainOBJ = new tetris(hdc);
                    tempvar++;
                }
            //PAINT METHOD CALL
                mainOBJ->paint(hdc);
            }



            if (hdc == NULL) {

                int x = 3;
                int y = x + 3; //SET DEBUG_BREAK POINT HERE
            }

            testHDC = hdc;
            SelectObject(hdc, oldP);
            DeleteObject(newP);
            EndPaint(hWnd, &ps);

        }
        break;
    }
}

头文件,里面包含俄罗斯方块和棋子classes

class tetris {
public:
    class pieces {
    private:
        COLORREF **test;
        int size;
    public:
    //Abbreviated for space
        class O {};
        class I {};
        class S {};
        class Z {};
        class T {};
        class L {};
        class J {};
        void setSize(int a) {
        //Initializing the piece-array
            test = new COLORREF*[a];
            size = a;
            int i = 0;
            while (i < a) {
                test[i] = new COLORREF[a];
                i++;
            }

        }
        void setShape(COLORREF **shape) {
            test = shape;
        }
        void setColor(HDC hdc, COLORREF rgb) {
            HPEN Penn = CreatePen(PS_SOLID, 1, rgb);
            HPEN Peno = (HPEN)SelectObject(hdc, Penn);
        }
        static pieces getObject(char type) {

            pieces Gen;
            switch (type) {

            case 'O':
            {
                //Identical (almost) to the other cases
                O pcs = O();
                Gen = *pcs.getObject();
                return Gen;
            }
            break;
            case 'I':
            case 'S':
            case 'Z':
            case 'T':
            case 'L':
            case 'J':

            return pieces();
        }

        void turnTet(bool clockwise) {
            int i = 0;
            int s;
            COLORREF **shape;
            int ter = size - 1;
            shape = new COLORREF*[2];

            while (i < size) {
                shape[i] = new COLORREF[2];

                s = 0;
                while (s < size) {
                    shape[i][s] = def;
                    s++;
                }
                i++;
            }
            i = 0;

            while (i < size) {

                s = 0;
                while (s < size) {
                    if (clockwise) {
                        shape[s][ter - i] = test[i][s];
                    }
                    else {
                        shape[ter - s][i] = test[i][s];
                    }
                    s++;
                }
                i++;
            }
            test = shape;
        }
        void drawObject(HDC hWnd) {
            int i = 0;
            int s;
            while (i < size) {
                s = 0;
                while (s < size) {
                    setColor(hWnd, test[i][s]);
                    int scaleOfBox = 90;
                    DrawBox((s + 1) * scaleOfBox, (i + 1) * scaleOfBox, scaleOfBox - 1, scaleOfBox - 1, hWnd);
                    s++;
                }
                i++;
            }
        }
        void DrawBox(int x, int y, int w, int h, HDC hdc) {
            if (h < 0) {
                h *= -1;
            }
            if (w < 0) {
                w *= -1;
            }

            int i = 0;
            while (i < h) {
                MoveToEx(hdc, x, y + i, NULL);
                LineTo(hdc, x + w, y + i);

                i++;
            }

        }

    };

    tetris(HDC hdc) {
        refresh();
        bagp[cur].drawObject(hdc);
    }
    void next(HDC hdc) {
        bagp[cur].DrawBox(0, 0, 600, 600, hdc);
        bagp[cur].drawObject(hdc);
        cur++;

        if (cur > 6) {
            refresh();
        }
    }
    void turnPiece(bool clockwise) {
        bagp[cur].turnTet(clockwise);
    }
    void refresh() {
        srand(time(NULL));
        bag[0] = rand() % 7;

        int i = 1;
        while (i < 7) {
            bool open = false;
            cur = i;

            while (!open) {
                bag[i] = rand() % 7;
                int s = 1;
                open = true;
                while (s <= i) {
                    if (bag[i] == bag[i - s]) {
                        open = false;
                    }
                    s++;
                }
            }
            i++;
        }

        cur = 0;
        while (cur < 7) {
            switch (bag[cur]) {
            case 0:
                bagp[cur] = pieces::getObject('O');
                break;
            case 2:
                bagp[cur] = pieces::getObject('T');
                break;
            case 1:
                bagp[cur] = pieces::getObject('I');
                break;
            case 3:
                bagp[cur] = pieces::getObject('S');
                break;
            case 4:
                bagp[cur] = pieces::getObject('Z');
                break;
            case 5:
                bagp[cur] = pieces::getObject('L');
                break;
            case 6:
                bagp[cur] = pieces::getObject('J');
                break;
            }

            cur++;
        }
        cur = 0;
    }
    void paint(HDC hdc) {
        COLORREF temp = def;
        bagp[cur].setColor(hdc, temp);
        bagp[cur].DrawBox(0, 0, 600, 600, hdc);
        bagp[cur].drawObject(hdc);
    }
private:
    int bag[7];
    int cur;
    pieces bagp[7];
};

我不明白为什么 HDC 会像它那样作废。我再次怀疑它与 hdc 如何作为参数传递或者我如何保存 hdc 有关。帮助...请(并谢谢你)。

哦,老派 windows 编程...

这可能不是问题的全部,但在您的 setColor 函数中,您使用 CreatePen 创建了一个 Pen,但从未对其调用 DeleteObject。当 Windows 用完对象的资源句柄时,这将泄漏资源并最终导致问题。

此外,BeginPaint 返回的设备上下文(通常)仅在调用 EndPaint 之前有效(除非您在 CreateWindow 调用中另行指定,如果我没记错的话)。这可能是句柄泄漏的另一个来源。

在任务管理器的“进程”选项卡中,您可以添加 "GDI objects" 列,如果您泄漏句柄,该列将随着程序运行而增加。我曾经看到过这个和我以前写的东西。