也不知道發生什么事情,CSDN把我的文章弄到首頁,結果有不少說我在誤人子弟,是啊,我去年就說過了,如果你要成為磚家級人物,請遠離我的博客,我這個人沒什么特長,唯一厲害的一點就是不相信權威,鄙視磚家,所以,我一直以來都有屬于俺自己的編程思想。
就好比當年詠春拳剛浮出江湖的時候,武學界罵聲不停,有人說:“這哪像拳?分明是女人拳。”然后不知道什么時候,一位叫葉問的大俠突然牛B起來了,于是,詠春拳的傳播速度比其他拳種更快,都傳到老外那里去了。
所以,為了繼續誤人子弟,我決定把這些文章寫下去,直到誤盡天下蒼生為止。
我們當然知道,現在,在實際開發中肯定不會像我這樣寫Win32程序的,你看,連個WinMain都要N行代碼。但很多人不明白什么叫學習,什么叫探索。實際上,通常能用于實際開發中的技巧只是占我們對客觀世界的認識總和不到20%,所以,如果你有興趣計算一下,估計有80%的知識你不知道用到哪里去了。就算我們今后不會把Win32程序投入到實際操作中,然而如果你了解過這東西,你會發現很多時候對我們是有幫助的。
哪怕只是簡單認識一下Win32的一些原理,相信對于日后編程的學習和成長,是有益處的。
為了提高誤人子弟的效果,上面我說了幾段F話,下面開始今天的正題。
要在窗口上添加菜單,當然你可能會研究出N種方法,不過,這里我說兩種,一種相當復雜,另一種稍微簡單。
**方法一,用代碼添加菜單**
這種方法的思路是:首先在全局范圍內定義一個HMENU的變量,用來保存窗口中菜單欄的句柄,根菜單(菜單欄)可以CreateMenu函數來創建,接著可以使用AppendMenu函數或者InsertMenuItem函數來創建菜單項。
句柄就是內存中各種資源的ID,比如圖標,圖片,字符串等。我們的菜單也是一種資源。
下面我寫了一個函數,用來動態創建菜單。
~~~
void CreateMyMenu()
{
hRoot = CreateMenu();
if(!hRoot)
return;
HMENU pop1 = CreatePopupMenu();
AppendMenu(hRoot,
MF_POPUP,
(UINT_PTR)pop1,
L"操作");
// 一種方法是使用AppendMenu函數
AppendMenu(pop1,
MF_STRING,
IDM_OPT1,
L"飛機");
// 另一種方法是使用InsertMenuItem函數
MENUITEMINFO mif;
mif.cbSize = sizeof(MENUITEMINFO);
mif.cch = 100;
mif.dwItemData = NULL;
mif.dwTypeData = L"機關槍";
mif.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
mif.fState = MFS_ENABLED;
mif.fType = MIIM_STRING;
mif.wID = IDM_OPT2;
InsertMenuItem(pop1,IDM_OPT2,FALSE,&mif);
}
~~~
hRoot是在外部定義的全局變量,保存菜單欄的標識。完整的代碼如下:
~~~
#include <Windows.h>
#define IDM_OPT1 301
#define IDM_OPT2 302
HMENU hRoot;
void CreateMyMenu();//創建菜單
LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR cmdLine,
int nShow)
{
CreateMyMenu();//創建菜單
WCHAR* cn = L"Myapp";
WNDCLASS wc={ };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = cn;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)MyWinProce;
RegisterClass(&wc);
HWND hm = CreateWindow(cn,
L"我的應用程序",
WS_OVERLAPPEDWINDOW,
20,
15,
420,
360,
NULL,
hRoot,
hInstance,
NULL);
if( hm == NULL )
return 0;
ShowWindow(hm,nShow);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
//DestroyMenu(hRoot);
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wParam,lParam);
}
}
void CreateMyMenu()
{
hRoot = CreateMenu();
if(!hRoot)
return;
HMENU pop1 = CreatePopupMenu();
AppendMenu(hRoot,
MF_POPUP,
(UINT_PTR)pop1,
L"操作");
// 一種方法是使用AppendMenu函數
AppendMenu(pop1,
MF_STRING,
IDM_OPT1,
L"飛機");
// 另一種方法是使用InsertMenuItem函數
MENUITEMINFO mif;
mif.cbSize = sizeof(MENUITEMINFO);
mif.cch = 100;
mif.dwItemData = NULL;
mif.dwTypeData = L"機關槍";
mif.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
mif.fState = MFS_ENABLED;
mif.fType = MIIM_STRING;
mif.wID = IDM_OPT2;
InsertMenuItem(pop1,IDM_OPT2,FALSE,&mif);
}
~~~

**方法二,通過編輯資源來添加菜單**
上面的方法雖然創建了菜單,但你也看到了,是相當地不方便,所以,下面我重點介紹一下用資源編輯器來創建菜單資源。
在你的開發工具上,依次找到菜單項【視圖】【資源視圖】。

在資源視圖中,右擊項目根節點,從彈出的菜單中選擇【添加】【資源】。

在隨后彈出的對話框中,選擇Menu,單擊右邊的“新建”按鈕。

可以通過屬性窗口來重命名菜單的ID。

我們可以使用可視化視圖來建立菜單,為了可以在代碼中使用,給需要的菜單一個ID,這個名字你可以自己喜歡,只是慣用的是以IDM_開頭,意思是Menu ID,比如IDC開頭的,意思是Control ID等等。

編輯好之后,保存,在【解決方案資源管理器】中你會看到一個resource.h文件,其實我們為資源定義的ID都以宏的形式聲明的,不信你打開看看。

資源ID都是數字,只是為它定義個名字,方便識別罷了,就好像人們平時只叫你的名字或者小名,你見過誰會叫你的身份證號碼的?
開發工具生成的ID有時候會有問題,或者有些ID我們在程序中沒用上,如果你覺得它們留在代碼文件中會影響市容的話,你可以這樣:
1、在【資源視圖】窗口中,右擊,從彈出的快捷菜單中選擇【資源符號...】,彈出一個窗口,這里可以看到應用程序中的資源ID列表,以及哪些ID已被使用,但是,這個窗口中顯示的內容,有時候不準確,有些ID明明沒有被使用,它右邊卻打上了勾。
這里可以修改ID的值,也可以新建資源ID,所以,你也可以在這里預先為資源分配ID,然后在屬性窗口設置資源的標識時,從下拉列表中選擇指定的ID。資源ID的名字和數值不能重復,但是,不同的資源是可以使用同一個資源ID的。例如,通常在應用程序中,某些菜單項的功能和工具欄上的按鈕是一一對應的,功能相同,這種情況下,我們可以考慮讓它們共用一個ID。
2、你可以直接打開resource.h頭文件,直接在上面修改。
**響應菜單命令**
當用戶單擊某個菜單項后,窗口處理程序(WindowProc)會收到一條WM_COMMAND消息,它的兩個附加參數如下:

在收到WM_COMMAND后,我們可以用LOWORD取得它的低數位,上表中已經說明,wParam的低位值表示菜單的資源ID,我們通過它的值與哪個菜單的ID相等,就知道用戶點擊了哪個菜單項。
所以,我們的程序代碼現在應為:
~~~
#include <Windows.h>
#include "resource.h"
LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int CALLBACK WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR cmdLine,
int nShow)
{
WCHAR* cn = L"Myapp";
WNDCLASS wc={ };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = cn;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)MyWinProce;
RegisterClass(&wc);
HWND hm = CreateWindow(cn,
L"我的應用程序",
WS_OVERLAPPEDWINDOW,
20,
15,
420,
360,
NULL,
// 加載菜單資源
LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN)),
hInstance,
NULL);
if( hm == NULL )
return 0;
ShowWindow(hm,nShow);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
{
// 取出資源Id值
// 并判斷用戶選擇了哪個菜單項
switch(LOWORD(wParam))
{
case IDM_PLANE:
MessageBox(hwnd,L"灰機來了。",L"提示",MB_OK);
break;
case IDM_GUN:
MessageBox(hwnd,L"讓炮彈飛。",L"提示",MB_OK);
break;
case IDM_MT_GUN:
MessageBox(hwnd,L"山炮欲來風滿樓。",L"提示",MB_OK);
break;
default:
break;
}
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wParam,lParam);
}
}
~~~
在注冊窗口類時,如要設置菜單,調用LoadMenu函數,第一個參數是當前程序實例的句柄,從WinMain的參數中獲得,第二個參數是菜單的ID,因為這里要名字,字符串,而我們的ID都是數值,可通過MAKEINTRESOURCE宏轉換。至于MessageBox函數就不用我介紹了。

好了,我們的應用程序已經創建了菜單了。
呵,有人說我的編程學習方法很奇特,其實,我們何苦要墨守成規呢,局限在定勢思維中呢?枯躁無味的東西,你可以人為地讓它變得充滿樂趣,關鍵是你的心態罷了。這讓我想起,以前某同學A跟我討論兩個問題:
1、我的程序只想保存用戶的一些使用設置,用數據庫沒必要,寫注冊不環保;
2、我有一個dll類庫,我希望最后我的程序既可以引用它,但同時只生成一個exe文件,能做到吧。
我就說了,問題一好辦,你定義一個存放設置項的類,把它直接進行XML序列化和反序列化就完事了,這既能保存數據,又可以封裝對象。如果不想讓別人看到XML文件中的內容,就把它用DES算法加密/解密;而第二個問題,你把dll文件當成資源嵌入到程序的資源文件中,運行時如果要用到類庫的類,那就把它反射出來,動態調用就行了。
然后他說,這好像沒有人這樣做,老師也沒教過。
我說:靠,你老爸小時候教過你泡妞嗎?你一上大學怎么就學會了泡妞?四年大學還換了N個妞,我一個都沒換成,你的愛情事業如此成功。再說了,呂不韋臨死前有教過秦始皇怎么統一中華嗎?難道秦始皇會說:以前沒人統一過華夏,我怎么統一?最后他老人家還是把六國給干掉了。
- 前言
- (1):關于C++的幾個要點
- (2):完整的開發流程
- (3):窗口的重繪
- (4):創建菜單
- (5):具有單選標記的菜單
- (6):創建右鍵菜單
- (7):多邊形窗口
- (8):繪圖(A)
- (9):繪圖(B)
- (10):繪圖(C)
- (11):使用控件——先來耍一下按鈕
- (12):使用控件——單選按鈕
- (13):握手對話框
- (14):用對話框作為主窗口
- (15):ListView控件
- (16):ListView的多個視圖
- (17):啟動和結束進程
- (18):使用對話框的兩個技巧
- (19):瀏覽和打開文件
- (20):瀏覽文件夾
- (21):復制&amp;粘貼&amp;剪貼板操作
- (22):抓取屏幕
- (23):漸變顏色填充
- (24):計時器
- (25):監視剪貼板