C++ Win32: HDC и отладка
Я пишу код для того, что должно быть игрой в тетрис. Это рано, и сейчас он показывает только один кусок (кусок, который будет "падать" в этот момент), и это все, что он должен делать. Стрелка вверх позволяет циклически перемещаться вперед (в частности, только) по случайно сгенерированной последовательности фрагментов (используя метод "bag"). И, используя стрелки влево и вправо, вы можете вращать части. В любом случае, это кодируется на платформе win32, и я обнаружил, что после определенного количества кадров (с WM_PAINT) основной HDC обнуляется, и все останавливается. Чтобы достичь этого, требуется примерно вдвое больше кадров, удерживающих клавиши со стрелками вправо или влево, чем при удерживании клавиши со стрелкой вверх. Что-то странное в том, что после примерно 1000 кадров область консоли, которую я активно закрашиваю (рамка 600x600 px), станет черной (hdc еще не был обнулен), и только когда нажата стрелка вверх, HDC аннулируется. Я подозреваю, что проблема в том, что методы, вызываемые при нажатии клавиши со стрелкой вверх, передают hdc в качестве параметра для методов в классе (скажите, если это плохая практика или я должен что-то делать вместо этого). Я думаю, что HDC может быть портит или что-то (честно говоря, я понятия не имею). Клавиши со стрелками влево и вправо не вызывают напрямую методы с HDC в качестве параметра. Из-за меток дел и того, как спроектирован шаблон win32, я должен хранить HDC, область которого выпадает из любой метки дела, чтобы я мог получить доступ к дескриптору окна вне случая рисования (я чувствую, что это плохая практика, но я хотел бы понять, почему, прежде чем я изо всех сил, чтобы найти новый путь). Я опубликую код, сохраненный HDC называется main HDC, и он определен непосредственно перед основным оператором case. Чтобы пояснить вам, я дам обзор структуры кода:
Основной файл.cpp, который содержит базовую программу win32, вызывает конструктор класса Tetris в WM_PAINT и сохраняет его повсеместно, подобно main HDC, и, при запросе, методу "next" (который приносит следующий фрагмент), "turnPiece" метод (который вращает фигуру по часовой стрелке или против часовой стрелки в зависимости от параметра) и метод "рисования", который обновляет экран, перерисовывая текущую фигуру. В классе tetris (который находится в его собственном заголовочном файле) есть подкласс, называемый "части", который содержит информацию о его объектах, которые определены другим уровнем подклассов, названных с единственным символом, изображенным частями форма. Форма, цвет и размер фрагментов хранятся в двумерном массиве указателей (используя COLORREF). "Pieces" содержит свой собственный метод "DrawObject", который рисует вызывающий его объект (который, как и все методы рисования / рисования, имеет HDC в качестве аргумента). Есть также метод, который вращает фигуру, называемую "turnTet" (метод "turnPiece" переводит вызов из основного файла.cpp в "куски"). Единственными другими применимыми методами являются те, которые встречаются в классе "tetris", это "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;
}
}
Заголовочный файл, который содержит классы Tetris и Piece
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. Помогите... пожалуйста (и спасибо).
1 ответ
Ооо, окна старой школы программирования...
Это может быть не полная проблема, но в вашем setColor
Функция вы создаете ручку с CreatePen
но никогда не звонит DeleteObject
в теме. Это приведет к утечке ресурса и, в конечном итоге, к возникновению проблем, когда в Windows не хватает дескрипторов ресурсов для объектов.
Кроме того, контекст устройства, возвращаемый BeginPaint, (как правило) действителен только до вызова EndPaint (если вы не укажете иначе в вызове CreateWindow, если я правильно помню). Это может быть еще одним источником утечки ручки.
На вкладке "Процесс" диспетчера задач вы можете добавить столбец "Объекты GDI", который будет увеличиваться по мере выполнения вашей программы, если у вас есть утечка дескрипторов. Я видел это однажды с чем-то, что я написал, когда.