基本窗口创建

时间:2018-06-02 20:44:20

标签: windows winapi input message-queue

我在处理Windows窗口时遇到问题,即使我以前用过这种方式也是如此,它工作正常。在阅读了针对此问题的最常见建议后,它仍然存在。有人能告诉我为什么输入处理被打破了吗?

预期行为:

  1. 创建一个标题为' FirstTry'
  2. 的窗口
  3. 使用PatBlt
  4. 使背景变黑
  5. 在第一次输入主循环时,在按下' w'后显示一个消息框。
  6. 按Alt + F4,Escape或关闭按钮关闭窗口,显示关闭消息。
  7. 观察到的行为:

    1. 按预期
    2. 按预期
    3. MessageBox第一次显示,但无法使用' w'
    4. 进行重新搜索
    5. 窗口不可关闭,除了使用TaskManager(有一次它显示'关闭应用程序' -MessageBox按预期,但只有一次)
      • 窗口可拖动,直到第一个'进入循环' -MessageBox关闭,之后其固定
      • 小蓝&繁忙' -bircle of windows10在第一个MessageBox之后全职显示
    6. 结论:消息处理已损坏。 我无法弄清楚为什么......

      系统:

      • Windows 10,版本1803(Build 17134.81),64位

      VS 2017社区版的编译器:

      • vcvarsall.bat amd64

      • cl -MTd -nologo -FC -Zi -W4 -WX -wd4100 -wd4312 FirstTry.cpp / link User32.lib Gdi32.lib

      #include "windows.h"
      
      static bool bAppIsRunning = false;
      static bool bMessageAlreadyShown = false;
      
      LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam){
          LRESULT result = 0;
          switch(msg){
              case WM_SYSKEYDOWN:
              case WM_SYSKEYUP:
              case WM_KEYDOWN:
              case WM_KEYUP:{
                  WPARAM vKeyCode = wParam;
                  bool bWasDown = ((lParam & (1 << 30)) != 0);
                  bool bIsDown = ((lParam & (1 << 31)) == 0);
                  if (bWasDown != bIsDown)
                  {
                      switch (vKeyCode)
                      {
                          case VK_ESCAPE:{
                              bAppIsRunning = false;
                          }break;
                          default:{
                              result = DefWindowProc(wnd,msg,wParam,lParam);
                          }break;
                      }
                  }
              }break;
              default:{
                  result = DefWindowProc(wnd,msg,wParam,lParam);
              }break;
          }
      
          return result;
      }
      
      int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow){
      
          WNDCLASSA wndCLass = {};
          wndCLass.style = CS_HREDRAW | CS_VREDRAW;
          wndCLass.lpfnWndProc = win_MainWNDCallback;
          wndCLass.hInstance = HInstance;
          wndCLass.lpszClassName = (LPCSTR)"WindowClass";
      
          if(RegisterClassA(&wndCLass)){
      
              HWND wnd = CreateWindowExA(
                  0, wndCLass.lpszClassName, (LPCSTR)"FirstTry", 
                  WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
                  CW_USEDEFAULT, CW_USEDEFAULT, 
                  1240, 720,
                  0, 0, HInstance, 0);
      
              if(wnd){
                  bAppIsRunning = true;
      
                  HDC DeviceContext = GetDC(wnd);
                  PatBlt(DeviceContext, 0, 0, 1240, 720, BLACKNESS);
                  ReleaseDC(wnd, DeviceContext);
      
                  while(bAppIsRunning){
      
                      if(!bMessageAlreadyShown){
                          MessageBoxA(NULL, (LPCSTR)"Successfully entered loop.", (LPCSTR)"Success!", MB_ICONINFORMATION | MB_OK);
                          bMessageAlreadyShown = true;
                      }
      
                      MSG msg;
                      while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)){
                          switch(msg.message){
                              case WM_SYSKEYDOWN:
                              case WM_SYSKEYUP:
                              case WM_KEYDOWN:
                              case WM_KEYUP:{
                                  WPARAM vKeyCode = msg.wParam;
                                  bool bWasDown = ((msg.lParam & (1<<30)) != 0);
                                  bool bIsDown = ((msg.lParam & (1<<31)) != 0);
                                  if(bIsDown != bWasDown){
                                      switch(vKeyCode){
                                          case 'W':{
                                              bMessageAlreadyShown = false;
                                          }break;
                                          default:{
                                              TranslateMessage(&msg);
                                              DispatchMessageA(&msg);
                                          }break;
                                      }
                                  }
                              }
                          }
                      }
                  }
                  MessageBoxA(NULL, (LPCSTR)"Closing Application.", (LPCSTR)"Bye bye!", MB_ICONINFORMATION | MB_OK);
              }
          }
          return ERROR_SUCCESS;
      }
      

1 个答案:

答案 0 :(得分:1)

您的代码的主要问题是,只有在收到某些按键消息时才会调用TranslateMessage()DispatchMessage()。您需要在主消息循环中为所有消息调用它们。你应该处理你的WndProc回调中的所有消息。

您还在使用基于TCHAR的API,但却误用了LPCTSTR类型转换。在将字符串/字符文字转换为TEXT()时,您需要使用TCHAR宏。

尝试更像这样的东西:

#include <windows.h>

static bool bMessageAlreadyShown = false; 

LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        case WM_SYSKEYDOWN:
        case WM_SYSKEYUP:
        case WM_KEYDOWN:
        case WM_KEYUP: {
            WPARAM vKeyCode = wParam;
            bool bWasDown = ((lParam & (1 << 30)) != 0);
            bool bIsDown = ((lParam & (1 << 31)) == 0);
            if (bWasDown != bIsDown) {
                switch (vKeyCode) {
                    case 'W':
                    case VK_ESCAPE:
                        DestroyWindow(wnd);
                        return 0;
                }
            }
            break;
        }

        case WM_ERASEBKGND:
            PatBlt((HDC)wParam, 0, 0, 1240, 720, BLACKNESS);
            return 0;
    }

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

int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow) {
    WNDCLASS wndCLass = {};
    wndCLass.style = CS_HREDRAW | CS_VREDRAW;
    wndCLass.lpfnWndProc = win_MainWNDCallback;
    wndCLass.hInstance = HInstance;
    wndCLass.lpszClassName = TEXT("WindowClass");

    if (RegisterClass(&wndCLass)) {
        HWND wnd = CreateWindowEx( 0, wndCLass.lpszClassName, TEXT("FirstTry"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1240, 720, 0, 0, HInstance, 0);
        if (wnd) {
            MSG msg;
            while (GetMessage(&msg, 0, 0, 0)) {
                if (!bMessageAlreadyShown) {
                    bMessageAlreadyShown = true;
                    MessageBox(NULL, TEXT("Successfully entered loop."), TEXT("Success!"), MB_ICONINFORMATION | MB_OK);
                }
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    MessageBox(NULL, TEXT("Closing Application."), TEXT("Bye bye!"), MB_ICONINFORMATION | MB_OK);

    return ERROR_SUCCESS;
}

请注意,我删除了您的bAppIsRunning变量,因为一旦消息循环处理了WM_QUIT消息,它就变得多余了。

我还删除了对 ALT-F4 的处理,因为操作系统会自动为您处理。它关闭窗口,触发WM_CLOSE消息。默认情况下,DefWindowProc()会通过销毁窗口来处理WM_CLOSE,从而触发WM_DESTROY消息。

我还为WM_ERASEBKGND添加了处理以在窗口上绘制背景。从消息循环外部绘制是错误的。只要需要在屏幕上刷新窗口,您执行的任何绘图都会丢失,因此您必须重绘所有内容以响应WM_ERASEBKGNDWM_PAINT