C Windows не получает никаких сообщений

#c #windows #message

#c #Windows #Сообщение

Вопрос:

Я создаю простое окно, но когда я вижу создаваемое окно и закрываю его, сообщение WM_QUIT никогда не получено. Вот некоторый код:

 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
    cWindowApplication app(hInstance);

    const long width = 1024L;
    const long height = 768L;

    if (app.CreateWindowApplication(width, height) == false)
    {
        MessageBox(NULL, "Unable to create OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
        app.DestroyWindowApplication();
        return 1;
    }

    return app.MainLoop();
}
  

Вот функция CreateWindowApplication(int, int):

 bool cWindowApplication::CreateWindowApplication(long width, long height, bool full_screen /*= false*/)
{
    DWORD dwExStyle;    // Window Extended Style
    DWORD dwStyle;      // Window Style

    mWindowRect.left = 0L;          // Set Left Value To 0
    mWindowRect.right = width;      // Set Right Value To Requested Width
    mWindowRect.top = 0L;           // Set Top Value To 0
    mWindowRect.bottom = height;    // Set Bottom Value To Requested Height

    mFullScreen = full_screen;

    // fill out the window class structure
    const char* class_name      = "MyClass";
    mWindowClass.cbSize         = sizeof(WNDCLASSEX);
    mWindowClass.style          = CS_HREDRAW | CS_VREDRAW;
    mWindowClass.lpfnWndProc    = cWindowApplication::StaticWindowsProcessCallback;
    mWindowClass.cbClsExtra     = 0;
    mWindowClass.cbWndExtra     = 0;
    mWindowClass.hInstance      = mhInstance;
    mWindowClass.hIcon          = LoadIcon(NULL, IDI_APPLICATION);          // default icon
    mWindowClass.hCursor        = LoadCursor(NULL, IDC_ARROW);              // default arrow
    mWindowClass.hbrBackground  = NULL;                                     // don't need background
    mWindowClass.lpszMenuName   = NULL;                                     // no menu
    mWindowClass.lpszClassName  = class_name;
    mWindowClass.hIconSm        = LoadIcon(NULL, IDI_WINLOGO);              // windows logo small icon

    // register the windows class
    if (!RegisterClassEx(amp;mWindowClass))
    {
        return false;
    }

    if (mFullScreen == true) //If we are Full Screen, we need to change the display mode                             
    {
        DEVMODE dmScreenSettings;                           // device mode

        memset(amp;dmScreenSettings, 0, sizeof(dmScreenSettings));
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);

        dmScreenSettings.dmPelsWidth = width;               // screen width
        dmScreenSettings.dmPelsHeight = height;             // screen height
        dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL;     // bits per pixel
        dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        if (ChangeDisplaySettings(amp;dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
        {
            // setting display mode failed, switch to windowed
            MessageBox(NULL, "Display mode failed", NULL, MB_OK);
            mFullScreen = false;
        }
    }

    if (mFullScreen == true)                // Are We Still In Full Screen Mode?
    {
        dwExStyle = WS_EX_APPWINDOW;        // Window Extended Style
        dwStyle =   WS_POPUP;               // Windows Style
        //ShowCursor(false);                  // Hide Mouse Pointer
    }
    else
    {
        dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;   // Window Extended Style
        dwStyle = WS_OVERLAPPEDWINDOW;                    // Windows Style
    }

    AdjustWindowRectEx(amp;mWindowRect, dwStyle, false, dwExStyle);     // Adjust Window To True Requested Size

    // class registered, and create our window
    mHWND = CreateWindowEx(NULL,                            // extended style
        class_name,                                         // class name
        "My Windows",                           // application name
        dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
        0, 0,                                               // x,y coordinate
        mWindowRect.right - mWindowRect.left,
        mWindowRect.bottom - mWindowRect.top,               // width, height
        NULL,                                               // handle to parent
        NULL,                                               // handle to menu
        mhInstance,                                         // application instance
        this);                                              // this pointer to call member functions

    // check if window creation failed (hwnd would equal NULL)
    if (mHWND == false)
    {
        return false;
    }

    mHDC = GetDC(mHWND);

    ShowWindow(mHWND, SW_SHOW);          // display the window
    UpdateWindow(mHWND);                 // update the window
    return true;
}
  

По сути, после вызова этой функции функция CreateWindowEx() вызовет StaticWindowProcessCallback(), которая выглядит следующим образом:

 LRESULT cWindowApplication::StaticWindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    cWindowApplication* win_app = NULL;

    if (msg == WM_CREATE)
    {
        //Creation event
        //Get the pointer we pass during CreateWindowApplication() call
        win_app = (cWindowApplication*)((LPCREATESTRUCT)lParam)->lpCreateParams;

        //Associate window pointer with the hwnd for the other events to access
        SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)win_app);
    }
    else
    {
        //Non-creation event
        win_app = (cWindowApplication*)GetWindowLongPtr(wnd, GWLP_USERDATA);

        if (win_app != NULL)
        {
            return DefWindowProc(wnd, msg, wParam, lParam);
        }
    }

    //call member
    return win_app->WindowsProcessCallback(wnd, msg, wParam, lParam);
}
  

Наконец, последняя строка этой функции вызывает функцию-член WindowProcessCallback(), которая выглядит следующим образом:

 LRESULT cWindowApplication::WindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
    {
        mHDC = GetDC(wnd);
        SetupPixelFormat();
        //Set the version that we want, in this case 3.0
        int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 };  //zero indicates the end of the array
        //Create temporary context so we can get a pointer to the function
        HGLRC tmp_context = wglCreateContext(mHDC);
        //Make it current
        wglMakeCurrent(mHDC, tmp_context);

        PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
        wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");

        if (wglCreateContextAttribsARB == NULL)
        {
            //No OpenGL 3.0, back to 2.0
            mHGLRC = tmp_context;
        }
        else
        {
            //Create OpenGL 3.0
            mHGLRC = wglCreateContextAttribsARB(mHDC, 0, attribs);
            //Delete the temp context
            wglDeleteContext(tmp_context);
        }

        //Make OpenGL 3.0
        wglMakeCurrent(mHDC, mHGLRC);
        mIsRunning = true;
    }
        break;
    case WM_QUIT:
    case WM_DESTROY:
    case WM_CLOSE:
        wglMakeCurrent(mHDC, NULL);
        wglDeleteContext(mHGLRC);
        mIsRunning = false;
        PostQuitMessage(0); //Send a WM_QUIT message
        return 0;
    default:
        break;
    }

    return DefWindowProc(wnd, msg, wParam, lParam);
}
  

Как вы можете видеть, там есть некоторый код обработки сообщений… но, кроме WM_CREATE, никаких других случаев не происходит. После отправки сообщения WM_CREATE вызывается функция MainLoop(), которая выглядит следующим образом:

 int cWindowApplication::MainLoop()
{
    while (mIsRunning == true)
    {
        ProcessWindowsMessages();
    }

    DestroyWindowApplication();
    return 0;
}
  

По сути, функция ProcessWindowsMessages() не получает никаких сообщений после закрытия окна… Я должен нажать stop VS от запуска, чтобы остановить процесс. Функция ProcessWindowsMessages() выглядит следующим образом:

 void cWindowApplication::ProcessWindowsMessages()
{
    MSG msg;

    if (PeekMessage(amp;msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage(amp;msg);
        DispatchMessage(amp;msg);
    }
}
  

Комментарии:

1. Вероятно, потому, что вы должны были найти это с помощью отладчика, если бы вы знали, как он должен был себя вести. Когда вы вводите сторонний код, убедитесь, что вы его понимаете.

2. Если это так, то этот сайт не нужен.

3. Вы можете прочитать некоторые часто задаваемые вопросы. Этот сайт не предназначен для справки по отладке. Проблема с вашим вопросом заключается в том, что даже если у кого-то в будущем возникнет точно такая же проблема, он не сможет найти ваш вопрос и мое решение. (Не ваша вина, я тоже не могу придумать хороший заголовок или поисковые запросы)

4. Хорошо, я буду стараться изо всех сил. Это просто сложно, когда вы пытались отлаживать несколько дней подряд и все еще не знаете, что не так.

5. В любом случае, рад, что он теперь работает.

Ответ №1:

Эта логика StaticWindowsProcessCallback выглядит в обратном направлении:

     if (win_app != NULL)
    {
        return DefWindowProc(wnd, msg, wParam, lParam);
    }
  

Если у вас нет указателя на объект window wrapper, вам нужно вызвать DefWindowProc . Так что это должно произойти if (win_app == NULL) . Это делается для обработки нескольких сообщений, которые были отправлены ранее WM_CREATE . В результате этого ваш код имеет неопределенное поведение для сообщений, обработанных ранее WM_CREATE , и отбрасывает (применяя обработку по умолчанию) все сообщения после WM_CREATE .

Однако было бы еще лучше использовать WM_NCCREATE для настройки ссылки. Кроме того, win_app это не очень хорошее название для этого, может win_obj быть, или что-то в этом роде.

Вы также не должны обрабатывать WM_QUIT в своей оконной процедуре, поскольку она не отправляется в окно. И поведение по умолчанию WM_CLOSE должно быть в порядке, оно вызовет DestroyWindow , который сработает WM_DESTROY .

Но первый, неспособность пересылать какие-либо сообщения после WM_CREATE вашей оконной процедуры, вероятно, объясняет ваше отсутствие WM_QUIT в основном цикле сообщений.

Комментарии:

1. Спасибо, это все объясняет. Я просматривал код из другого источника, и у них был if (!win_app) … Я перевел это неправильно.