我們當然知道 ,現在,在實際開發中肯定不會像我這樣寫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個妞,我一個都沒換成,你的愛情事業如此成 功。再說了,呂不韋臨死前有教過秦始皇怎麼統一中華嗎?難道秦始皇會說:以前沒人統一過華夏,我 怎麼統一?最後他老人家還是把六國給干掉了。