【问题标题】:WIN32GUI menu bar not rendering when text input window is added添加文本输入窗口时,WIN32GUI菜单栏不呈现
【发布时间】:2021-11-02 01:38:23
【问题描述】:

概要

当在下面的行中调用CreateWindowW 时,使用AppendMenu 实例化的GUI 菜单栏消失。

CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL, 200, 152, 100, 50, hWnd, NULL, NULL, NULL);

CreateWindowW 的调用会创建一个文本输入框。

以下两张图片显示了带有 (1) 和不带 (2) 注释掉上述代码行的 GUI 窗口。

GUI 不违反 CreateWindowW 调用:

带有违规 CreateWindowW 调用的 GUI:

代码

#include <Windows.h>
#include <sal.h>



constexpr auto FILE_DROPDOWN_ID = 1;
constexpr auto HELP_POPUP_ID = 2;
constexpr auto LOAD_DROPDOWN_ID = 3;
constexpr auto LOAD_TSD_NO_OFFSET = 4;
constexpr auto FILE_MENU_EXIT = 5;
constexpr auto CHANGE_TITLE = 6;

LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

void AddMenus(HWND);
void AddControls(HWND);
void AddMenusAndControls(HWND);

HMENU hMenu;

//int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    
    //MessageBox(NULL, L"Upconverter Start up", L"Please load your file", MB_OK);

    WNDCLASSW wc = { 0 };
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // Define window background
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hInstance = hInstance;
    wc.lpszClassName = L"myWindowClass";
    wc.lpfnWndProc = WindowProcedure;

    if (!RegisterClassW(&wc)) // Pass as reference because arg is type pointer
        return -1;

    CreateWindowW(L"myWindowClass", L"My Window Name", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 200, 500, 500,
        NULL, NULL, NULL, NULL);

    // Event driven loop

    MSG msg = { 0 }; 


    while (GetMessage(&msg, NULL, NULL, NULL)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }


    return 0;
}

LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg)
    {
    case WM_COMMAND: // Triggered for every button or menu item. Tells us that something has been clicked. 

        switch (wp)  // This switch statement governs the resultant behavior of a button press on the main window. 
        {
        //case FILE_DROPDOWN_ID:
            //MessageBeep(MB_DEFBUTTON1);
        case HELP_POPUP_ID:
            MessageBeep(MB_DEFBUTTON2);
        case FILE_MENU_EXIT:
            DestroyWindow(hWnd);
        case LOAD_DROPDOWN_ID:
            MessageBeep(MB_ICONINFORMATION);
            break;
        case CHANGE_TITLE:
            
            break;
        }

    case WM_CREATE:
        //AddControls(hWnd);
        //AddMenus(hWnd);
        AddMenusAndControls(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefMDIChildProcW(hWnd, msg, wp, lp);
    }
}

void AddMenusAndControls(HWND hWnd) {

    hMenu = CreateMenu();
    HMENU hFileMenu = CreateMenu();
    HMENU hSubMenu = CreateMenu();

    AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, L"File");
        AppendMenu(hFileMenu, MF_STRING, LOAD_DROPDOWN_ID, L"New");
        AppendMenu(hFileMenu, MF_POPUP, (UINT_PTR)hSubMenu, L"Open Submenu");
            AppendMenu(hSubMenu, MF_STRING, CHANGE_TITLE, L"Change Title");
        AppendMenu(hFileMenu, MF_SEPARATOR, NULL, NULL);
        AppendMenu(hFileMenu, MF_STRING, FILE_MENU_EXIT, L"Exit");
    
    AppendMenu(hMenu, MF_STRING, HELP_POPUP_ID, L"Help");
    

    CreateWindowW(L"static", L"Enter text here: ", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 50, hWnd, NULL, NULL, NULL);
    CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL, 200, 152, 100, 50, hWnd, NULL, NULL, NULL); // WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL

    SetMenu(hWnd, hMenu);
}

void AddMenus(HWND hWnd) {

    hMenu = CreateMenu();
    HMENU hFileMenu = CreateMenu();
    HMENU hSubMenu = CreateMenu();

    AppendMenu(hSubMenu, MF_STRING, CHANGE_TITLE, L"Change Title");


    AppendMenu(hFileMenu, MF_STRING, LOAD_DROPDOWN_ID, L"New");
    AppendMenu(hFileMenu, MF_POPUP, (UINT_PTR)hSubMenu, L"Open Submenu");
    AppendMenu(hFileMenu, MF_SEPARATOR, NULL, NULL);
    AppendMenu(hFileMenu, MF_STRING, FILE_MENU_EXIT, L"Exit");

    AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, L"File");
    AppendMenu(hMenu, MF_STRING, HELP_POPUP_ID, L"Help");


    SetMenu(hWnd, hMenu);

}

void AddControls(HWND hWnd) {
    CreateWindowW(L"static", L"Enter text here: ", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 50, hWnd, NULL, NULL, NULL);
    CreateWindowW(L"Edit", L"...", WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL, 200, 152, 100, 50, hWnd,
        NULL, NULL, NULL); // WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL
}

讨论

我怀疑这与窗口句柄 hWnd 允许覆盖窗口内容有关。所以我改变了之前两次通过hWnd

    case WM_CREATE:
        AddControls(hWnd);
        AddMenus(hWnd);

到单个函数调用。

case WM_CREATE:
        AddMenusAndControls(hWnd);

但这没有任何效果。它也可能与SetMenu 有关,但我不确定。我尝试将SetMenu 调用移动到AddMenusAndControls 中的不同行,这也没有效果。

这是怎么回事?

感谢您的帮助。

【问题讨论】:

  • 通常当使用WS_CHILD 和父 hWnd 调用CreateWindow 时,您应该提供一个子 ID。确定您需要子 ID 或从 CreateWindow 返回的 hWnd 用于编辑框,否则您将无法控制它。
  • 另外,您是从哪里想到为非 MDI 子窗口调用 DefMDIChildProcW 的?
  • 你不能因为DefMDIChildProcW 而责怪教程,因为我看到它使用DefWindowProcW。但是,我强烈建议您为自己准备一个更好的教程,因为它确实会犯返回失败的错误。 Youtube 视频通常是一种学习编程的糟糕方式,因为它们只支持一种嵌入式媒体:视频。没有源代码、正确的图表等,你根本不可能有一个编程教程。
  • 微软的在线文档现在很垃圾。如果你能得到一本好书来学习 Win32,你会更好。
  • 我不知道有多少次报告了关于预处理器宏的所有文档都已损坏。它似乎被置若罔闻。让 .NET 人员就如何最好地为原生平台 API 创建和交付文档发表意见是一个糟糕的决定。

标签: c++ windows user-interface winapi win32gui


【解决方案1】:

您的窗口程序严重损坏。它有一个非 void 返回类型,但在所有路径上都没有返回任何东西,除了default:

窗口过程的正确结构是在函数结束时无条件地调用默认窗口过程。任何不应传递给默认窗口过程的消息都需要通过return(并指定返回值)而不是break退出其case

改变

case WM_DESTROY:
    PostQuitMessage(0);
    break;
default:
    return DefMDIChildProcW(hWnd, msg, wp, lp);
}

case WM_DESTROY:
    PostQuitMessage(0);
    break;
}
return DefWindowProcW(hWnd, msg, wp, lp);

我还修正了您对要调用的 哪个 默认窗口过程的选择,因为您的 WinMain 似乎将此窗口用作顶级框架,而不是 MDI 子级。

因为 MDI 子级在焦点更改时会自动将其菜单迁移到 MDI 父级,并且因为您正在添加一个能够接收焦点的新子控件(在框架拥有它之前),因此很可能与焦点相关发送到窗口的消息会发生变化,并且由于您尽职尽责地转发了它们,因此您得到了您要求的 MDI 菜单行为(即使您不想要它)。如果您没有 MDI,请不要使用 DefMDIChildProcW

【讨论】:

  • 谢谢 这很有帮助,但不幸的是,在我将 return DefMDIChildProcW(hWnd, msg, wp, lp); 添加到 case WM_create: 的末尾后问题仍然存在
  • @MusilMark:不要尝试一次修复一个案例,在没有return 语句的情况下到达非无效函数末尾的 any 消息是即时未定义的行为。
  • 它仍然显示相同的行为。但这给了我一个很好的起点。我确信还有其他事情需要我修补以达到更好的行为。谢谢
  • 现在可以使用了,谢谢。对于以后阅读本文的任何人,请确保添加中断案例,不要依赖失败来达到return DefWindowProcW(hWnd, msg, wp, lp)
  • @MusilMark:失败不会进入default: 块,它会进入源代码中紧随其后的任何内容。您的HELP_POPUP_ID 案例正在调用DestroyWindow
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-11
  • 2017-12-25
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-24
相关资源
最近更新 更多