从控制台程序到窗口程序,还需要解决多个文件的问题。看到MFC创建的很多类型文件,初学者肯定 “忐忑不安”。
从结构化编程的思想来看,独立的功能模块,应该封装成函数。同时为了方便管理,类似的功能模块可以集中在一起,不同的功能组应该分开管理。所以,对第一个窗口程序进行整理。
项目按功能划分为三大块:主函数、初始化、窗口函数
每块都按照一个头文件+一个源文件 的格式来设计,块之间功能的调用则只需要包含相应的头文件即可。合计 6 个文件:
1、初始化函数分为两大功能:创建并注册窗口类, 根据注册的窗口类名创建主窗口
头文件 Init.h:
- #ifndef INIT_H
- #define INIT_H
- #include "main.h"
-
- HWND InitInstance(HINSTANCE hInstance,int nCmdShow);
- BOOL InitApplication(HINSTANCE hInstance);
-
- #endif
#ifndef INIT_H
#define INIT_H
......
#endif
是通过预编译语句避免重复包含,如 A 包含 X 头文件,B 包含 X 和 A,这样就存在重复包含X的问题,但是可以利用文件名的唯一性(把 点换成 _ ),判断是否定义宏来避免。
还有更简单的方式: #pragma once 可以完成以上功能。
源文件: Init.cpp
- #include "init.h"
- #include "wndproc.h"
-
- char szAppName[]="AppName"; //指定窗口类名称
- char szTitle[]="Second API Application"; //窗口标题
-
- BOOL InitApplication(HINSTANCE hInstance) //自定义函数
- {
- WNDCLASS wc; //利用系统提供的类型定义变量存储窗口类数据
-
- wc.style=CS_VREDRAW|CS_HREDRAW; //窗口类风格
- wc.lpfnWndProc=MainWndProc; //指定窗口消息处理函数(Finit.cpp)
- wc.cbClsExtra=0; //额外分配在类数据结构尾端的字段数
- wc.cbWndExtra=0; //额外分配在应用程序运行副本尾端的字段数
-
- wc.hInstance=hInstance; //应用程序实例句柄(来自WinMain函数)
- wc.hIcon=LoadIcon(hInstance,NULL); //最小化时图标
- wc.hCursor=LoadCursor(NULL,IDC_ARROW); //鼠标光标
- wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); //背景颜色
- wc.lpszMenuName=NULL; //窗口菜单
- wc.lpszClassName=szAppName; //给窗口类起个名称
- return(RegisterClass(&wc)); //窗口类注册
- }
-
-
- //产生应用程序的主窗口,并以nCmdShow指定方式显示
- HWND InitInstance(HINSTANCE hInstance,int nCmdShow) //自定义函数
- {
- HWND hwnd; //利用系统提供的类型定义窗口句柄
-
- hwnd = CreateWindow(//Windows系统接口函数(Windows API)
- szAppName, //窗口类名称(在wc.lpszClassName中指定)
- szTitle, //窗口标题
- WS_OVERLAPPEDWINDOW, //窗口风格,无标题栏
- 0, //窗口左上角x坐标
- 0, //窗口左上角y坐标
- GetSystemMetrics(SM_CXFULLSCREEN), //窗口宽度
- GetSystemMetrics(SM_CYFULLSCREEN)+60, //窗口高度
- NULL, //父窗口句柄
- NULL, //窗口菜单句柄
- hInstance, //程序实例句柄
- NULL //提供窗口使用的额外数据
- );
-
- if (!hwnd)
- return NULL; //未能正常产生窗口
-
- //Windows系统接口函数(Windows API)
- ShowWindow(hwnd,nCmdShow); //以nCmdShow指定方式显示窗口
- UpdateWindow(hwnd); //更新用户区
- return hwnd;
- }
类名和窗口标题做成了全局变量(尽量避免),另外,函数的命名可能与功能不太匹配。
2、窗口函数用于处理窗口消息,这是窗口应用程序关注的重点,MFC费尽心思就是想让程序员集中精力于处理消息响应,事与愿违,造成了MFC入门困难的窘境。
头文件:wndproc.h
- #ifndef WNDPROC_H
- #define WNDPROC_H
- #include "main.h"
-
- LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
- WPARAM wParam, LPARAM lParam);
-
-
-
- #endif
源文件:wndproc.cpp
- #include "wndproc.h"
-
- LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message,
- WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- case WM_ERASEBKGND:
- {
-
- HDC hdc = ::GetDC(hWnd);
- RECT rect;
- ::GetClientRect(hWnd,&rect);
- ::FillRect(hdc,&rect,(HBRUSH)::GetStockObject(WHITE_BRUSH));
-
- break;
- }
- /*
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- HDC hdc = ::BeginPaint(hWnd,&ps);
- RECT rect;
- ::GetClientRect(hWnd,&rect);
- ::FillRect(hdc,&rect,(HBRUSH)::GetStockObject(GRAY_BRUSH));
- ::EndPaint(hWnd,&ps);
- break;
- }*/
-
- case WM_CLOSE:
- {
- ::DestroyWindow(hWnd);
- break;
- }
-
- case WM_DESTROY: //如果按下关闭按钮
- {
- PostQuitMessage(0); //发出关闭程序的消息
- break;
- }
- default: //其他消息发给windows默认窗口处理函数
- return (DefWindowProc(hWnd,message,wParam,lParam));
- }
- return 0;
- }
3、主函数,MFC屏蔽了主函数(编译的时候加入),让初学者对程序的流程很难理解,心怀忐忑
头文件:Main.h
- #ifndef MAIN_H
- #define MAIN_H
- #include
-
- #endif
在其他头文件中包含Main.h ,主要是需要 #include
源文件:Main.cpp
- #include "main.h"
- #include "init.h"
-
-
-
- int WINAPI WinMain(HINSTANCE hInstance, //应用程序当前实例句柄
- HINSTANCE hPrevInstance, //应用程序前一个实例句柄
- LPSTR lpCmdLine, //命令行字符窜指针
- int nCmdShow) //应用程序的显示方式
- {
-
- HWND hwnd; //窗口句柄
- MSG msg; //消息结构变量
-
- if (!InitApplication(hInstance)) //若注册窗口类失败,自定义函数(Finit.cpp)
- return false;
-
- //产生应用程序的主窗口,并以nCmdShow指定方式显示,自定义函数(Finit.cpp)
- hwnd=InitInstance(hInstance,nCmdShow);
- if (!hwnd)
- return false;
-
- while (::GetMessage(&msg,NULL,NULL,NULL))
- {
-
- if (msg.message==WM_QUIT) //如果接受到退出的消息WM_QUIT,整个程序将结束.
- break;
- TranslateMessage(&msg); //把键盘消息组合成相对的字符消息
- DispatchMessage(&msg);
-
- }
-
- return(msg.wParam);
- }
-