MFC基础知识

MFC终究是避不开的,那就慢慢来学习吧

 

看网上都推荐《深入浅出MFC》。结果发现并没有很容易的入门。我个人还是推荐《MFCWindows 应用程序设计》。任哲

 

一、预备知识

  代码运行机理探究:

    我们运行程序时,程序是怎么运作的呢。这一点分析清楚,有助于我们理解窗口运行机制。我们编写完代码后,点击编译,把所有文件都连接起来,形成一个可以被操作系统执行的文件。但操作系统怎么来执行我们的程序呢?它是通过调用系统调用来执行。正如我们在操作系统上所知道的,操作系统会提供我们用户接口。通过这个接口来调用我们的程序,由于语言的特性,调用我们的程序后,要有一个主函数。我们平常编写的console程序,入口是main。即控制台通过main函数进入执行我们的代码。而Windows程序则不同,它的入口函数是winmain。直观来说:Windows程序的架构是这样的:

 

Windows程序代码---------(winmain)-----------Windows操作平台-------------------硬件系统

而console程序的架构是:

console application 程序代码-------(main)----------MSDOS-------------Windows操作平台-----------硬件系统。(其中,MSDOS实质是一个运行平台,在Windows操作系统之上,与Windows程序相比,增加了一个中间层。提供了一个可视化界面。MSDOS不仅可以调用库函数,也可以调用Windows的API。)

 

Windows程序的特点:

  Windows程序除了入口为winmain的主函数,还有一个消息处理函数,两者并不是调用关系,这两个函数都是系统调用的。主函数主要进行的操作是:1.创建程序图形窗口界面。2.进入一个叫做消息循环的循环中,并在这个循环中等待用户事件的产生。当收到事件后,主函数把事件的消息传到系统。系统再次调用Windows程序的消息处理函数。循环这种操作,直到事件是一个终止程序运行的事件。

 

Windows API的特点:

  为了支持应用程序的设计和运行,Windows在系统函数中,向用户开放了一些可以由应用程序调用的函数(API)。实质上,这些API函数是一种软中断。因此称为系统调用。我们在操作系统中,也简要的学过:用户只有通过中断才能进入操作系统。

  Windows API主要分为三大类型:

    1.窗口管理函数:窗口的创建,移动,修改。

    2.图形设备函数:实现图形的绘制及操作,图形设备接口(GDI)

    3.系统服务函数:与操作系统有关的功能。

Windows开发工具:SDK,实际上就是利用Windows API。

Windows的特殊数据类型:

   1.用typedef   起的别名。

   2.特殊的数据类型:

    1.句柄。是一个结构体。用来隐藏内部的信息。句柄实质上是结构类型变量指针的再封装。避免用户直接操作指针操作的危险。虽然与指针形式相同,但不能像指针那样参与运算。

      HINSTACE句柄:应用程序实例句柄。在用户眼里程序可能就是静态的代码,操作系统中,程序是动态的过程。程序利用HINSTACE这种结构来管理这种动态行为。这个变量就代表了正在运行的一个程序。

    2.WNDCLASS。结构体。这个结构体是Windows程序创建窗口的参数。其声明如下:

typedef struct _WNDCLASS
{
    UNIT style;//指定窗口风格,通常选择style=0
    WNDPROC lpfnWndproc;//函数指针,指向消息处理函数
    int cbClsExTra;
    int cbWndExTra;
    HANDLE hInstance;//多窗口时,这个是和主窗口相同的。
    HICON hIcon;//用户使用的图标。
    HCURSOR hCursor;//窗口所使用的光标。
    HBRUSH hbrBackground;//窗口背景颜色
    LPCTSTR lpseMenuName;//窗口的菜单
    LPCTSTR lpszClassName;//窗口的类名

}WNDCLASS;

  3.Windows函数的调用。

    主要是宏定义的理解。像WINAPI  CALLBACK 等。这些属于修饰符,用来说明调用的顺序。程序被系统调用时,总要传一些参数,这些修饰符就是来说明传递参数的顺序的,因为使用堆栈来进行存储的,所以对参数顺序肯定是有要求的。比如你的main函数是应该先传递左边的参数,还是括号右边的。比较常用的就是_stdcall和_cdecl。_cdecl是默认vc++调用方式。但是,在winAPI中,都遵循_stdcall.所以在编写Windows程序时,也要声明这种修饰符。声明调用方式。WINAPI 和CALLBACK这些都是经过宏定义之后别名。其实质是_stdcall.

窗口的创建:

  步骤是先定制窗口,在注册窗口,然后是创建,最后是显示在屏幕上。

  代码结构是:

//1.定制窗口,利用结构体赋值来声明。
WNDCLASS wc;
wc.style=0;
...
//2.窗口的注册
BOOL RegisterClass (WNDCLASS & wc);
//3.窗口的创建
//利用CreateWindow()来创建,用很多参数。
HWND hwnd;
hwnd=CreateWindow("class name","title","style",x,y,w,h,fatherhandle,hMenu,hInstance,NULL)
//4.窗口的显示
BOOL ShowWindow(
    HWND hwnd,

    int nCmdshow   //窗口的显示方式
    );
BOOL UpdateWindow(HWND hwnd);

事件、消息循环和窗口函数。

  用户所提出的一个服务就是一个事件。这些事件的信息在Windows程序中构造了一个结构体用来存储这些消息。MSG。其结构是:

typedef struct tagMsg
{
    HWND hwnd;//产生消息的窗口句柄,即事件的地点
    UNIT messeage;//窗口消息的标识码,用宏定义来标识的。
    WPARAM wparam;//事件的附加消息
    LPARAM lparam;//事件的附加消息
    DWORD time;//产生的时间
    POINT pt;//发送消息时,光标所在的位置。
}MSG;

 

  系统消息的标识码一般以VM_开头。

  消息队列和消息循环:

MSG msg;
while(GetMessage(&msg,NULL,NULL,NULL)){
    TranslateMessage(&msg);//将键盘码消息转换为字符消息
    DispatchMessage(&msg);//把消息派送给系统
}

    消息派送给系统后,系统根据消息的hwnd找到窗口,然后根据窗口的lpfwinproc来找到消息处理函数,所以说消息处理函数是由系统调用的。消息处理完成后,会返回循环函数继续等待。多个消息时,会有一个消息队列进行缓存。也有不经过消息队列直接送达窗口的。

windows程序的结构:

  一个入口为winmain的主函数(包括窗口的创建,以及消息的循环)

int WINAPI WinMain(HINSTANCE hInstance,
    hINSTANCE PreInstance,
    LPSTR lpCmdbine,
    int nCmdshow){

    //窗口的创建
    //消息循环
};

 

  另一个是窗口函数(消息处理函数。)

LRESULT CALLBACK WndProc(HWND hwnd,
    UNIT messeage,
    WPARAM wparam,
    LPARAM lparam){
    //消息处理函数
}

 学到现在应该形成这样一个概念:

 

 

消息映射表的实现原理:

  在消息处理函数即窗口函数中,我们可以用 switch和case结构来进行消息处理。但为了能够进行代码重构以及编写的方便,我们可以利用消息映射表来重写。实际上就是封装一下。

  1.从函数中,我们可以发现,我们可以用一个结构体来封装消息的标识,以及消息对应的处理函数。

  

struct MSGMAP_ENTRY
{
    UNIT nmessage;
    void (*pfn)(HWND,UNIT,WPARAM,LPARAM);
};

  2.我们可以用一个结构体数组来标识窗口需要处理的消息,即可以定义一个数组。

  

    

MSGMAP_ENTRY _messageEntres[]={
    {WM_PAINT,On_Paint,},
    ...
}

  3.在窗口函数中,我们就可以利用循环来遍历。

  4.为了更进一步的完善我们的代码。可以利用宏定义来实现。因为宏定义的意思是在代码出现的位置替换掉。即可以更加简单。

//1.结构体声明 
struct MSGMAP_ENTRY
{
    UNIT nmessage;
    void (*pfn)(HWND,UNIT,WPARAM,LPARAM);
};
//2.宏定义。这个要放在开头,只不过现在是为直观
//2.1 宏定义结构体数组。为了直接在源码中利用宏定义声明消息映射表
#define DECLARE_MESSAGE_MAP() MSGMAP_ENTRY _messageEntres[];
//2.2 宏定义 消息映射表的实现
#define BEGIN_MESSAGE_MAP() MSGMAP_ENTRY _messageEntres[]={\
#define  ON_WM(messageID,msgFuc) messageID,msgFuc,\
#define END_MESSAGE_MAP()\
};
//3声明消息处理函数原型
void On_Paint(HWND,UNIT,WPARAM,LPARAM);

    在代码中,我们就可以这样添加消息处理的内容。

//消息映射表的声明
DECLARE_MESSAGE_MAP()

//消息映射表的实现
BEGIN_MESSAGE_MAP()
ON_WM(VM_Paint,On_Paint)
...//添加消息即消息处理函数
END_MESSAGE_MAP()

  消息映射表体现了设计模式中的观察者模式,用来实现分离。被观察者进行消息循环,观察者用来处理。

 

MFC入门:

  我们上面讲到的是利用WindowsAPI来进行编写应用程序的步骤,MFC实际是一个类库,利用MFC我们可以很简单的就可以编写出应用程序,因为这个类库它对很多通用的代码进行了封装。我们在利用vs创建MFC程序时,vs会自动帮我们把框架改好,我们只需要把我们需要修改和添加的代码进行封装即可。关于利用vs编写MFC程序,可以参考一下这个网址。我感觉还不错。   http://www.jizhuomi.com/catalog.asp?tags=MFC&page=4

  下面简要写一下我自己的理解:

  MFC发展至今已经是一套比较流行的框架,现行的框架结果是采用文档/视图结构。就是把以前winAPP类分开。因为现在窗口处理的任务越来越复杂。

  文档/视图结构:

    把窗口类的功能分为三个部分:1.数据存储管理部分(文档类CDocument),2.数据显示与用户交互部分(视图类CView)。3.管理窗口的状态(CFrameWnd)。

    用这三个类创建3个对象来构建一个窗口。这三个对象又由一个叫做文档模板的对象统一来创建和管理。

    单文档结构(SDI)和多文档结构(MDI)的应用程序。

   

 

    1.1.1文档类:是应用程序的数据库。是程序员定义程序数据和对这些数据进行操作的成员函数的地方。用户可以重写的两个虚函数,其中Serialize()函数比较重要。这个函数是负责从文件中读取数据,或者向文件存储数据的。

    1.1.2视图类:为框架窗口提供用户区。创建的对象时一个交互界面 。类中比较重要的两个成员函数:

      1.GetDocument():用于获取文档类对象的指针。是与文档类进行通信的通道。视图类对象必须通过这个指针来访问文档类对象中的数据。

      2.OnDraw():重画函数,每次应用窗口出现及大小发生变化时,系统会自动调用OnDraw()函数。

    1.1.3窗口框架类CFrameWnd:类中有获取文档对象和视图对象指针的函数

    1.1 文档模板类:是管理视图对象、框架窗口对象和文档对象的管理器。

      文档模板类(CDocTemplate)又派生出单文档模板类(CSingleDocTemplate)和多文档模板类(CMultiDocTemplate)。

    1.应用程序类:是上述各类对象的容器。主要功能是实现应用程序的初始化和消息循环。

应用程序各个对象之间的创建顺序:

  1.系统会创建应用程序对象。

  2.应用程序对象会在InitInstance()函数中,创建文档模板对象。

  3.创建文件模板对象时,构造函数会直接创建文档类对象,窗口框架类对象。

  4.窗口框架对象会创建视图对象。

  ****各个对象之间,通过指针相互协同。

类信息表CRuntimeClass:结构体

  实现这个类信息表的首要目的是:可以动态的创建对象。

我们用vs创建程序后,我们需要做的事:

  1.重写CWinAPP派生类中的虚函数 InitInstance();

  2.CDocument的派生类中,声明程序所需要的数据和对这些数据进行必要的操作。

  3.在CView的派生类中,编写消息处理的代码,如果需要用到文档中的数据,需用用GetDocument()函数来获取文档对象。

  4.在CView重写OnDraw()。重绘时的代码。

  5.用宏定义实现消息映射表。

 

图形:

  1.DC与GDI 。

    DC是Windows系统中在驱动层的一个抽象层,是一种设备描述表,DC为应用程序提供了画板,程序可以在DC上进行操作,再由DC与底层进行交互。

    GDI是图形设备接口,这些接口可以用来改变DC属性。

  2.CDC。和CWND类似。MFC把进行图形操作的一些操作,封装成类。利用这个类,来进行操作。HDC是进行图形操作的句柄。获得这个句柄就可以进行图形操作。

  3.CDC又派生出其他特定功能的类。

   

 

posted @ 2018-09-09 13:31  昔时  阅读(541)  评论(0编辑  收藏  举报