MFC封装WinMain的原理

设计一个简单完整MFC程序,产生一个窗口。当然这不能让AppWizard自动生成。我们可以在Win32 Application工程下面这样写:
#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;                                //返回
}
};
MyApp theApp; //①建立应用程序。

设定链接MFC库,运行,即可看见一个窗口。
从上面可以看到建立一个MFC窗口很容易,只用两步:一是从CWinApp派生一个应用程序类(这里是MyApp),然后建立应用程序对象(theApp),就可以产生一个自己需要的窗口(即需要什么样就在InitInstance()里创建就行了)。
整个程序,就改写一个InitInstance()函数,创建一个对象(theApp),就是一个完整的窗口程序。

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(),因为这是程序的入口点,初始化程序的地方。
看个例子:

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

void main(){}

在上面的程序里,入口的main()函数表面上什么也不做,但程序执行了(注:实际入口函数做了一些我们可以不了解的事情),并输出了一句话(注:全局对象比main()首先运行)。WinMain()函数可以什么都不做,程序依然可以运行,但没有这个入口函数程序会报错。

那么WinMain()函数会放哪个类上面呢,请看下面程序:
#include <afxwin.h>
class MyApp : public CWinApp
{
public:
BOOL InitInstance() //②程序入点
{
AfxMessageBox("程序依然可以运行!");
return true;
}
};
MyApp theApp; //①建立应用程序。

上面并没有构造框架,而程序却可以运行了——弹出一个对话框(如果没有WinMain()函数程序会报错)。上面这样写还是为了直观起见,其实只要写两行程序:
#include <afxwin.h>
CWinApp theApp; 
//整个程序只构造一个CWinApp类对象,任可事情,程序就可以运行!

只要构造了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()函数外面运行吗?

回过头来,我们可以让WinMain()函数一条语句都不包含吗?不可以,看一下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与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函数。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值