ATOM MyRegisterChildClass(void)
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = ChildProc;
wcex.hInstance = hInst;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 8);
wcex.lpszClassName = ChildClassName;
return RegisterClassEx(&wcex);
}
static HFONT newFont;
static HWND hChild[9];
unsigned char k[9] = {0};
char text[] = {' ', 'X', '0'};
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
static int sx, sy;
switch (message)
{
case WM_CREATE:
MyRegisterChildClass();
for(i = 0; i < 9; i++)
hChild[i] = CreateWindow(ChildClassName, NULL, WS_CHILD |
WS_DLGFRAME | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
break;
case WM_SIZE:
if(wParam == SIZE_MINIMIZED)
break;
sx = LOWORD(lParam)/3;
sy = HIWORD(lParam)/3;
if(newFont)
DeleteObject(newFont);
newFont = CreateFont(min(sx, sy), 0, 0, 0, FW_NORMAL, 0, 0, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
for(i = 0; i < 9; i++)
{
MoveWindow(hChild[i], (i%3)*sx, (i/3)*sy, sx, sy, TRUE);
UpdateWindow(hChild[i]);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_NEW:
for(i = 0; i < 9; i++)
{
k[i] = 0;
InvalidateRect(hChild[i], NULL, TRUE);
UpdateWindow(hChild[i]);
}
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
RECT rt;
int s, i;
char *ch;
switch(message)
{
case WM_LBUTTONDOWN:
for(i = 0; hWnd != hChild[i]; i++);
if(k[i])
break;
else
k[i] = 1;
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
srand(lParam);
for(i = s = 0; i < 9; i++)
if(k[i])
s++;
if(s == 9)
MessageBox(hWnd, L"...",L"...", MB_OK|MB_ICONQUESTION);
else
{
while(true)
{
s = rand()*9/(RAND_MAX+1);
if(k[s])
continue;
k[s] = 2;
InvalidateRect(hChild[s], NULL, TRUE);
UpdateWindow(hChild[s]);
break;
}
}
break;
case WM_PAINT:
for(i = 0; hWnd != hChild[i]; i++);
if(k[i])
{
ch = text+k[i];
hDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);
SelectObject(hDC, newFont);
DrawTextA(hDC, ch, 1, &rt, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps);
}
else
DefWindowProc(hWnd, message, wParam, lParam);
break;
default:
DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Я пытаюсь сделать игру с тик-таком. Вот код, который я написал вручную (здесь нет стандартного кода, который генерируется Visual Studio). Я сделал 9 дочерних окон. Этот код работает, но он не показывает дочерние окна и не реагирует, когда я нажимаю левую кнопку мыши. С помощью отладчика я видел, что сообщения WM_LBUTTONDOWN и WM_PAINT никогда не отправляются в функцию ChildProc. Что не так?
for(i = 0; i < 9; i++)
hChild[i] = CreateWindow(ChildClassName, NULL, WS_CHILD |
WS_DLGFRAME | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
Вы делаете традиционную ошибку, вы слепо надеетесь, что функции winapi преуспеют. Эта неудачная надежда, CreateWindow() терпит неудачу и возвращает NULL. Что-то, что вы также можете увидеть с помощью отладчика, вы увидите, что массив hChild содержит только нулевые значения. Всегда пишите защитный код, при абсолютном минимальном использовании assert() для резервного копирования ваших предположений:
for(i = 0; i < 9; i++) {
hChild[i] = CreateWindow(ChildClassName, ...);
assert(hChild[i]);
}
Множество других мест в вашем коде, где вы должны это делать. Теперь вам придется нелегко диагностировать провал.
Фактическая ошибка находится в ChildProc():
default:
DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
Или, другими словами, он всегда возвращает 0. Это не нормально, вы должны вернуть это значение только при обработке сообщения. Вы не обрабатывали каждое сообщение. Снова используйте отладчик и установите контрольную точку в инструкции switch() в ChildProc. Вы увидите, что первое сообщение было неправильно обработано, WM_NCCREATE. Для этого требуется, чтобы вы вернули TRUE, чтобы продолжить создание окна. Вы этого не сделаете, вы вернете ЛОЖЬ. Так что быстрый конец создания окна. Вы должны вернуть значение DefWindowProc(). Fix:
default:
return DefWindowProc(hWnd, message, wParam, lParam);
Помните, что вы также играете в опасную игру с сообщением WM_CREATE в своем WndProc(). Возврат 0 работает случайно, вы всегда должны передавать сообщение DefWindowProc(), так как вы фактически не обрабатывали WM_CREATE, вы просто использовали его как уведомление.