从个人的学习经验看来。为了少走弯路,在深入学习Visual C++ 面向对象与可视化程序设计时,很有必要先对 Window应用程序框架来个初步的深刻的理解 并以此过度到真正的MFC编程中。
好了 废话少说 直奔主题
提示:Window 的一些 函数名称,类型,宏 等都是比较人性化的,所谓的人性化就是与英文的拼写比较接近,这一特征在一下代码有所体现
这里说一下 宏 的定义:C++ 宏 是将一个标识符定义为一个字符串(百度百科上的解释) 这里说一下为什么需要使用宏,
我个人理解使用宏的最重要的一个原因 就是减轻程序猿的记忆负担,将一个毫无规律可循的字符串用一个(有规律可循的,比较好认的)标识符来表示,同时也可以使程序看起来美观吧。举个简单的例子 我们在程序中经常使用的
const double pi = 3.1415926; //定义一个常量PI
但我们也可以用 宏来完成:
#define PI 3.1415926
我们先看一段代码:
这里我为了将 Window 应用程序框架表示的更清晰
用 Init()函数封装了 窗口的设计过程. 然后窗口设计中用到的一些局部变量我就以引用的
方式传给Init() 在传给 函数内部使用了
#include<tchar.h>
#include<Windows.h>
LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
void Init(WNDCLASS &wcex,HINSTANCE &hInstance);
// 以上是各种函数的函数声明,函数使用前必须声明啊,系统函数除外,这里必须说明下消息循环函数不是系统函数,
// 有程序员编写, 因此函数名就可以自己去 不过一般不改变,在此我使用WindowProc , 我们也可以改为WndProc(..)
// 我们可以发现 WindowProc 函数在返回值 LRESULT(这是一个 宏 我们并不需要深究他 只知道他是一个类型就OK 类似于int)
// 前面有 CALLBACK 这是怎么回事呢 ? CALLBACK 回调, 有CALLBACK 修饰的函数成为回调函数, 我们可以简单理解为
// 由程序员写的 但是由系统调用的函数成为回调函数
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
WNDCLASS wcex; //创建一个WNDCLASS结构对象
HWND hwnd; //创建过个窗口句柄
MSG msg; //创建一个MSG结构对象
Init(wcex, hInstance); //设计窗口
// 设计完一个类接下来就是 注册这个类了
//这就好比我们设计了一个好东西,为了防止别人抄袭,我们需要去注册它
if( ! RegisterClass(&wcex)) //注册窗口 我们用RegisterClassEx这个系统提供的函数 ,注意前来有个 ! 运算符
//从函数命名可以知道这是用来注册用的,至于参数,暂时不管,后面解释
{
//MessageBox(NULL,"窗口注册失败","窗口注册",NULL); //MessageBox()其实就是弹出一个(提示)消息框,我们使用电脑时经常也遇到
return 0; // 如果注册失败 返回0;
}
//注册完窗口开始创建窗口了,我们调用系统函数CreateWindow(),
//注意他返回的是一个窗口句柄, 我们之前创建的 窗口句柄就是用来 接受它的返回值的
hwnd = CreateWindow("可视化编程","程序讲解",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,
hInstance,NULL);// 注意 创建窗口只需要调用一个函数就完成了,参数过长可以分多行写
if( ! hwnd)// 如果创建窗口失败 hwnd值为NULL !hwnd 为真,返回0 退出WinMain函数
{
//MessageBox(NULL,"窗口注册失败","窗口注册",NULL);
return 0;
}
//创建完还不行啊,要给使用者看啊。因此需要显示窗口
ShowWindow(hwnd,nCmdShow); //注意两个参数,第一个参数就是CreateWindow返回的 hwnd,而第二个
//参数就是 我们的WinMain 函数的最后一个参数,窗口的初始状态(是最大化的
//还是最小化的))
//回想一下我们注册窗口是的背景,我们是用白色是粉刷的,但是结合实际,我们
//平常常用的程序一进去并不是白色的,因此我们这里也一样,显示了窗口之后,必要对窗口作一下必要的初始化
//用到UpdateWindow() 这个系统函数
UpdateWindow(hwnd); //参数也是使用前面的hwnd; 至于为什么 先不管
while(GetMessage(&msg,NULL,0,0)) //进行消息循环
{
TranslateMessage(&msg); // 将虚拟键转化为字符消息,
//用键盘进行输入时将按键虚拟键转化为字符消息,一边显示出来
DispatchMessage(&msg); //将消息"发送"到指定的窗口函数 即 WindowProc
}
// 至此,一个应用程序的基本框架就完成了,等等,不是说消息循环是Window程序主体么?
//为什么没看到调用窗口消息处理函数,其实程序内部已经调用了 只是我们不知道而已,那么系统是如何调用的?
// 我们发现 注册窗口时 Init()函数内 有这么一个表达式 wcex.lpfnWndProc = WindowProc; 系统就是从这里得知消息处理函数的,
// 函数地址都已经知道了 调用岂不是容易事, 好了,他的内部操作我们就不深入了
return (int) msg.wParam; // 程序对出之后我们返回这个消息的某些参数 wParam是MSG 结构的一个字段
}
void Init(WNDCLASS &wcex,HINSTANCE &hInstance) //使用的是引用参数,注意我传入的两个参数
{
wcex.style = 0; // (这里表示将要创建的窗口的类型)
//值为0是采用的是默认类型,我们用他默认类型就可以了
wcex.cbClsExtra = 0; //有兴趣的可以自己上网查查,这里不做介绍,
//在SDK编程中用处也不大。
wcex.cbWndExtra = 0; //同上
wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
//这里表示的是将要创建的窗口的背景
//背景需要用画刷(HBRUSH)填充 填充背景一般使用GetStockObject()函数
wcex.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_APPLICATION));
//从函数名可以知道这里加载一个图标hIcon 中的h 表示这是一个Icon的句柄
//IDI_APPLICATION 是一个宏,至于为什么是IDI_...
//因为这是一个ID 并且是Icon的ID 取Icon首字母I 连起来就是IDI_
//此函数的第二个参数一个指针参数,而IDI_APPLICATION只是一个数值,需要类型转换
//因此就使用宏MAKEINTRESOURCE() 来转换,其实就是函数转换函数,
//至于内部怎么操作的我们就不用管了
wcex.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_ARROW));
//理解如上,IDC_ARROW 表示ARROW(箭头)的Cursor(光标)的ID
wcex.hInstance = hInstance; //窗口句柄,使用从WinMain函数中传进的hInstance句柄就可以了(至于为什么后面解释)
wcex.lpfnWndProc = WindowProc; //lpfnWndProc中,lp表示长整形(l)的指针(p),WndProc 表示什么相信已经猜到了就是调用
//WndProc(...)窗口函数,窗口函数就是消息循环函数的别称罢了
//至于等号的右值函数的名称就是函数的地址
//与他要求的指针类型完全匹配
wcex.lpszClassName = _T("可视化编程");
//这里要注意了,你设计窗口是定于的是什么classname(类名),你后面创建窗口时就必要
//使用相同的名字,至于这里为什么要 _T("...") 主要是为了提高程序的移植性。当然也
//可以直接用" 可视化编程" 窗口菜单,为了简化这里我们先不要菜单了 所以设值为NULL
//就OK,其实0也可以,不过规范的编程中我们使用的是NULL 因为这是一个指针.指针为空时
//我们让他指向NULL;
wcex.lpszMenuName = NULL;
}
// 一下是消息处理函数实现代码
LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
//调用系统默认函数
break;
}
return 0;
}
好了.
我们在来理清一下 Window程序的基本框架思路:(函数必要的返回值 参数我就省略了,这样子 看起来思路比较清晰);
WinMain()
{
// 创建3钟特殊的结构的对象 以后用到
WNDCLASS wcex
HWND hwnd;
MSG msg;
// 窗口注册
Init()
{
//窗口的注册,完成一些必要的初始化;
//主要用到了 WNDCLASS wcex 这个结构个各种字段;
//以及重要的 wcex.lpfnWndProc = WindowProc;
//让系统找到 窗口消息函数
}
//注册窗口 注册失败返回0, 退出程序
if( !Register())
{
return 0;
}
//完成窗口的创建 并将返回值赋值给 hwnd
hwnd = CreateWindow(){}
if( ! hwnd) //判断是否创建成功 不成功则返回 0 退出程序
{
return 0;
}
ShowWindow(); //显示窗口
UpdateWindow(); //更新窗口
//完成消息循环
while( GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam //消息循环时 返回消息的某个字段
}
完成到这里,已经基本完成了,这篇文章主要介绍了 Window程序设计的框架,其他的就不在讨论范围了, 像一些系统函数的参数为什么这样 这些问题 只有MSDN 帮到你了
这里强烈建议 想学好的朋友 安装一个MSDN
之前有一个朋友问过我 :你这么厉害啊 函数的参数表都记住了,,我说 ,你来记吧 谁会这么无聊记参数表,其实我只是在MSDN上 查到了 然后粘贴复制的问题罢了。
最后说明 本文写给刚刚进入可视化编程学习的朋友 希望对他们有帮助,, 另本文完成得比较仓促 文中有笔误或者不妥之处就请各位路过的大牛不吝指出,并一起讨论。一起学习了。