【转载】MINI MFC

刚开始从C++向MFC过渡的时候,发现一个HELLO就要上百行语句,面对Wizard一头N个大,一打开电脑就问自己,这东西是从哪开始的啊,WinMain哪去了,我靠了,上百行写个HELLO还找不到WinMain,找不到WinMain我从哪开始学啊我?
    再后来发现不光是WinMain,注册窗口类,消息循环,窗口函数,CreateWindow通通没有,想改个窗口标题都没地找,在一大堆书和光盘中爬行了几个月后突然发现一个手工写的纯MFC程序就16行代码,可以正常编译运行,就拿出C++编程的那点底子,不就16行吗,一天一行半个月我也通了.果然经过一翻..啊..再结合以前看过的书和光盘,今天终于大功告成,就把这个小程序给大家贴出来,另附心得,我想对新人应该有点帮助,发现错误也请行家们帮忙指出,深表谢意。如果你也是刚开始学MFC的,想互相交流的加我QQ41184836
 
注释:   本程序书写方法相当不准确,可以说十分恶劣,只是为初学理解方便
    第一步:VC6.0启动->菜单栏[文件]->新建->在工程项选择Win32 Application,工程名称: text  点击确定进入下一页面 -> 选择一个空工程 ->点击确定完成.
    第二步:进入VC++环境界面后->菜单栏[工程]->设置(Alt+F7)->选择 常规 项 ->右边第一个下拉列表框    原来默认值是"不使用MFC",把它改为"使用MFC作为静态链接库"->点确定完成
    第三步:菜单栏[文件]->新建->在文件项选择C++ source; 文件名:text ->点确定
    第四步:把下面的代码粘到新建的C++源文件中,就可以编译执行了.
 
****************代码如下***********************************
//text.cpp
 
#include
 
 
class CMyWinApp : public CWinApp
{
public:
    BOOL InitInstance();
};
 
class CMyFrameWnd : public CFrameWnd
{
public:
    CMyFrameWnd();    
};
 
 
CMyWinApp  theApp;
 
BOOL CMyWinApp::InitInstance()
{
    m_pMainWnd = new CMyFrameWnd();
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();
 
    return TRUE;
}
 
CMyFrameWnd::CMyFrameWnd()
{
    Create(NULL, "演示窗口");
}
******************************代码结束*****************************
 
 
 
***************************心得如下********************************
 
以下是对上面那个16行代码小程序的逐行解释,也是一个MFC程序启动,运行,结束的全过程.
 
 
 
#include
    //调用MFC类库的头文件,所有MFC程序都要用到这个头文件。
 
两个派生类的声明就不说了,下面是正文:
 
CMyWinApp theApp;
    MSDN对基于MFC类库的编程规定:每个程序必要有一个而且仅有一个全局的
         从CWinApp类的派生的类产生的对象。
        
 
    ①CWinApp::CWinApp
        既然从类中产生了一个对象,自然其构造函数马上就执行了,
                  应该是CMyWinApp::CMyWinApp才对但因为我们在定义CMyWinApp的时
                  候没有重写构造函数,所以执行其父类的构造函数WinApp::CWinApp,
        这个函数在MFC类库早已经写好了;因为CWinApp本身就是MFC库中
                  的一个类;这个函数运行的结果是CWinApp中的成员变量被初始化,
                  所有变量在内存中有了地址,并有初值。
        因为CMyWinApp是CWinApp的子类,这一动作也就使theApp对象的成
                  员变量有了初值,一切也因此开始了;
        theApp这个对象在面对对象的程序的设计中表示:整个应用程序,也
                  就是说整个程序也是一个对象。 
 
 回复人:黯月精灵 回复时间:2005-10-15 22:07:00 
WinMain(...)
    在程序代码中看不到这一行,在我开始学MFC的时候,曾无数次问过自己这个
         问题,WinMain哪去了?它怎么知道什么时候运行啊?
    郁闷了好一阵子,最后看MSND的回答非常干脆,我靠了,答案如下:就一句话
    在基于MFC类库的编程中:在用户自定义的全局对象(变量)和theApp这个全
         局对象产生后MFC将自动提供并运行一个WinMain函数
    哪来的?是MFC事先写好了放在那的,什么时候运行?全局对象定义完了就运
         行,...
    这个WinMain函数是MFC已经写好了的,为便于理解内容简写如下
    
    ********MFC自动提供的WinMain程序代码简略如下***************
    int CALLBACK WinMain(...)
    {
        CWinApp* pApp = AfxGetApp();
 
        AfxWinInit(...);
 
        pApp->InitApplication();
 
        pApp->InitInstance();
 
        pApp->Run();
 
        return ...
    }    
    *************************代码结束*************************
 
 
    ***********WinMain程序代码说明如下***********************
 
int CALLBACK WinMain(...)  //实际MFC原文中不是这么写的,是用宏写的,
                           //把宏打开还是这个德行;
{
    CWinApp* pApp = AfxGetApp();  //MFC类库中的一个全局函数,功能是取得theApp
                                  //指针,只要有了指针我们就可以利用theApp中
                                  //大量的现成的成员函数为所欲为了;
 
    ②AfxWinInit(...);            //MFC的全局函数,其功能是初始化MFC内部操
                                  //作,这里的初始化似乎和AFX的关系很大;
                  //其实到底这个函数初始了什么东西我也搞不
                                  //懂,也不想搞懂,太难了但有意思的是这个函
                                  //数是每个MFC程序必须要走的一步;
                  //注释:MFC中不仅有类,还有大量的全局函数,
                                  //以Afx开头的都是这也就是说用MFC编程并不是
                                 //纯粹的面向对象的程序设计,
                                 //书上又说这种混血的设计更灵活
 
    ③pApp->InitApplication();   //看看得到指针后调用的第一个成员函数是什么,
                                 //因在theApp的类中没有重写这个函数所以最后执
                                 //行的是CWinApp.InitApplication()
                 //在基于MFC的编程中这个函数是不用重写的,直
                                //接用现成的就行了它的作用也是初始化MFC内部操
                                //作,这里的初始化和DOC模板关系很近说深了我也
                                //不知道它初始了什么东西,但也是必走的一步;
 
    ④pApp->InitInstance();          //这是取得theApp指针后调用的第二个成员函
                                 //数,因为这个函数在代码中重写了所以最后执
                                //行的是自己写的代码CMyWinApp.InitInstance()
                 //这个函数必须是要重写的,因为它的父类WinApp
                                //中的这个函数什么也没写,是空的MFC提供的
                                //WinMain函数调用这个函数目的很单纯,就是让用
                                //户自己创建一个程序主窗口                          //我们的编程也是从这个函数开始写第一行代码
//本程序写的代码是 m_pMainWnd = new CMyFrameWnd(); 
//有人问m_pMainWnd是哪来的,它是CWinAPP类的成员变量
//在这个程序的第①步,它被初始化了,当然初值为NULL,
//这里我们给它赋新值了.前缀 m_ 表示类的成员变量 p 表示指针 
//就是指向本应用程序主窗口的指针
//这里我们 new 的是 CFrameWnd,  表示建立一个简单的窗口
//也可以   new 一个 CDialog,    表示建立一个简单的对话框
//也可以new 一个 CSingleDoctemplate 表示建立一个单文档应用程序(AppWizard)
//也可以new 一个 CMultiDoctemplate  表示建立一个多文档应用程序(AppWizard)
//回到本程序,既然new了CMyFrameWnd(),老规矩执行构造函数
//CMyFrameWnd::CMyFrameWnd()这个函数我们也重写了,代码在上面
//实际上我们自己一共就重写了二个函数,这是最后一个,内容见下一步;
//注意:  从这时已经从WinMain跳出来了,
//转而执行CFrameWnd类的成员函数Create
 
 
    ⑤Create(NULL, "演示窗口");     //为什么要偏偏要写这行代码呢,
                                    //就不能写别的?答案还是在MSDN里找,
                     //MSDN上是这样写的:要产生一个CFrameWnd类
                                      的实体分二步走
//第一步从类中定义一个对象,这步我们做了:m_pMainWnd = new CMyFrameWnd();
//第二步利用Create函数创建一个与这个对象关联的窗口,就这么简单,
//创建一个与这个对象关联的窗口有三种做法:
//㈠Create(NULL, "演示窗口");
//㈡CreateEx(NULL,NULL,"演示窗口",WS_OVERLAPPEDWINDOW,rectDefault,NULL,NULL);
//㈢::CreateWindowEx(......);
//第三种又回归SDK了,我们这里不讨论,这里只讨论前二种
//其实和大家想的一样,前二种方法最后调用的一定还是::CreateWindowEx(......)
//继续,这行代码实际上调用的是CFrameWnd::Create
//这个函数也是MFC早已写好了的,我们只是调用了它,并给它传递了参数        
//这一行是本程序最复杂的一行代码,
//传说中的注册窗口类和窗口函数都是在这一行完成的
//因为这个类成员函数又调用了(也可以说封装)了好多API函数,
//打开MFC的关于这个成员函数的定义,发现它最后又调用了另一个成员CWnd::CreateEx
//你是不是要问你在哪打开这个函数定义部分的,我没那本事,书上写的,呵呵
//另外千万不要试着重写Create函数,后果很严重啊,用现成的就行
//你是不是又要问不改写我怎么体现自己想要的东西啊,别急,Create函数有8个参数
//后6个参数有默认值,本程序省略了,你可以给它传递各式的参数满足你的要求
//我们继续,通过这个函数的调用我们的第⑥步也就出来了
  
 回复人:黯月精灵 回复时间:2005-10-15 22:08:00 
⑥CreateEx(NULL,NULL,"演示窗口",
               WS_OVERLAPPEDWINDOW,rectDefault,NULL,NULL,NULL);
                          
 
//恩,这是CWnd类的一个成员函数
//大家可以试一下,把上面那个16行小程序的最后一行Create(NULL,"演示窗口")删掉
//用这行得代替,这个程序一样能编译成功,而且结果一样
//CreateEx 和 Create这两个函数参数个数一样,功能也一样,其实这里MFC想的很周到
//如果你不想传递窗口扩展样式这个参数你就用Create,省事,因为写2个参数就可以了
//如果你想传递窗口扩展样式这个参数直接用这个函数也行,
//当然用Create也是可以的,只不过参数就不能用默认值了
//这个差别和CreateWindow与CreateWindowEx的差别有点相像
//其实关于CreateEx 和 Create这两个函数你只要查一下MSDN就全明白了
//下面把CreateEx函数在MFC定义部分打开,天也就亮了
                          
 
 
**************CreateEx函数在MFC中的定义部分简写********************
 
    BOOL CWnd::CreateEx( DWORD dwExStyle, LPCTSTR lpszClassName,
                         LPCTSTR lpszWindowName,DWORD dwStyle, int x, 
                         int y, int nWidth, int nHeight,HWND hWndParent, 
                         HMENU nIDorHMenu, LPVOID lpParam) 
 
    //从参数个数上来说就比CreateWindowEx少一个,
    //少的那个参数下面有一行语句会补上,这样就可以调用CreateWindowEx修成正果了
{
CREATESTRUCT cs;              //对初学者来说这个结构体还有和WNDCLASS
                              //这两个结构体太重要了,入门必修
 
cs.dwExStyle = dwExStyle;
cs.lpszClassName = lpszClassName;
cs.lpszWindowName = lpszWindowName;
cs.dwStyle = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hWndParent = hWndParent;
cs.nIDorHMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle(); //少的那个参数就是这个:程序实例的句柄,
                                       //为什么少,MFC类封装了这个句柄
                  //现在要取回这个句柄就得依靠全局函数来完成,
cs.lpParam = lpParam;                 //到此为止,我们熟悉的API函数
                                      //CreateWindowEx的12个参数一个也不少了
 
PreCreateWindow(cs);                   //这一行最复杂,一会说
 
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClassName, 
                             cs.lpszWindowName, cs.dwStyle, cs.x, 
                             cs.y, cs.cx, cs.cy, cs.hWndParent,
                             cs.nIDorHMenu,cs.hInstance,cs.lpParam)
                  
                                       //最后一步,窗口终于产生了
}
 
*************代码结束,以下为这个函数的说明部分*********************
 
//说句废话,我们仍然在第⑥步讨论
//这个函数展开后主要做了五件事
//㈠定义了一个CREATESTRUCT型的结构体并把CreateEx函数接收的参数放到这个结构体中
//㈡额外地取得了实例句柄这个数据也放到这个结构体中
//㈢把这个结构体用别名传参的方式传给函数PreCreateWindow(cs)
//㈣这个结构体被PreCreateWindow函数处理后成为API函数CreateWindowEx的12个参数
//㈤调用API函数CreateWindowEx产生窗口
//多说一句,如果你还不太清楚别名传参,请不要急于接触MFC,很危险,
//不把(虚函数)和(传参)这两个知识点搞透就学习MFC的结果只有一个
//那就是你会最终会选择放弃MFC,实际上MFC一点也没有扩展C++,看不懂的都是宏
//下面进行第⑦步,    PreCreateWindow(cs)
  
 回复人:黯月精灵 回复时间:2005-10-15 22:08:00 
⑦PreCreateWindow(cs)         
 
//看看这个函数的声明:BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
//从这可以看出它接收的是一个别名参数,别名传参太霸道了
//首先说PreCreateWindow这个成员函数是CWnd类的一个虚函数
//包括CWnd类自己,每一个CWnd的派生类对这个函数都重新做了定义
//实际上我们自己从CWnd类子类中派出自己的类时也要重写这个函数,当然也可以不重写
//当这个函数PreCreateWindow(cs)接收到结构体cs以后,会自动检测cs.lpszClassName
//cs.lpszClassName这个数据表示窗口类的名称,本程序传递的为空,就是没有窗口类
//当这个函数检测到cs.lpszClassName为空时,也就是当它发现程序没有窗口类时
//它会自动真充一个WNDCLASS结构,并注册之,那它根据什么提供什么样的窗口类呢
//前面说了每个CWnd类及其子类都有自己定义的PreCreateWindow函数
//那就是说CWnd是一种,CFrameWnd又是一种,还有:CMDIFrameWnd,CMDIChildWnd,CView
//这5种类都有自己默认的窗口类,如果你未手动提供窗口类,它就用默认的
//另外说一下这个窗口类,很多书上都是这么写,这对初学者是一种考验
//窗口类不是MFC类库中的类,是一个数据结构WNDCLASS的名称,WNDCLASS就是窗口类
//个人以为WNDCLASS应该写成(窗口"类")而不是(窗口类)        
//继续: 那么本程序调用的PreCreateWindow是CWnd的,还是CFrameWnd的成员函数呢?
//要知道PreCreateWindow在CWnd和CFrameWnd这两个类中都有定义,
//而且是CWnd::CreateEx这个函数启动的PreCreateWindow(cs),那是CWnd类????
//不是,正确答案是调用CFrameWnd类PreCreateWindow(cs),为什么?
//对象决定this指针,只有已经存在的对象才有this指针存在
//本程序已经存在的对象是m_pMainWnd = new CMyFrameWnd(),
//this指针指向CMyFrameWnd,但本程序CMyFrameWnd没有重新定义PreCreateWindow
//所以最后执行CMyFrameWnd的父类CFrameWnd的成员函数
//那么这个默认的窗口类也就是CFrameWnd提供的                
//到这步了,你可能还问,窗口类是找到了,那窗口函数在哪,在哪处理消息啊?
//恩,是个问题,在SDK编程中,窗口函数的指针是在窗口类指定的,
//然后再编写窗口函数处理消息,当然这个窗口函数的名字要和窗口类中指定的一致.
//在本程序那就看看CFrameWnd提供的默认的窗口类是怎么写的?
//WNDCLASS wc.lpfnWndProc = DefWindowProc;
//DefWindowProc 熟悉吧,SDK编程窗口函数最后一行都是这个
//就是说它提供的窗口函数就把是全部消息都送去系统默认处理
//恩,不错,窗口函数也有了,不过你可能又要问
//我编程不能全指望默认处理啊,那菜单消息,按钮鼠标键盘自定义消息怎么办?
//用MFC提供的功能强大的消息图 MESSAGE_MAP                
//消息图这东西的原理十分复杂,在我看来能编出这东西的是神一级的人物
//看过N遍,至今没搞懂但有一点,原理复杂,用起来却十分简单
//想让哪个窗口能处理消息,就在这个窗口声明部分加一句DECLARE_MESSAGE_MAP()
//在这个窗口的定义部分加下面二句
//BEGIN_MESSAGE_MAP(本身类名, 其父类名)
//END_MESSAGE_MAP()    
//再在这两句中间把所有要处理的消息声明一下,就可以编写声明完的消息处理函数了//你可能又要问了,处理自定义消息是有了,但有时要改变一些系统消息的处理
//没有窗口函数那还不是什么也做不了?
//可以这样,有一个强大的API函数GetWindowLong,它可以改变系统原来指定的窗口函数
//之后你自己再自定义一个窗口函数(呵,又回归SDK了),之后你想做什么就随你了
//到目前为止WinMain,注册窗口类,窗口函数,CreateWindow全找到了
//就差一个消息循环最后说.                        
//还有一问题就是这个小程序没写几句代码窗口自己就出来了,那么
//在不重写本程序的前提下如何修改窗口样式,窗口标题,窗口大小和添加菜单
//很简单,重写CMyFrameWnd这个类的虚函数PreCreateWindow
//重写归重写,重写时还是先调用其父类CFrameWnd的PreCreateWindow,
//不然的话就啥都没了,然后重新给cs.dwStyle  cs.lpszWindowName等赋值
//想改哪个就给哪个变量赋新值,至于为什么这样能行,(别名传参)太霸道了
//稍复杂一点的是改变窗口图标,指针形状和窗口背景
//因为这三个变量是在窗口类中指定的,办法如下:
//还是要重写CMyFrameWnd这个类的虚函数PreCreateWindow
//不过这回要改变系统默认指定的窗口类,就是这个cs.lpszClassName
//要先自己声明并填充一个WNDCLASS,要填满,一个不能少
//特别注意窗口函数项wndcls.lpfnWndProc=::DefWindowProc(要加上::)
//再给自己定义好的WNDCLASS起一个新名字,并把它赋给cs.lpszClassName 
//因为是你自己定义的WNDCLASS,想改成啥样就能改成啥样(又是回到SDK了)
//之所以说这个笨方法是因为好理解,在实际要改的时候有一个API函数,一行就搞定了
扯的太远了,第⑧步是什么?
 
    ⑧::CreateWindowEx(...)          //那个MFC提供默认窗口类的窗口诞生了
                 //窗口创建以后这个API函数并没有再调用其它函
                                //数,就回到CMyWinApp::InitInstance()
 
    ⑨m_pMainWnd->ShowWindow(m_nCmdShow);
      m_pMainWnd->UpdateWindow();
 
                   //到此CMyWinApp::InitInstance()函数也执行完毕,
                             //回到WinMain函数
 
    ⑩pApp->Run();              //这就是系统提供的消息循环,不停的等待消息,
                                 //翻译消息,再把消息发送到窗口函数
//直到收到WM_QUIT这个消息时,循环结束,整个程序也结束了
//另外说一句,这个Run()同时还提供了时间片函数
  【转载】一篇有助于理解MFC的文章

 

 程序运行结果,上面左图是没有view的mfc,右图是只有frame的mfc(即上程序运行结果)。别误以为view是那个菜单栏,实际上菜单栏还是属于frame。
这篇文章结合孙鑫书的3.2节来看,就有对MFC有一个大体的认识了。
附1 win32简单程序:
#include <windows.h>
#include <stdio.h>
 
LRESULT CALLBACK WinSunProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);
 
int WINAPI WinMain(
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int nCmdShow              // show state
)
{
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc;
wndcls.lpszClassName="sunxin2006";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls);//注册自己的窗口类
 
HWND hwnd;//创建,显示,更新窗口
hwnd=CreateWindow("sunxin2006","http://www.sunxin.org",WS_OVERLAPPEDWINDOW,
0,0,600,400,NULL,NULL,hInstance,NULL);
 
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
 
MSG msg;
while(GetMessage(&msg,NULL,0,0))//消息循环
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
 
LRESULT CALLBACK WinSunProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
switch(uMsg)
{
case WM_CHAR:
break;
case WM_LBUTTONDOWN:
break;
case WM_PAINT:
break;
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}

 

转载于:https://www.cnblogs.com/yurius/archive/2013/01/11/2857172.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值