右鍵選單 (Context Menu) 為浮動選單,不依附於視窗選單列。建立流程:
WM_CONTEXTMENU 訊息TrackPopupMenu 顯示DestroyMenu 釋放資源新增選單資源,結構為:根選單 → 彈出式選單 → 選單項目。根選單標題 (如「abc」) 不會顯示,僅作為容器。

case WM_CONTEXTMENU: {
HMENU hroot = LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT));
if (hroot) {
HMENU hpop = GetSubMenu(hroot, 0); // 取得第一個彈出式選單
int px = GET_X_LPARAM(lParam);
int py = GET_Y_LPARAM(lParam);
TrackPopupMenu(hpop, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RIGHTBUTTON,
px, py, 0, (HWND)wParam, NULL);
DestroyMenu(hroot); // 釋放資源
}
} break;

需判斷滑鼠位置是否在**用戶區** 內。使用 GetClientRect 取得用戶區矩形 (相對視窗座標),但 WM_CONTEXTMENU 的座標是**螢幕座標**,需轉換:
case WM_CONTEXTMENU: {
RECT rect; POINT pt;
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
GetClientRect((HWND)wParam, &rect);
ScreenToClient((HWND)wParam, &pt); // 螢幕→用戶區座標
if (PtInRect(&rect, pt)) { // 在用戶區內
HMENU hroot = LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT));
if (hroot) {
HMENU hpop = GetSubMenu(hroot, 0);
ClientToScreen((HWND)wParam, &pt); // 用戶區→螢幕座標 (TrackPopupMenu 需要)
TrackPopupMenu(hpop, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RIGHTBUTTON,
pt.x, pt.y, 0, (HWND)wParam, NULL);
DestroyMenu(hroot);
}
} else {
return DefWindowProc(hwnd, msg, wParam, lParam); // 交給系統處理 (顯示系統選單)
}
} break;




case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDM_WANG: MessageBox(hwnd, L"你選擇了王維。", L"提示", MB_OK); break;
case IDM_MENG: MessageBox(hwnd, L"你選擇了孟浩然。", L"提示", MB_OK); break;
case IDM_LI: MessageBox(hwnd, L"你選擇了李白。", L"提示", MB_OK); break;
}
} return 0;
#include <Windows.h>
#include "resource.h"
#include <WindowsX.h>
LRESULT CALLBACK MyMainWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hThisApp, HINSTANCE hPrevApp, LPSTR cmdLine, int nShow) {
WNDCLASS wc = { };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"MyApp";
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = hThisApp;
wc.lpfnWndProc = (WNDPROC)MyMainWindowProc;
RegisterClass(&wc);
HWND hwnd = CreateWindow(L"MyApp", L"我的超級應用", WS_OVERLAPPEDWINDOW,
60, 25, 420, 300, NULL, NULL, hThisApp, NULL);
if (hwnd == NULL) return 0;
ShowWindow(hwnd, nShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MyMainWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0); return 0;
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDM_WANG: MessageBox(hwnd, L"你選擇了王維。", L"提示", MB_OK); break;
case IDM_MENG: MessageBox(hwnd, L"你選擇了孟浩然。", L"提示", MB_OK); break;
case IDM_LI: MessageBox(hwnd, L"你選擇了李白。", L"提示", MB_OK); break;
}
} return 0;
case WM_CONTEXTMENU: {
RECT rect; POINT pt;
pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam);
GetClientRect((HWND)wParam, &rect);
ScreenToClient((HWND)wParam, &pt);
if (PtInRect(&rect, pt)) {
HMENU hroot = LoadMenu((HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
MAKEINTRESOURCE(IDR_CONTEXT));
if (hroot) {
HMENU hpop = GetSubMenu(hroot, 0);
ClientToScreen((HWND)wParam, &pt);
TrackPopupMenu(hpop, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RIGHTBUTTON,
pt.x, pt.y, 0, (HWND)wParam, NULL);
DestroyMenu(hroot);
}
} else {
return DefWindowProc(hwnd, msg, wParam, lParam);
}
} break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
WM_CONTEXTMENU:wParam=視窗控制代碼,lParam=螢幕座標ScreenToClient 判斷用戶區 → ClientToScreen 還原給 TrackPopupMenuDefWindowProc 讓系統顯示標題列選單TrackPopupMenu 後必須 DestroyMenu 釋放資源Shift+F10 也會觸發 WM_CONTEXTMENU