MFC入门

一. Win32的界面编写

  1. 实现一个带按钮并响应点击事件的界面,代码如下:

MFC入门
 1 #include <tchar.h>
 2 #include <windows.h>
 3 #include "CommCtrl.h"
 4 
 5 #define IDC_BTN_TEST (101)
 6 
 7 //窗口过程函数 
 8 LRESULT CALLBACK pfnWndProc(HWND stHwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
 9 {
10     switch (uiMsg)
11     {
12     case WM_CREATE:
13         ::CreateWindow(WC_BUTTON, _T("按钮测试"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 200, 200, 120, 50, stHwnd, (HMENU)IDC_BTN_TEST, NULL, NULL);
14         break;
15     case WM_COMMAND:
16         if ((IDC_BTN_TEST == LOWORD(wParam)) && (BN_CLICKED == HIWORD(wParam)))//点击按钮响应
17         {
18             ::MessageBox(NULL, _T("你点击了测试按钮"), _T("点击测试"), MB_OK);
19         }
20         break;
21     case WM_DESTROY:
22         PostQuitMessage(0);
23         break;
24     default:
25         return DefWindowProc(stHwnd, uiMsg, wParam, lParam);
26     }
27 
28     return 0;
29 }
30 
31 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
32 {
33     MSG stMsg;
34     memset(&stMsg, 0, sizeof (stMsg));
35     HWND stHwnd;
36     memset(&stHwnd, 0, sizeof (stHwnd));
37     WNDCLASS stWndClass;
38     memset(&stWndClass, 0, sizeof (stWndClass));
39     const TCHAR szAppName[] = _T("windowsUI");
40 
41     stWndClass.style = CS_HREDRAW | CS_VREDRAW;
42     stWndClass.lpfnWndProc = pfnWndProc;
43     stWndClass.cbClsExtra = 0;
44     stWndClass.cbWndExtra = 0;
45 
46     stWndClass.hInstance = hInstance;
47     stWndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
48     stWndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
49     stWndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
50     stWndClass.lpszMenuName = NULL;
51     stWndClass.lpszClassName = szAppName;
52 
53     if (!RegisterClass(&stWndClass))//注册窗口类
54     {
55         MessageBox(NULL, _T("Regiester failure"), szAppName, MB_ICONERROR);
56         return 0;
57     }
58 
59     stHwnd = CreateWindow(szAppName, //创建窗口
60         _T("windows UI"),
61         WS_OVERLAPPEDWINDOW,
62         CW_USEDEFAULT,
63         CW_USEDEFAULT,
64         800,
65         600,
66         NULL,
67         NULL,
68         hInstance,
69         NULL);
70 
71     ShowWindow(stHwnd, iCmdShow); //显示窗口
72     UpdateWindow(stHwnd);
73 
74     while (GetMessage(&stMsg, NULL, 0, 0))//消息循环
75     {
76         TranslateMessage(&stMsg);
77         DispatchMessage(&stMsg);
78     }
79 
80     return stMsg.wParam;
81 }
View Code

  2. 代码量大?记不住?为嘛不使用MFC实现?

    • 不用记这些参数什么的,但是UI流程必须要理解:注册窗口类---->创建窗口---->显示窗口---->消息循环---->窗口过程;
    • MFC本质是对windows的原生控件进行了封装,为了更好地使用MFC,可以多熟悉CommCtrl.h文件;  

二. MFC界面编写

  1. 同样实现一个带按钮并点击事件的界面(代码就不贴了,比较简单),生成两个重要文件MfcTest.cpp和MfcTestDlg.cpp

  2. 代码入口在哪?也就是WinMain函数在哪?我要怎么写界面?

    • CUI(控制台)入口函数:main   GUI(win32)入口函数:WinMain          Dll(动态库)入口函数:DllMain;
    • MFC除了对原生界面进行封装外,还对线程,文件,网络,字符串,STL等都进行了封装,但本质是win32程序;
    • MfcTest.cpp主线程类,MfcTestDlg.cpp主窗口类;

   3. MfcTest.cpp主线程类分析:

    • 通过“调用堆栈”可以追溯到WinMain函数,通过一步步调用,最后调用到MfcTest.cpp的InitInstance函数,那我们就可以认为InitInstance == WinMain
MFC入门
1 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
2     _In_ LPTSTR lpCmdLine, int nCmdShow)
3 #pragma warning(suppress: 4985)
4 {
5     // call shared/exported WinMain
6     return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
7 }
View Code
    • 入口函数找到了,那么像win32那样的界面是怎么创建的呢?在主线程里我们继续分析,可以找到窗口生成的如下代码:
MFC入门
1     CMfcTestDlg dlg;
2     m_pMainWnd = &dlg;
3     INT_PTR nResponse = dlg.DoModal();
4     
View Code

  4. MfcTestDlg.cpp主窗口类分析:

    • 窗口是怎么生成的?我们看下代码对比:
MFC入门
 1 //win32
 2     if (!RegisterClass(&stWndClass))//注册窗口类
 3     {
 4         MessageBox(NULL, _T("Regiester failure"), szAppName, MB_ICONERROR);
 5         return 0;
 6     }
 7 
 8     stHwnd = CreateWindow(szAppName, //创建窗口
 9         _T("windows UI"),
10         WS_OVERLAPPEDWINDOW,
11         CW_USEDEFAULT,
12         CW_USEDEFAULT,
13         800,
14         600,
15         NULL,
16         NULL,
17         hInstance,
18         NULL);
19 
20     ShowWindow(stHwnd, iCmdShow); //显示窗口
21     UpdateWindow(stHwnd);
22 
23 ... ...
24 
25 //MFC
26     CMfcTestDlg dlg;
27     m_pMainWnd = &dlg;
28     INT_PTR nResponse = dlg.DoModal();
29 
30 //这里不纠结模态与非模态对面框
View Code
    • 那么窗口过程呢?怎么找不到,我们再来对比下:
MFC入门
 1 //窗口过程函数 
 2 LRESULT CALLBACK pfnWndProc(HWND stHwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
 3 {
 4     switch (uiMsg)
 5     {
 6     case WM_CREATE:
 7         OnInitDialog();//CMfcTestDlg
 8         break;
 9     case WM_COMMAND:
10         OnSysCommand();//CMfcTestDlg,先不作判断,只是简单对比
11         break;
12     case WM_PAINT:
13         OnPaint();//CMfcTestDlg
14         break;
15     case WM_QUERYDRAGICON:
16         OnQueryDragIcon();//CMfcTestDlg
17         break;
18     case WM_DESTROY:
19         PostQuitMessage(0);
20         break;
21     default:
22         return DefWindowProc(stHwnd, uiMsg, wParam, lParam);
23     }
24 
25     return 0;
26 }
View Code
    • 通过对比,我们知道MFC只是对win32的窗口生成和窗口消息进行了二次封装,最后我们再分析下消息列表是怎么回事:
MFC入门
1 BEGIN_MESSAGE_MAP(CMfcTestDlg, CDialogEx)
2     ON_WM_SYSCOMMAND()
3     ON_WM_PAINT()
4     ON_WM_QUERYDRAGICON()
5     ON_BN_CLICKED(IDC_BTN_TEST1, &CMfcTestDlg::OnBnClickedBtnTest)
6     ON_BN_CLICKED(IDC_BTN_TEST2, &CMfcTestDlg::OnBnClickedBtnTest2)
7     ON_MESSAGE(WM_USER_BTN, &CMfcTestDlg::OnUserBtnTest)
8 END_MESSAGE_MAP()
View Code
    • F12跟进里面各种宏头都看晕了,但是说白了就是将消息ID与消息处理函数绑定起来,想想C11里的std::bind性能或者委托模式的CDelegate实现;

  5. 窗口消息之间的相互传递

    • 在这里只说下自定义消息的传递SendMessage(同步)和PostMessage(异步),代码如下:
MFC入门
 1 //自定义消息
 2 #define WM_USER_BTN (WM_USER + 100)
 3 
 4 ... ...
 5 
 6 BEGIN_MESSAGE_MAP(CMfcTestDlg, CDialogEx)
 7         ... ...
 8     ON_MESSAGE(WM_USER_BTN, &CMfcTestDlg::OnUserBtnTest)
 9 END_MESSAGE_MAP()    
10 
11 ... ... 
12 
13 LRESULT CMfcTestDlg::OnUserBtnTest(WPARAM wParam, LPARAM lParam)
14 {
15     MessageBox(_T("收到自定义消息"));
16 
17     return NULL;
18 }
View Code    
上一篇:MFC深入浅出-消息映射的实现


下一篇:MFC中回车处理