Дочерние окна не отображаются

0
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. Что не так?

Теги:
winapi

1 ответ

3
Лучший ответ
    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, вы просто использовали его как уведомление.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню