質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

Win32APIでメニューバーを途中で違うものに替えたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 747

_volatile

score 11

前提・実現したいこと

C++/Win32APIで
リソースに複数のメニューバーを用意し、何かしらのイベントでウィンドウのメニューバーを切り替えられるようにしたいです。
例として、左クリックした場合は日本語のメニューバー、右クリックした場合は英語のメニューバーに切り替わるようにします。

発生している問題・エラーメッセージ

関数SetClassLongPtrが上手く機能せず、メニューバーが切り替わりません。

該当のソースコード

// winmain.cpp
#define UNICODE
#define _UNICODE
#define MAX_LOADSTRING 100

#include <tchar.h>
#include <windows.h>
#include "resource.h"

namespace
{
// Global Variables:
HINSTANCE hInst;                     // current instance
TCHAR szTitle[MAX_LOADSTRING];       // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
} // namespace

// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
bool anotherInstance();

int WINAPI _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR lpCmdLine,
                     int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_APP, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    MSG msg;
    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return static_cast<int>(msg.wParam);
}

//  FUNCTION: MyRegisterClass()
//  PURPOSE: Registers the window class.
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = nullptr;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MENU_JPN);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = nullptr;

    return RegisterClassEx(&wcex);
}

//   FUNCTION: InitInstance(HINSTANCE, int)
//   PURPOSE: Saves instance handle and creates main window
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//  PURPOSE: Processes messages for the main window.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        case IDM_ABOUT:
            MessageBox(nullptr, TEXT("Version x.x.x"), TEXT(""), MB_OK);
            break;
        }
    break;
    case WM_LBUTTONDOWN:
        SetClassLongPtr(hWnd, GCLP_MENUNAME, (LONG_PTR)IDC_MENU_JPN); //日本語用のメニューバーにする
        break;
    case WM_RBUTTONDOWN:
        SetClassLongPtr(hWnd, GCLP_MENUNAME, (LONG_PTR) nullptr);  //Set English Menubar
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
//resource.h
#define IDS_APP_TITLE 100
#define IDC_APP 101

#define IDC_MENU_JPN 110
#define IDC_MENU_ENG 111

#define IDM_EXIT 120
#define IDM_ABOUT 121
//resource.rc
#pragma code_page(65001)
#include "resource.h"

STRINGTABLE
BEGIN
    IDS_APP_TITLE   "Sample Windows Application"
    IDC_APP         "SAMPLE"
END

IDC_MENU_JPN MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN  
        MENUITEM "終了(&X)", IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "情報(&A)", IDM_ABOUT
    END
END

IDC_MENU_ENG MENU
BEGIN
    POPUP "&File"
    BEGIN  
        MENUITEM "E&xit", IDM_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About", IDM_ABOUT
    END
END

試したこと

コンパイルは

windres resource.rc resource.o
clang++ winmain.cpp resource.o -Wall -std=c++17 -mwindows -municode


SetClassLongPtr関数の実引数を色々書き換えましたが、コンパイルエラー、あるいは何も起きないです…。
どのようにすれば実現できるか分かる方助言をお願いしますm(__)m

補足情報(FW/ツールのバージョンなど)

Windows10

//clang -v
clang version 7.0.1 (tags/RELEASE_701/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\msys64\mingw64\bin
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

メニューを動的に変更するには SetMenu APIを使います。流れ的には、LoadMenuでメニューをロードしておき、SetMenuでセット。必要無くなったらDestroyMenuで破棄、です。

ウィンドウプロシージャにおいて、WM_CREATEメッセージ受信時にLoadMenuしておいて、何らかのイベントとする為にマウス左ボタンクリックであるWM_LBUTTONDOWN時にSetMenuでメニューを切り替え、WM_DESTROY時に破棄する例です。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static bool fToggle = false;
    static HMENU hMenuEng = NULL;
    static HMENU hMenuJpn = NULL;

    switch (message)
    {
    case WM_CREATE:
    {
        // あらかじめ必要なメニューをロードしておく
        hMenuJpn = LoadMenu(hInst, (LPCTSTR)MAKEINTRESOURCE(IDC_MENU_JPN));
        hMenuEng = LoadMenu(hInst, (LPCTSTR)MAKEINTRESOURCE(IDC_MENU_ENG));
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    break;

    case WM_LBUTTONDOWN:
    {
        // マウス左クリックで交互に入れ替えるテスト
        fToggle = !fToggle;     // フラグ反転
        HMENU hMenu = fToggle ? hMenuEng : hMenuJpn;
        SetMenu(hWnd, hMenu);
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    break;

    case WM_DESTROY:
    {
        // Window破棄時にメニューも破棄
        if (hMenuJpn != NULL) {
            DestroyMenu(hMenuJpn);
        }
        if (hMenuEng != NULL) {
            DestroyMenu(hMenuEng);
        }

        PostQuitMessage(0);
        return 0;
    }
    break;

    case WM_COMMAND:
    {

...続く


RegisterClassExで構造体メンバーlpszMenuNameにあらかじめメニューをセットしていた場合、厳密には自力で破棄する必要があることになるので注意してください。

SetMenuのリファレンスより引用

既にメニューが割り当てられているウィンドウに対して SetMenu 関数を呼び出すと、この関数は既存のメニューを置き換えますが、破棄しません。以前のメニューを破棄するには、アプリケーションは DestroyMenu 関数を呼び出すべきです。

尚、Visual Studo 2017で確認したもので、clang環境でのビルドは試していませんのでご了承ください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/30 23:53

    ありがとうございます。
    無事解決いたしました。

    キャンセル

0

動的に変える場合はLoadMenuSetMenuっていうそのまんまのAPIがあるんでこちらを使ってください。

SetMenu(hWnd, LoadMenu(hInst, MAKEINTRESOURCE(IDC_MENU_JPN);

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる