【问题标题】:C++ Win32: HDC's and debuggingC++ Win32:HDC 和调试
【发布时间】:2015-12-29 02:41:00
【问题描述】:

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

包含基本 win32 程序的主 .cpp 文件调用 WM_PAINT 中的俄罗斯方块类构造函数,并像 mainHDC 一样普遍存储它,当提示时,“next”方法(带来下一个片段), “turnPiece”方法(根据参数顺时针或逆时针旋转棋子)和“paint”方法更新屏幕,重新绘制当前棋子。在俄罗斯方块类(在它自己的头文件中)中,有一个名为“pieces”的子类,其中包含有关其对象的信息,这些信息由另一个级别的子类定义,这些子类以碎片所描绘的单个字符命名形状。碎片的形状、颜色和大小存储在一个二维指针数组中(使用 COLORREF)。 “Pieces”包含自己的“DrawObject”方法,该方法绘制调用它的对象(与所有绘图/绘画方法一样,都有一个 HDC 作为参数)。还有一种旋转形状的方法称为“turnTet”(“turnPiece”方法将调用从主 .cpp 文件中继到“pieces”)。其他唯一适用的方法是在“俄罗斯方块”类中找到的方法,它们是“绘制”和“下一步”(它们最终绘制对象)。 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;
    }
}

头文件,包含俄罗斯方块和片断类

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 有关。帮助...请(谢谢)。

【问题讨论】:

  • 不相关的文字过多。您的问题与 俄罗斯方块 无关。请以最简洁的方式陈述您的问题。

标签: c++ winapi parameters null gdi


【解决方案1】:

哦,老派的 Windows 编程......

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

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

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

【讨论】:

  • 任务管理器中的列称为GDI 对象Handles 列指的是内核对象,而不是 GDI 对象。
  • Thanks 似乎(到目前为止)有效,而且还没有那么老。这不像是fortran。啧啧,傲慢的程序员。强大的 1201ProgramAlarm 有什么建议?
  • @IInspectable 已修复;我失去了(越来越不可信)的记忆。
  • @jacob "Old school" 就像使用 Windows 过程和所有样板代码来处理消息传递,而不是某种隐藏所有细节的类框架。
猜你喜欢
  • 1970-01-01
  • 2021-07-28
  • 2020-02-17
  • 2013-07-26
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
相关资源
最近更新 更多