開文序

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;
}


// ========================================

最後,若對程式碼或概念有問題

歡迎回覆討論

創作者介紹
創作者 Edison 的頭像
Edison

藍影

Edison 發表在 痞客邦 留言(10) 人氣()


留言列表 (10)

發表留言
  • EdisonShih
  • 好亂 = = 沒辦法正常顯示 - 炸!!
  • 尼歐醬
  • 請問是用什麼軟體寫的阿
  • C++

    Edison 於 2010/06/18 18:23 回覆

  • finalfrank
  • 哎呀...我居然在自修完之後才看到這篇好文 真是相見恨晚!
  • 呵. 關於 C. C++. Win32. MFC. 等
    如果有任何問題還是對什麼 topic 有興趣
    也歡迎一起討論 ^^

    Edison 於 2010/08/24 22:11 回覆

  • Rete
  • 你好,很感謝你的文章
    因為最近剛好開始碰win32API的東西

    從文章看起來,版主應該是資工相關科系
    想請問版主在win32API這邊有沒有推薦的書集做學習?

  • 耶... 您有點誤會了,我是商科(也不是資管)的,八輩子和資工打不到關係的科系.要學 Win32 設計視窗的話可以考慮這本:Programming Windows - Win32 API (Charles Petzold),目前 5e,學完後再看你要不要買類似 Win32 API 字典的東西放在身上方便查閱。

    Edison 於 2010/12/20 22:25 回覆

  • RR
  • 你好...

    想請問一下
    再函數前面加上 WINAPI CALLBACK
    這兩個究竟是甚麼意思呢?
  • WINAPI 可以不用理它,把它當作是為了以後 develope 留下的伏筆便可;CALLBACK 代表為事件驅動,由於視窗程式設計和一般 Console 設計有出入,是以 "訊息" 驅動為導向,使用 CALLBACK 函式時,那些訊息都會被 CALLBACK function 所截取,只是要不要處理而已。

    Edison 於 2011/02/15 10:54 回覆

  • 訪客
  • callback 不是直這麼說地
  • 小鬼
  • 想問一下 為我跑出來的背景 無法繪製成白色呢?
    #include <windows.h>
    long FAR PASCAL WndProc(HWND,WORD,WORD,LONG);

    int PASCAL WinMain
    (
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nShowCmd)

    {
    WNDCLASS wc;
    MSG msg;
    HWND hwnd;
    if(!hPrevInstance)
    {
    wc.style=NULL;
    wc.lpfnWndProc=WndProc;
    wc.cbClsExtra=0;
    wc.cbWndExtra=0;
    wc.hInstance=hInstance;
    wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor=LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground=GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName=NULL;
    wc.lpszClassName="c";
    RegisterClass(&wc);
    }

    hwnd=CreateWindow(
    "c",
    "First Porgram",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    NULL,
    hInstance,
    NULL);

    ShowWindow(hwnd,nShowCmd);
    UpdateWindow(hwnd);
    while(GetMessage(&msg,hwnd,NULL,NULL))
    DispatchMessage(&msg);
    return msg.wParam;
    }

    long FAR PASCAL WndProc(HWND hwnd,WORD message,WORD wParam,LONG lParam)
    {
    switch(message)
    {
    case WM_DESTROY:
    PostQuitMessage(0);
    break;
    default :
    return(DefWindowProc(hwnd,message,wParam,lParam));
    }
    return NULL;

    };
  • 風 寒
  • 有個BUG 消息循環那裡GetMessage(&msg, hwnd, NULL, NULL)
    會導致程式結束後還卡在消息循環
    改成 GetMessage(&msg, NULL, NULL, NULL)
    就可以正常結束了
  • Kai
  • 感謝~很實用的入門教學
    能再請教怎麼建立一個Button嗎?
  • Monitor
  • WINAPI 應該是代表 __cdecl 吧!(沒記錯的話喇)
    不過 devcpp 的編譯器是 gcc 預設就是 cdecl 了 加不加 WINAPI 應該是沒差的...