MFC封装WinMain原理

参考:MFC程序入口函数WinMain封装原理分析-百度经验 (baidu.com)

个人进行了代码整理及一些修正


启动vs, 新建一个名为myMfcTest的空白工程(新建--->项目--->Visual C++--->空项目),
1. 可以看到myMfcTest工程没有源码文件, 接下来, 我们新建一个源文件和头文件tstMfcEntry.h和tstMfcEntry.cpp, 
我们的目标是设计一个简单完整MFC程序, 产生一个窗口, 但是这里不能让AppWizard自动生成相关代码!
在文件tstMfcEntry.h中输入以下代码:

#pragma once
#include <afxwin.h>
class MyApp : public CWinApp
{
public:
    BOOL InitInstance()                          // ②程序入点
    {
        CFrameWnd *Frame = new CFrameWnd();      // 构造框架
        m_pMainWnd = Frame; // 将m_pMainWnd设定为Frame;
        Frame->Create(NULL, "最简单的窗口");     // 建立框架
        Frame->ShowWindow(SW_SHOW);              // 显示框架
        return true;                             // 返回
    }
};


在文件tstMfcEntry.cpp中输入以下代码:

#include "tstMfcEntry.h"
MyApp theApp; //①建立应用程序。


2. 3
点击"生成"菜单下面的"生成解决方案", 发现没有成功并且产生了一堆的警告和错误

3. 4
分析错误信息, 得出可能是没有设定链接MFC库, 那么怎么在空白工程上来设定链接MFC库呢 ?
(1)."配置属性"--->"常规"--->"项目默认值", 改为"在静态库中使用 MFC";      

(2)."配置属性"--->"链接器"--->"系统"--->"子系统", 改为"窗口 (/SUBSYSTEM:WINDOWS)";(这个好像不需要?)
再点击"应用"和"确定", 再次生成解决方案生成成功.


4. 5
从上面可以看到建立一个MFC窗口很容易, 只用两步:
一是从CWinApp派生一个应用程序类(这里是MyApp),
然后建立应用程序对象(theApp), 就可以产生一个自己需要的窗口(即需要什么样就在InitInstance()里创建就行了);
整个程序, 就改写一个InitInstance()函数, 创建一个对象(theApp), 就是一个完整的窗口程序。
5. 6
MFC黑箱操作帮我们插入了代码,它插入的实际上是每次编写窗口程序必须的通用的代码。每次视窗编程都要写WinMain()函数,都要有注册窗口,产生窗口,消息循环,回调函数……等等.
看一下上面两个类的父子关系(箭头代表派生) :
CObject->CCmdTarget->CWinThread->CWinApp->自己的重写了InitInstance()的应用程序类。
CObject->CCmdTarget->CWnd->CFrameWnd
看到层次关系图后,可以开始写MFC类库了。
按照上面层次结构, 我们可以写以下六个类(为了直观, 省去了构造函数和析构函数)。
class CObiect{};     //MFC类的基类
class CCmdTarget : public CObject{}
class CWinThread : public CCmdTarget{}
class CWinApp : public CWinThread{}
class CWnd : public CCmdTarget{}
class CFrameWnd : public CWnd{}
CWinApp类或者它的基类CCmdTarget里面应该
有一个虚函数virtual BOOL InitInstance(),
因为这是程序的入口点, 初始化程序的地方。
这里都是讲解了一些mfc框架的相关类及关系为后面做准备。
6. 7
下面一起看一段程序代码 :

#include <iostream.h>class test
{
public:
    test(){ 
        cout << "请改变对main()函数的看法!" << endl; 
    }
};
test test1;
void main()
{
}


入口的main()函数表面上什么也不做, 但程序执行了, 为什么 ?
实际入口函数做了一些我们可以不了解的事情, 最后程序输出了一句话!
这里要注意 : 全局对象test1是比main()首先运行。

      通过以上代码我们, 程序在入口函数main以前, 还有先要执行的代码。
        7. 8
    win下面有两个入口程序 : main和WinMain;
如果WinMain()函数也是什么都不做, 但是程序仍然可以运行, 不过, 没有这个入口函数程序会报错, 那么WinMain()函数会放哪个类上面呢, 请看下面程序:

#include <afxwin.h>
class MyApp : public CWinApp
{
public:    
    BOOL InitInstance() // ②程序入点    
    {
        AfxMessageBox("程序依然可以运行!");        
        return true;    
    }
};
MyApp theApp;          // ①建立应用程序。


上面程序代码并没有构造框架,
而程序却可以运行了------弹出了一个对话框;
我们理解如果没有WinMain()函数程序会报错, 这里并没有报错!
上面这样写还是为了直观起见, 其实可以更简洁, 只要写两行程序
#include <afxwin.h>
CWinApp theApp;
整个程序只构造一个CWinApp类对象, 没有做任何事情, 程序就可以运行了!
8. 9
上面的代码段只构造了CWinApp对象, 似乎就可以执行WinMain()函数。
猜测WinMain()函数可能是在CWinApp类或它的基类中 ?
想想在编写C++程序时, 不可能在一个类中包含入口函数!
WinMain()是由系统调用, 跟我们平时程序自身调用的函数有着本质的区别。
可以暂时简单想象成, 当CWinApp对象构造完的时候, WinMain()跟着执行。
我们可以看出, 大部分的"通用代码"都可以放到CWinApp类中,
那么它又是怎样运行起来的呢?
为什么构造了CWinApp类对象就"自动"执行那么多东西 ?
CWinApp类对象构造之后, 它会"自动"执行自己的构造函数。
那么我们可以把想要"自动"执行的代码放到CWinApp类的构造函数中!
那么CWinApp类可能这样设计(这里先不管正确与否) :

class CWinApp : public CWinThead{
public:      
virtual BOOL InitInstance();  //解释过的程序的入点
CWinApp::CWinApp(){   //构造函数    
    WinMain();         
    Create();    //设计、创建、更新显示窗口      
    Run();     //消息循环      //  
    }
};


WinMain()函数在这里好象真的一点用处都没有,
 并且能这样被调用吗

(WinMain()不是普通的函数,  它要肩负着初始化应用程序, 包括全局变量的初始化,是由系统而不是程序本身调用的, WinMain()返回之后, 程序就结束了,进程撤消)。
再看Create()函数, 它能确定设计什么样的窗口, 创建什么样的窗口吗 ?
如果能在CWinApp的构造函数里确定的话,我们以后设计MFC程序时窗口就一个样, 变得写程序变有必要。再看Run()函数, 它能在WinMain()函数外面运行吗 ?

10
WinMain() 函数的四个参数:WinMain(HINSTANCE, HINSTANCE, LPSTR, int)    其中第一个参数指向一个实例句柄,在设计WNDCLASS时一定要指定实例句柄。
                     窗口编程, 肯定要设计窗口类。
                     所以, WinMain()再简单也要这样写 :
                     int WinMain(HINSTANCE hinst,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
                 {
                         hInstance = hinst
                     }

既然实例句柄要等到程序开始执行才能知道,
那么用于创建窗口的Create()函数也要在WinMain()内部才能执行
[因为如果等到WinMain()执行完毕后,程序结束,进程撤消,
当然Create()也不可能创建窗口]    那么Run()(消息循环)放在那里执行好呢?
众所周知,消息循环就是相同的那么几句代码,
但也不要企图把它放在WinMain()函数之外执行。
在WinMain()函数里面,程序要象以下这样写
WinMain(……){ ……窗口类对象执行创建窗口函数…………程序类对象执行消息循环函数…… }
对于WinMain(), 封装时是不可以把它封装到CWinApp类里面,
但由于WinMain()的不变性(或者说有规律可循),
MFC完全有能力在构造CWinApp类对象的时候, 完成那几行代码。
那么MFC到底是怎么做到的呢, 请看下面的分析 ?
10. 11
表面上MFC与SDK编程截然不同,
但实质上MFC只是用类的形式封装了SDK函数, 封装之后,
在WinMain()函数中只需要几行代码, 就可以完成一个窗口程序。
我们也由此知道了应如何去封装应用程序类(CWinApp)和
主框架窗口类(CFrameWnd)。
下面开始设计这两个类 :
为了简单起见,忽略这两个类的基类和派生类的编写!
代码如下 :

#include <windows.h>
HINSTANCE hInstance;
class CFrameWnd {
    HWND hwnd;
public:
    CFrameWnd();   //也可以在这里调用Create()
    virtual ~CFrameWnd();
    int Create();    //类就留意这一个函数就行了!
    BOOL ShowWnd();
};
class CWinApp1 {
public:
    CFrameWnd* m_pMainWnd;           //在真正的MFC里面,它是CWnd指针,但这里由于不写CWnd类,只要把它写成CFrameWnd指针
    CWinApp1* m_pCurrentWinApp;      //指向应用程序对象本身
    CWinApp1();
    virtual ~CWinApp1();
    virtual BOOL InitInstance();     //MFC原本是必须重载的函数,最重要的函数
    virtual BOOL Run();             //消息循环
};
CFrameWnd::CFrameWnd(){}
CFrameWnd::~CFrameWnd(){}
int CFrameWnd::Create()   //封装创建窗口代码
{
    WNDCLASS wndcls;
    wndcls.style = 0;
    wndcls.cbClsExtra = 0;
    wndcls.cbWndExtra = 0;
    wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);
    wndcls.hIcon = LoadIcon(NULL, IDC_ARROW);
    wndcls.hInstance = hInstance; wndcls.lpfnWndProc = DefWindowProc; //默认窗口过程函数。大家可以想象成MFC通用的窗口过程。
    wndcls.lpszClassName = "窗口类名";
    wndcls.lpszMenuName = NULL;
    RegisterClass(&wndcls);
    hwnd = CreateWindow("窗口类名", "窗口实例标题名", WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, NULL, NULL, hInstance, NULL);
    return 0;
}
BOOL CFrameWnd::ShowWnd()//显示更新窗口
{
    ShowWindow(hwnd, SW_SHOWNORMAL);
    UpdateWindow(hwnd);
    return 0;
}
/
CWinApp1::CWinApp1(){
    m_pCurrentWinApp = this;
}
CWinApp1::~CWinApp1()
{
}
//InitInstance()函数,MFC中要为CWinApp的派生类改写,这里为方便理解,把它放在CWinApp类里面完成,只要记住真正的MFC在派生类改写此函数就行了。
BOOL CWinApp1::InitInstance(){
    m_pMainWnd = new CFrameWnd;
    m_pMainWnd->Create();
    m_pMainWnd->ShowWnd();
    return 0;
}
BOOL CWinApp1::Run()     //封装消息循环
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
CWinApp1 theApp;   //应用程序对象(全局)
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
    hInstance = hinst;
    CWinApp1* pApp = theApp.m_pCurrentWinApp;   //真正的MFC要写一个全局函数AfxGetApp,以获取CWinApp指针。
    pApp->InitInstance();
    pApp->Run();
    return 0;
}//    CFrameWnd类的Create(), CWinApp类的InitInstance()和Run()。

在此特别要说明的是InitInstance(), 真正的MFC中, 那是我们跟据自己构造窗口的需要, 自己改写这个函数。
大家可以看到, 封装了上面两个类以后, 在入口函数WinMain中就写几行代码, 就可以产生一个窗口程序。
在MFC中, 因为WinMain函数就是固定的那么几行代码,
所以MFC绝对可以帮我们自动完成(MFC的特长就是帮我们完成有规律的代码), 所以创建MFC应用程序时, 看不到WinMain函数。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值