開文序
Win32 和 MFC,其實之前一直都有碰過,
只是到後來都是寫一些資料分析、數值分析的程式
都用 Console 介面去完成,從來沒想過特別寫成 Window 介面
在此先再強調一些別人特別常問的事..
Q1 : 學 Win32 好 還是學 MFC 好?
我建議是學 Win32, MFC 之後會很快上手, 如果您是工作上趕著用的話, 可以先買一本 MFC 聖經, 摸熟後也一定要再回頭學 Win32
Q2 : 我沒有程式底子適合學 Win32/MFC 嗎?
請先學 C++ 吧..
Q3 : 我只有學過 C,不會C++,適合學 Win32/MFC 嗎?
如果只學過 C 沒學過 C++, 請先直接看 C++ 中的類別、繼承、複載等關係單元
還有例外處理單元
正文
1. Win32 程式設計概念 : Win32 主要是拿來刻視窗用的, 裡面提供一狗票 API, 當然還有很多的 "觀念性" 東西要學, 特別是 "訊息" 與 "處理" 的概念,說穿了,早期的 C/C++ 程式進入點是 int main(), 而 win32 程式進入點變成了 int WINAPI WinMain(.......), (還找得到進入點算不錯了..), 請記住一件事, win32 以後是以 "設計視窗程式" 為主的, 既然是視窗程式, 免不了一定會有所謂的 "訊息處理", 訊息的種類少說也是上百種, 建立視窗(WM_CREATE)、改變視窗大小(WM_SIZE)、拖曳檔案(WM_DRAG)... 一狗票,
以下所提及的也是重要的二大重點 (1) 建立視窗 (2) 視窗訊息的處理
2. 視窗結構體 - WNDCLASSEX : 先聲明, 如果你還不知道什麼叫 "類別(class)", 那代表你該先學學 C++, in32 裡面常用的視窗結構體就是 WNDCLASSEX, 定義如下
typedef struct {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
(1) cbSize: 這個變數的大小, 常以 sizoef(WNDCLASSEX) 表示
(2) style : 視窗的樣式, 這部份可設定的很多, 常以 CS_HREDRAW | CS_VREDRAW 表示
(3) lpfnWndProc : 這是一個函數指標, 要先事先寫好一個處理視窗訊息的函數, 這個指標再指過去
(4) cbClsExtra : 設 0
(5) cbWndExtra : 設 0
(6) hInstance : 代表視窗的一個句柄 (說實話, handle 怎麼翻我是真的不會), 你可以把它想像成, OS會把每個視窗一個數字, 而這個數字我們就用 HANDLE 去管理它, HANDLE hInstance 便是告訴你 OS 給這個視窗的編號
(7) hIcon, hIconSm : 你視窗出現的 Icon
(8) hCursor: 在你視窗裡面所使用的 mouse 形狀
(9) hbrBackground : 設定視窗的背景顏色
(10) lpszMenuName: 設定 Menu 的名稱, 如果沒有 Menu , 設為 NULL,
(11) lpszClassName : 設定這個視窗的 Class Name
3. 設定完結構體後之步驟
(1) 註冊(RegisterClassEx) - 設定完 WNDCLSSEX 後, 調用 RegisterClassEx 函數, 向系統註冊視窗類別, 成功時傳回 TRUE, 失敗傳回 FALSE
(2) 顯示視窗(ShowWindow) - 注意, 以上之動作都是在背後默默執行, 等到這些動作都 OK 的時候, 再進行視窗顯示之動作, ShowWindow() 函數裡有 SW_SHOWNORMAL, SW_HIDE, SW_MAXIMIZED, SW_MINIMIZED 等值可用, 下面用 SW_SHOWNORMAL
(3) 立即更新一次視窗(UpdateWindow) - 當以上動作都做完時, 先更新一次視窗,
(4) 訊息處理 -
(4.1) GetMessage - 取得對此視窗造成之訊息
(4.2) TranslateMessage - 將這些訊息傳送出去
(4.3) DispatchMessage - 將這些訊息解碼處理
上面都只是函數字面上的意思, 實作上大多是這麼做
while(GetMessage(...)) // 無窮迴圈去等待/取得訊息
{
TranslateMessage(....); // 將取得之訊息傳出去
Dispatchmessage(....); // 將取得之訊息進行解碼
}
當然, 我們還有一個步驟沒做, 就是編寫 "處理訊息的函數"
4. 處理訊息函數
處理訊息的函數長得都差不多 -
LRESULT CALLBACK 訊息函數名稱(HWND hWnd,
UINT msg, WPARAM wParam, LPARAM );
注意,上面可以改的真的也只有 訊息函數名稱,引數內容為固定
而裡面的寫法基本上大同小異
LRESULT CALLBACK 訊息函數名稱(HWND hWnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case 訊息1(如WM_CREATE) :
......
break;
case 訊息2 (如 WM_PAINT) ;
.........
break;
......
......
default:
// 剩下沒做的, 叫 DefWindowProc 這個裡面就有的函數幫我們做
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
5. 架構大致上上面都說明完畢, 還有一些參數、訊息定義, 不適合在這篇說完
往後會再進行補充, 若有興趣知道, 也歡迎回覆討論, 以下為完整之程式碼
請參考..
// ========================================
// filename: Hello.cpp
// author : Edison.Shih.
// Date : 2010.2.24
// Complier: VC 6.0
// ========================================
#include <windows.h>
// ========================================
// function declare
LRESULT CALLBACK MainProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lPCmdLine,
int nShowCmd);
// ========================================
// WinMain function
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lPCmdLine,
int nShowCmd)
{
WNDCLASSEX wcx;
HWND hwnd;
MSG msg;
TCHAR ClsName[100] = ("Class Name");
//填充視窗類別 wcx
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.cbSize = sizeof(wcx);
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcx.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcx.hInstance = hInstance;
wcx.lpfnWndProc = MainProc;
wcx.lpszClassName = ClsName;
wcx.lpszMenuName = NULL;
wcx.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClassEx(&wcx)) {
MessageBox(NULL,("Register Fail."),MB_OK,NULL);
return 1;
}
hwnd = CreateWindow(
"Class Name",
"Title Name",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
if(!hwnd){
MessageBox(NULL,("CreateWindow Fail."), MB_OK, NULL);
return 1;
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
while(GetMessage(&msg, hwnd, NULL, NULL)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)(msg.wParam);
}
// ========================================
// map message function
LRESULT WINAPI CALLBACK MainProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
// for WM_PAINT variable
HDC hdc;
PAINTSTRUCT ps;
// message process
switch(msg)
{
case WM_CREATE: // 視窗建立時
break;
case WM_PAINT: // 繪圖管理
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc,0,0,("Hello,World"),strlen(("Hello,World")));
EndPaint(hwnd, &ps);
break;
case WM_DESTROY: // 視窗毀滅時
PostQuitMessage(0);
break;
case WM_SIZE: // 視窗改變大小時
MessageBox(NULL, "change size..", MB_OK, NULL);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// ========================================
最後,若對程式碼或概念有問題
歡迎回覆討論
留言列表