c++ - SDL2 + Win32 API 菜单栏单击事件不起作用

标签 c++ winapi emulation sdl-2 menubar

我目前正在 Visual C/C++ 中使用 SDL2 + windows.h 库开发 GameBoy/GameBoyColor 模拟器以获取附加功能。我已成功将菜单项附加到我的 SDL 创建的窗口中,但是在尝试处理单击菜单项时窗口发送的消息时遇到了麻烦... Image of running application with window menu items

在我的设计中,我决定创建一个单独的源文件,其命名空间包含各种程序实用程序(例如窗口管理功能)。因此,我创建了一个名为 getMenuEvent() 的函数,它由窗口实例的消息循环组成,从技术上讲,它应该在同一命名空间中调用我定义的 WndProc() 函数查看 wParam 的内容是什么...

这是我编写的实用程序命名空间源文件

HWND getSDLWinHandle(SDL_Window* win)
{
    SDL_SysWMinfo infoWindow;
    SDL_VERSION(&infoWindow.version);
    if (!SDL_GetWindowWMInfo(win, &infoWindow))
    {
        std::cout << "test";
        return NULL;
    }
    return (infoWindow.info.win.window);
}

//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
    hMenuBar = CreateMenu();
    hFile = CreateMenu();
    hEdit = CreateMenu();
    hHelp = CreateMenu();

    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");

    AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
    AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");

    AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");

    AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");

    SetMenu(windowRef, hMenuBar);
}

void getMenuEvent(MSG* message, HWND hwnd)
{
    if (GetMessage(message, hwnd, 0, 0))
    {
        TranslateMessage(message);
        DispatchMessage(message);
    }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
    std::cout << "test" << std::endl;
    switch (message)
    {
    case WM_COMMAND:
        if (LOWORD(wparam) == ID_EXIT)
        {
            exit(0);
        }
        break;
    }
}

这是我的主要内容:

int main(int argc, char** argv)
{
    int test = 0;
    const char* title = "GBemu";
    bool isRunning = true;
    SDL_Event mainEvent;
    HWND windowHandler;
    MSG menuHandler;

    //Initialize the SDL Subsystems
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
    {
        SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
        return 1;
    }

    //Create a window pointer + allocate a window object to it.
    SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
    windowHandler = utilities::getSDLWinHandle(mainWindow);

    //Activate the menu bar of the window
    utilities::ActivateMenu(windowHandler);

    SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
    SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
    SDL_RenderPresent(mainRenderer);

    while (isRunning)
    {
        SDL_PollEvent(&mainEvent);
        switch (mainEvent.type)
        {
        case SDL_WINDOWEVENT_CLOSE:
            mainEvent.type = SDL_QUIT;
            SDL_PushEvent(&mainEvent);
            break;
        case WM_COMMAND:

        case SDL_QUIT:
            isRunning = false;
            break;
        };

        utilities::getMenuEvent(&menuHandler,windowHandler);
    }


    return 0;
}

目前,菜单栏会弹出并显示子菜单项(例如"file"->“加载 ROM”),但当我单击“退出”等子菜单项时没有任何反应。 如何让我的程序调用我在实用程序命名空间中定义的 WndProc() 函数,以便处理来自菜单项单击的消息?我的方法可以吗?

附注我从多个在线资源中了解到 DispatchMessage() 函数会自动调用 WndProc() 函数,但在我的情况下似乎没有发生。

最佳答案

找到了一个简单的修复方法,它也清理了很多代码!

事实证明,您可以使用 SDL 的事件处理系统来检索 winAPI 消息。

这是编辑后的主函数:

const char* title = "GBemu";
bool isRunning = true;
SDL_Event mainEvent;
HWND windowHandler;

//Initialize the SDL Subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
    SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
    return 1;
}

//Create a window pointer + allocate a window object to it.
SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
windowHandler = utilities::getSDLWinHandle(mainWindow);

//Activate the menu bar of the window
utilities::ActivateMenu(windowHandler);

//Initialize Rendering Device
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderPresent(mainRenderer);

//Enable WinAPI Events Processing
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);

while (isRunning)
{
    SDL_PollEvent(&mainEvent);
    switch (mainEvent.type)
    {
    case SDL_WINDOWEVENT_CLOSE:
        mainEvent.type = SDL_QUIT;
        SDL_PushEvent(&mainEvent);
        break;
    case SDL_SYSWMEVENT:
        if (mainEvent.syswm.msg->msg.win.msg == WM_COMMAND)
        {
            if (LOWORD(mainEvent.syswm.msg->msg.win.wParam) == ID_EXIT)
            {
                isRunning = false;
            }
        }
        break;
    case SDL_QUIT:
        isRunning = false;
        break;
    };
}

return 0;

这是编辑后的命名空间实用程序源文件:

//Namespace variables/Defines
#define ID_LOADROM 1
#define ID_ABOUT 2
#define ID_EXIT 3
#define ID_CONTROLS 4
static HMENU hHelp;
static HMENU hEdit;
static HMENU hFile;
static HMENU hMenuBar;


//Function which retrieves the address/Handle of an SDL window
//Also retrieves the specific subsystem used by SDL to create that window which is platform specific (Windows, MAC OS x, IOS, etc...)
HWND getSDLWinHandle(SDL_Window* win)
{
    SDL_SysWMinfo infoWindow;
    SDL_VERSION(&infoWindow.version);
    if (!SDL_GetWindowWMInfo(win, &infoWindow))
    {
        return NULL;
    }
    return (infoWindow.info.win.window);
}

//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
    hMenuBar = CreateMenu();
    hFile = CreateMenu();
    hEdit = CreateMenu();
    hHelp = CreateMenu();

    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
    AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");

    AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
    AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");

    AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");

    AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");

    SetMenu(windowRef, hMenuBar);
}

为了让 SDL 检索这些消息,需要执行两个重要步骤:

  1. 在事件检查之前必须调用 SDL_EventState()。这是因为为了提高可移植性,SDL 默认情况下在检查事件时会忽略 native API 消息。

  2. 对 SDL 创建的窗口句柄的引用必须保存在代码中方便的地方,以便向其附加 WinAPI 菜单。这可以通过调用 SDL_GetWindowWMInfo() 函数轻松完成,该函数将返回一个具有 HWND 窗口句柄数据成员的 SDL_SysWMInfo 对象。

完成此操作后,当用户单击"file"菜单中的“退出”选项时,程序将成功退出。

希望这会对计划做类似事情的其他人有所帮助!

关于c++ - SDL2 + Win32 API 菜单栏单击事件不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51250046/

相关文章:

c++ - 字符串初始值设定项和只读部分

c++ - "Body requirements not met"将请求传递给 c++ 中的方法(Boost Beast 库)

c++ - C++ 中的 L 前缀究竟是什么?

c++ - 在 Win32 API ListView 中添加带换行符的文本

c++ - 访问冲突 C++(删除 vector 中的项目)

android studio模拟器加载但不工作

c++ - curl_easy_perform() 是同步的还是异步的?

C++ WINAPI 窗口不刷新

android - 尝试在 Android Studio 中运行模拟器时出错

javascript - 单击按钮时的 jQuery 正斜杠