何苦做程序?!

业精于勤,荒于嬉;行成于思,毁于随! I believe , I can flying! 勿在浮砂筑高台!

C++博客 首页 新随笔 联系 聚合 管理
  4 Posts :: 1 Stories :: 14 Comments :: 0 Trackbacks

2006年3月11日 #

 孙鑫VC讲座笔记--WINDOWS程序内部运行原理

声明:

        本人最近也在看孙老师的视频,为了加强理解,所以想一些读书笔记。但是在CSDN上一搜索,发现已经有朋友做了相关笔记。根据面向对象的“继承”观点,为了解决劳动力,所以我打算在他们的基础上添加、修改。应该不涉及著作权什么的东东吧?!

        我在BLOG.CSDN.NET/LEWISLAU上搜索了下 ,有两位朋写了相关笔记(而且都是一样的)。不知道谁才是原作者,所以列出两位BLOG地址:

http://blog.csdn.net/hhitjsj021                                http://blog.csdn.net/d007879

以后我会在前辈的基础上修改、发文!呵呵!继承嘛!

 

 

 

 

windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。

 

MSG Structure

--------------------------------------------------------------------------------

The MSG structure contains message information from a thread's message queue.

Syntax

typedef struct {
    HWND hwnd;   //指示一个窗口的句柄,改消息和那个窗口相关联。
    UINT message;  //具体的消息,用无符号整形表示
    WPARAM wParam; //关于消息的附加参数
    LPARAM lParam; //同上
    DWORD time; //32位整数,表示消息被投递出去的时间
    POINT pt; //表示光标位置
} MSG, *PMSG;

 句柄,资源的标识,操作系统通过句柄指到资源。常见的句柄有图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序句柄(HINSTANCE)
 
例如:当按下按键会发送出WM_CHAR消息   通过消息的附加参数,保存对应的ASCII码,即可知道按下的是那个键。

 

 


消息队列:
每个应用程序OS都为它建立一个消息队列,消息队列是个先进先出的缓冲区,其中每个元素都是一个消息,OS将生成的每个消息按先后顺序放进消息队列中,应用程序总是取走当前消息队列中的第一条消息,应用程序取走消息后便知道用户的操作和程序的状态,然后对其处理即消息响应,消息响应通过编码实现。

使用VC编程除了良好的C基础外还需要掌握两方面:
一,消息本身。不同消息所代表的用户操作和应用程序的状态。
二,对于某个特定的消息来说,要让OS执行某个特定的功能去响应消息。


Window程序入口:
int WINAPI WinMain(
  HINSTANCE hInstance,  // 当前事例句柄。
  HINSTANCE hPrevInstance,  // 先前事例句柄。
  LPSTR lpCmdLine,      // 命令行指针
  int nCmdShow          // (窗口)显示的状态
);
说明:WinMain函数是Windows程序入口点函数,由OS调用,当OS启动应用程序的时候,winmain函数的参数由OS传递的。

 

创建一个完整的窗口需要经过下面四个操作步骤:
一,设计一个窗口类;如:WNDCLASS wndcls;
二,注册窗口类;    如:RegisterClass(&wndcls);
三,创建窗口;      如:CreateWindow(),CreateWindowEX();
四,显示及更新窗口。如:ShowWindow(),UpdateWindow();
说明:创建窗口的时候一定要基于已经注册的窗口类.

 

 

Windows提供的窗口类:
typedef struct  WNDCLASS {
    UINT    style;        //窗口的类型
    WNDPROC lpfnWndProc;  //窗口过程函数指针(回调函数)
    int     cbClsExtra; //窗口类附加字节,为该类窗口所共享。通常0。
    int     cbWndExtra; //窗口附加字节。通常设为0。
    HANDLE  hInstance;  //当前应用程序事例句柄。
    HICON   hIcon;      //图标句柄 LoadIcon();
    HCURSOR hCursor;    //光标句柄 LoadCursor();
    HBRUSH  hbrBackground; //画刷句柄 (HBRUSH)GetStockObject();
    LPCTSTR lpszMenuName;  //菜单名字
    LPCTSTR lpszClassName; //类的名字
} WNDCLASS,*PWNDCLASS;


窗口类型style为一个变量,该变量每一位对应着一种特性。对应为1时,有该种特性;对应为0时,无该种特性。为了方便记忆,用一些宏对应一些特征,通过取反(~)和相与(&)可以取消一些特性。  通常设置为"CS_HREDRAW | CS_VREDRAW"表示垂直重绘和水平重绘。

HICON可以由LoadIcon 赋值(它有两个参数HINSTANCE和LPCTSTR,通常第一个参数为空,只对第二个参数赋值,即图标的ID)
HCURSOR同HICON
HBRUSH 使用GetStockObject函数,它可以用来获取笔、画刷、字符、调试板的画刷。使用时要用HBRUSH做一直强制转化。因为GetStockObject返回值和HBRUSH不同。

窗口类注册:
ATOM RegisterClass(
  CONST WNDCLASS *lpWndClass   // address of structure with class
                              // data
);
//注意,是使用地址符

 


创建窗口:
HWND CreateWindow(
  LPCTSTR lpClassName,  //注册窗口类名,用引号
  LPCTSTR lpWindowName, //窗口标题,用引号
  DWORD dwStyle,        //窗口类型(风格)通常为(WS_OVERLAPPEDWINDOW)
  int x,                // 窗口X坐标
  int y,                // 窗口X坐标
  int nWidth,           // 宽度
  int nHeight,          // 高度
  HWND hWndParent,      // 指向父窗口的句柄
  HMENU hMenu,          // 菜单句柄
  HANDLE hInstance,     // 当前实例的句柄,由WINMAIN传递
  LPVOID lpParam        // WM_CREATE附加参数传入指针
);
创建窗口的时候会发送WM_CREATE消息


显示和更新窗口窗口:
BOOL ShowWindow(
  HWND hWnd,     // handle to window
  int nCmdShow   // show state of window
);
BOOL UpdateWindow(
  HWND hWnd   // handle of window  送出WM_PAINT消息
);


消息循环
MSG msg;
while(GetMessage(&msg,...))    //从消息队列中取出一条消息
{
 TranslateMessage(&msg); //进行消息(如键盘消息)转化。转化过程不会影响原消息,只会创建新的消息。
 DispatchMessage(&msg); //分派消息到窗口的回调函数处理,(OS调用窗口回调函数进行处理)。
}

BOOL GetMessage(
  LPMSG lpMsg,         // 消息结构体变量
  HWND hWnd,           // 句柄,那个一个窗口?为NULL则为所有窗口句柄
  UINT wMsgFilterMin,  // 最小消息值,为0时返回所有消息
  UINT wMsgFilterMax   // 最大消息值
);

 

回调原理:当应用程序受到给某个窗口的消息时,就应调用某一函数来处理这条消息。这一消息有操作系统自动完成。

注:函数名可以用以表示函数代码的首地址(函数指针),额外数据通常为0。


窗口过程函数(回调函数)原型:
LRESULT CALLBACK WindowProc(  //这里WindowProc是个代号名字。
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);
说明:两种函数调用约定(__stdcall 和 __cdecl):
#define CALLBACK    __stdcall
//__stdcall 标准调用预定,是PASCAL 调用约定,象DELPHI使用的就是标准调用约定
#define WINAPIV     __cdecl 
// __cdecl 是C 语言形式的调用约定。
主要区别:函数参数传递顺序 和 对堆栈的清除上。
问题:除了那些可变参数的函数调用外,其余的一般都是__stdcall约定。但 C/C++编译默然的是__cdecl约定。所以如果在VC等环境中调用__stdcall约定的函数,必须要在函数声明的时加上 __stdcall 修饰符,以便对这个函数的调用是使用__stdcall约定(如使用DELPHI编写的DLL时候)。
(VC中可通过这途径修改:project|settings..|c/c++|...)
在窗口过程函数中通过一组switch语句来对消息进行处理:
如:
LRESULT CALLBACK WindowProc( 
  HWND hwnd,
  UINT uMsg,
  WPARAM wParam,
  LPARAM lParam  
)
{
    switch(uMsg)
    {
 case WM_PAINT:
  ...
  break;
 case ...
  break;
 case WM_CLOSE:
  //DestroyWindow(hwnd);
   //销毁窗口,并发送WM_DESTROY消息。
  break;
 case WM_DESTROY:
  //PostQuitMessage(0);
  //发送WM_QUIT消息到消息队列中,请求终止。
         //GetMessage()取到WM_QUIT消息后,返回0,退出消息循                //   环,从而终止应用程序。
  break;
 default:
  return DefWindowProc(hwnd,uMsg,wParam,lParam);
 //用缺省的窗口过程处理我们不感兴趣的消息(其它消息)。
 //这是必须的。
    }//switch
 return 0;
}//WindowProc


 响应WM_DESTROY,调用PostQuitMessage(int)结束进程。它会投递一个WM_QUIT消息对消息队列中。当消息循环的GetMessage取到WM_QUIT消息,则返回0,程序结束。
 另外对于不感兴趣的消息要景象缺省的处理,使用DefWindowProc()内为窗口的参数。

 


关于DC句柄获取:
a)使用BeginPaint(),EndPaint()对。注意只能在响应WM_PAINT消息时使用。
b)使用GetDc(),ReleaseDC()对。注意他们不能在响应WM_PAINT中使用

posted @ 2006-03-11 11:19 lewislau 阿木 阅读(2650) | 评论 (1)编辑 收藏

 【原创】我在成都当程序员

题记:前几天出去感受一下,感触颇深,发觉自己真的很逊。现在回到清静的校园,自己觉得应该更加努力。社会真的不是我们想的那样,工作也不像学习一样轻松。只希望各位朋友在走出校门,迈向社会的过程中,一切顺利!

 在成都待了一个星期,确切的说只有六天。在这六天里面,我完完全全是一个打工的,一个社会上的人,一个程序员。听上去多牛B,程序员啊,这个可是我梦寐以求的职业啊,可是事实上一切都不是我想的那样辉煌。我这几天脑海里反复浮现一句话,程序员的的确确是十分辛苦的职业。
 我所在的公司是一家游戏外挂公司,专门做游戏外挂。从社会的角度来说,这种“勾搭”只能暗箱操作,如今却还明目张胆的“开张营业”,确实搞笑;从技术的角度来说,游戏外挂,利用HOOK拦截数据包、解密、修改、封包,样样都是安全方面的比较牛B的技术,在没有进这个公司之前,我没有想过一年以内会接触到这些底层的技术。所有说公司对我来说,有些失望、但是也有一些期望。
 以前在绵阳(家乡)的一些小IT公司也玩儿过,但是这次是独自在外,什么问题都要自己解决,所有从各个方面来说,都是自己的一些新的尝试。不过还好的是,这次我是和朋友一切去成都“实习”。呵呵,不过我比他好的就是,他在电信设计规划院实习三个月,没有实习工资,三个月转正;而我实习一个月实习工资一千,一个月后转正,根据情况而定工资,表现的好的话在3K左右(听上去挺吸引人的,但是真的很累)。
 我们到成都只用了2个小时找了一处住房,合租,350一月,只有一张床和一个书桌(天啦,成都的房价真TMD的吓死人)。唯一比较欣慰的是我们住在川大外面,步行到川大只要5分钟,原先设想的是每天下班去川大教室去上自习,可是现实往往与设想有很大的差距,每天回到家累的简直书都不想看。(白天在公司至少看6个小时以上的文档,对这那个15寸的CRT,我简直万分的不爽,公司里面50多人就我一个用CRT显示器,我靠,欺负我??!)每晚和朋友在川大转悠两圈就回去睡觉,这种生活,让我回忆起两岁的时候家里还不景气的状态。汗!
 言归正转,还是说工作的事儿,每天我7:40起床,解决完所有问题大约8:20到达车站,搭上19路,顺利的话8:55作用能到达高升桥(公司所在地),唉,不禁汗颜,每天要白白浪费大约90分钟时间搭车,在成都人看来,这还算近的(大城市的是比我们这些农民洋盘)。9点左右到达公司,不出意外的话只有我旁边的仁兄比我早到。(公司老大考虑技术部经常加班,所以允许技术部的兄弟们9:30上班,这个还够人性化。)这位仁兄待会要重点介绍,公司里面就跟他混的最熟,特牛B。说到加班,我突然想起才去公司和技术部老大谈的时候,老大特别交代,“如果你加班的话,晚上可以打车回家。拿上小票,第二天回公司报销。”我汗,原来这个公司加班就跟喝水一样随便。
 我才到公司,我的工作就是不停的看文档,中文的、英文的都看。(我都怀疑我居然能看懂英文)老大给我的任务很简单,就一句话:“利用HOOK抓取程序的数据包。”他还特别交代,“不需要你解密数据包,抓取就可以”看上去降低了很大难度哦,但是有两个人能证明这个我这个公司不是我现在的水平能够接受的。第一个冯SIR,他说这个东西对于我来说,非常非常具有挑战,希望我能创造奇迹。(汗,奇迹!)第二个是李SIR,回学校后我给他讲述我的“作业”,他说,哦,这个东西和我师兄研究生的毕业设计很像。(我再一次汗颜)我承认自己的喜欢有挑战的工作,但是我还是很有自知之明,确切的说我很自量力。
 我每天的工作就是看文档,看的我要疯,HOOK属于WINDOWS的核心技术,趋于操作系统底层,用于拦截操作系统发出的消息。对于我来说决不是简单的事儿,所以不敢懈怠,我每天就GOOGLE,MSDN上搜寻关于它的一切,中文的英文的都看,要疯!看看我旁边的家伙,现在就来说说他,他是测试部的,他的工作就是不停的耍各种各样的游戏(居然耍游戏真的能找到工作,我简直为我们学校的兄弟们感到欣慰),有的时候网速比较卡,他没法耍游戏,就只能委屈一下看小说,这样的工作让我羡慕的不行。偶尔看到他再不断的抄录房产信息,就问他是否要买房。他说,恩。我就纳闷了,看上去年纪轻轻的就想房子,公司耍游戏的难不成拿8K一个月?继续问到,“你工作几年了哦?都有钱买房了?”“我工作了都五年了,我一个月1500,一年存不到5000。靠自己的话都不知道什么时候才能买哦、。其实是我父母给钱。”终于发现一个比我还强的人了。事后,我给我爸妈打电话汇报我的近况,闲聊到此事儿,只听电话那边一片寂静,我爸耍句“你给我说这些,难不成你还要我们给你准备钱买房子?”(上帝作证,我绝对没那么想)。
 每天吃午饭大约半小时不面对那可恶的CRT,其余时间就逃不过浩劫,现在用到笔记本是爽哦,至少不是CRT的。公司里面基本上都要玩儿游戏,还有人被盗过帐号。(我们都是干地下生意的,还被人家黑吃黑?!)我就想,写个木马对于我们公司的几个高手来说绝对是小菜一碟,但是自己的帐号被盗了还是没办法,说不定就是自己的木马盗的自己的帐号。
 公司还有一个女程序员,佩服啊。今天也是三八妇女节,向牛B的女程序员致敬!
 每天下午六点,其实那个时候我都可以离开了,但是技术部所有同僚加班(除了我,因为我是新来的,还做不了事儿。)所以我也不好走的太快,慢慢腾腾的磨到六点一刻才离开公司,回家咯。
 上面所有行文,也许你还没感觉到辛苦,因为有些细节的事儿再说说:
 我每天上班搭乘公车,大约一共要站90分钟,绝对有少无多。上班还好,人并不多;我大概描述下我下班的情况,我第一天下班等了两辆19路后换成77路,结果多步行了四十分钟的样子,到家的时候都快20:30了,还没吃饭。后来我搭19路,大概情况是,本来是前门透币上车的,我只能从后门上,上车后贴在车门,途中路过要下车的我还要从后门下来让别人下车。我靠,这下感觉到毛大爷真不对,啥子人多力量大,瞎扯!
 回到住的地方,吃过饭的时间是最难打发的。以前在家熬夜,也不可能睡的太早,大概每天就十一点的样子睡,期间的时间就不知道如何处理?!一切在家时候就玩儿电脑,现在连个电视都看不到,感觉自己都与外界隔绝了一样。每天就到川大的散散步步,然后回住的地方躺在床上,发几条短信,打几个电话,便早早的睡去。这个是让我精神上最难以忍受的地方。我靠,我是IT人哦?信息时代哦?!这到底是什么见鬼的生活,我最受不了就是这一点。不过还好,我是和朋友一起出来的,晚上还可以闲聊几句,不过想想以后要是一个人出门在外,我估计自己早晚要疯。
 晚上,书也不想看,要是以后还是这个样子,我估计这辈子就只能当一辈子的垃圾程序员了。
 有的时候我想,程序员的确非常辛苦,自己选择这条道路对不对。后来想了想,努力做好每一件事儿,总有一天我相信自己的能力得到锻炼以后,我相信态度决定一切。
 唉,现在回到学校了,真的很多感悟,大家还是一起努力吧!
 现在感觉做程序真的很累,而且付出和收入显然不是成正比的,现在有点上了贼船又无法回头,仔细想想又有点相追求库克船长那样传奇的人生,矛盾!

posted @ 2006-03-11 11:18 lewislau 阿木 阅读(939) | 评论 (8)编辑 收藏

2006年2月18日 #

 MFC程序设计
             之来龙去脉
 题记:前些日子一直想写这个东西,做了一个开头放在我的BLOG上(但是名为<MFC框架程序WINMAIN函数分析>),到后来就没有再管了,其实那只是冰山一角.具体MFC是怎么运行的,还是没有交待清楚,虽然自己的BLOG很少有人光顾,但是本着做事儿就要做到底的心态,继续完成该文。
 说明:1、本文作者在VS2003中跟踪代码,此代码为VS2003中拷贝,使用MFC7。
       2、不同框架的MFC程序由所不同,本文以单文档为例。
          3、本文读者需要有一定的SDK的基础,不需要太多,至少知道它的基本框架和来龙去脉即可!
          4、文章只想起到说明作用,所以代码会有一些删除。
 学MFC,竟然还不知道MFC的MAIN函数在什么地方?怎么运行的?实在不高明。
 看过候捷(JJHOU)老师的《深入浅出MFC》的,对它一定很熟悉。呵呵,本文是献给没有看过那本书,但是又很希望学习MFC程序设计的朋友的。(没有看过那本书的朋友还不赶快去买?)其实本文,主要是对《深入浅出MFC》第六章的一个总结和补充罢了!(本文有该书不同的地方,也有一些笔者自己的见解!)
 言归正传。
 假如你用AppWizard一步一步NEXT下来,然后在CLASSVIEW中去找寻WINMAIN函数,那么你只有失望。MFC最大的特点是什么?封装!MFC的确封装的太好了,以至于很多想学习MFC的人都望而却步。闲话少说,还是继续我们今天的话题,MAIN函数!实话告诉你吧,即使你搜索所有的MFC生成的文件,都无法发现WINMAIN的字眼,那么它就近在什么地方呢?
 我相信你已经想到,MAIN函数应该在主要的应用程序文件中。难道是“您定义的程序名.cpp”这个文件?不错就是它。再Crtl+F一下,看有没有我们要找的WINMAIN函数?看来你又要失望了,但是你注意有这样一句:
 
/////////////////////////////////////////////////////////////////////////////
// The one and only CMyApp object

 CMyApp theApp;   //本人建立的工程名为My。
 
 
 是不是很特别,再注意一下那句注释“The one and only CMyApp object”,每个应用程序有且只用一个CMyApp对象。我想你应该想到了,WinMain函数每个程序也只能有一个,那么这个全局对象跟WinMain函数肯定有莫大的关系?没错,相信你的直觉。
 特别注意:深晓C++细节的人一定知道,全局对象优先于MAIN函数执行的道理。如果你不知道也没关系,那么我在这里告诉你:“全局对象优先于MIAN函数执行,且构建于栈中,切记,切记!”
 现在,我们该深入WinMain运行机制了,确切的说,应该是MFC的机制!
 首先,看看MFC的库文件把,它能给我们带来许多惊喜。(vc6的相应的目录是\Microsoft Visual Studio\VC98\MFC\SRC;VC7相应的目录是\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\src\mfc)
 现在我们就从这个全局下手,开始今天的旅途。
 CMyApp theApp;
 此时,系统会执行CMyApp的父类(CWinApp)构造函数,再执行CMyApp的构造函数。(先有老爹,再有儿子!),此时就会调用CWinApp的构造函数。
 
 CWinApp的构造函数(在VC提供的MFC代码中以“文中的一个字或词组”的方式查询关键字,此时打开APPCORE.CPP,以下使用相同搜索方式,不再复述。)找到以下内容:
 CWinApp::CWinApp(LPCTSTR lpszAppName)
 {
  if (lpszAppName != NULL)
   m_pszAppName = _tcsdup(lpszAppName);
  else
   m_pszAppName = NULL;
 
  // initialize CWinThread state
  AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
  AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
  ASSERT(AfxGetThread() == NULL);
  pThreadState->m_pCurrentWinThread = this;
  ASSERT(AfxGetThread() == this);
  m_hThread = ::GetCurrentThread();
  m_nThreadID = ::GetCurrentThreadId();
 
  // initialize CWinApp state
  ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
  pModuleState->m_pCurrentWinApp = this;
  ASSERT(AfxGetApp() == this);
 ... ...
 }
 OK,就到这里就可以了,仔细看上面代码,它已经完成了应用程序线程额的启动,它给予了我们程序的生命。现在请注意:
        pThreadState->m_pCurrentWinThread = this;
 pModuleState->m_pCurrentWinApp = this;
     这两行代码其实都是做的一件事儿。
     这段代码的意思是,获得了CMyApp的全局对象的this指针。(此时你肯定要疑问,为什么是CMyApp的指针?this目前是在CWinApp中啊?   对此我的答案是,可是你是由CMyApp的对象引发的CWinApp的构造啊!!)这个指针可非一般的人物,稍后我们的很多工作都要靠它完成。
     CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置和初始值。
  构造完父类,现在构造子类。可是我们看到,AppWizard给我们的子类里它什么也没做?是的,这一切都听从你的安排!
    CMyApp::CMyApp()
    {
 // TODO: add construction code here,
 // Place all significant initialization in InitInstance
 }
 
 
 
  接下来就是今天的主角儿了,搜索关键字“WinMain”,出现很多文件。别急,因为现在我们应该先看看WinMain的声明。打开appmodul.cpp:

     _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
 {
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
 }
 
这里_tWinMain是为了支持UNICODE而命名的一个宏,真正起作用的是AfxWinMain,注意看看它的参数,是不是和SDK的WinMain函数一样?
 现在再搜索下AfxWinMain,其实在winmain.cpp中:
 
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
{
 ASSERT(hPrevInstance == NULL);

 int nReturnCode = -1;
 CWinThread* pThread = AfxGetThread();
 CWinApp* pApp = AfxGetApp();

 // AFX internal initialization
 if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
  goto InitFailure;

 // App global initializations (rare)
 if (pApp != NULL && !pApp->InitApplication())
  goto InitFailure;

 // Perform specific initializations
 if (!pThread->InitInstance())
 {
  if (pThread->m_pMainWnd != NULL)
  {
   TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
   pThread->m_pMainWnd->DestroyWindow();
  }
  nReturnCode = pThread->ExitInstance();
  goto InitFailure;
 }
 nReturnCode = pThread->Run();
... ...
}
 此段代码注意五个细节:
 CWinApp* pApp = AfxGetApp();
 意为获得对象指针,其实就是刚才那个THIS。不记得了?指向CMyApp的那个!还值得注意的是,Afx意是全局的,随时你都可以调用它。(AFX就是MFC开发小组的开发代号,意为Application Framework 传说X只是为了好看,没实在意思?!)
 if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
 AfxWinInit完成了线程的初始化和窗框类的注册。具体参看appinit.cpp中的定义。
 if (pApp != NULL && !pApp->InitApplication())
 其实pApp和pThread是同一个指针,都是指向CMyApp的指针,这里因为CMyApp中没有定义InitApplication,实际上就调用的CWinApp::InitApplication(),完成了MFC的内容管理。
 if (!pThread->InitInstance())
 因为CMyApp中改写了它,所以调用CMyApp中的,其实它也是初始化工作。此时也完成了默认窗口类的定义。假如你熟悉SDK编程的话,一定不会忘记窗口类的设计、注册、创建、现实及更新的步骤,此时MFC以为你设计好了默认的窗口类。
 现在你不禁要疑问,InitApplication()和InitInstance()有何不同?
 答案是,假如你执行一个程序,于是两个函数都会被调用;当你在不关闭前一个程序的前提下,再执行一个程序,那么就只执行后一个函数。
 nReturnCode = pThread->Run();
 这个一步骤在《深入浅出MFC》中被成为程序的活水源头,在我看来它就是你开车踩油门的步骤。待会我们会具体阐述!
 
 在设计窗口类以后,就应该是注册,MFC自动调用(跳转到)AfxEndDeferRegisterClass(WINCORE.CPP中),为你注册了五个窗口类,分别是:AfxWnd,AfxCreateBar,AfxMDIFrame,AfxFrameOrView,AfxOleControl以上窗口类MFC将自动转化成独立无二的类名,供其调用。
 在窗口的注册以后,就应该是窗口的创建工作,此时会调用CFrameWnd::Create(),该代码位于WINFRM.Cpp中
BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
 LPCTSTR lpszWindowName,
 DWORD dwStyle,
 const RECT& rect,
 CWnd* pParentWnd,
 LPCTSTR lpszMenuName,
 DWORD dwExStyle,
 CCreateContext* pContext)
{
 HMENU hMenu = NULL;
 if (lpszMenuName != NULL)
 {
  // load in a menu that will get destroyed when window gets destroyed
  HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
  if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
  {
   TRACE(traceAppMsg, 0, "Warning: failed to load menu for CFrameWnd.\n");
   PostNcDestroy();            // perhaps delete the C++ object
   return FALSE;
  }
 }

 m_strTitle = lpszWindowName;    // save title for later

 if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
  rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
  pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
 {
  TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd.\n");
  if (hMenu != NULL)
   DestroyMenu(hMenu);
  return FALSE;
 }

 return TRUE;
}
 
 其中完成了窗口的创建工作,里面还涉及扩展风格的调用CreateEx,具体细节请参看MSDN。
 
 此时你不禁要问,我们的事儿都让MFC做完了?工业化生产出来的窗口都是千篇一律啊,我要有我自己的风格!
 别急,MFC给用户提供了一个修改窗口设计的机会那就是:PreCreateWindow(CREATESTRUCT& cs) 你在MSDN中查询一下CREATESTRUCT这个结构体,你会发现它和我们的CreateWindow几乎是一模一样,这个就是MFC留给你修改窗口的一个机会。在PreCreateWindow时,会跳到CWnd::PreCreateWindow,里面有一个宏:AfxDeferRegisterClass,它的作用是:如果该窗口类没有被注册,那么就注册它;如果注册了,就什么也不管!
 窗口类的设计、注册、创建都已经完成,现在只剩下更新和显示了。这些工作都交由 CMyApp::InitInstance()完成:
  m_pMainWnd->ShowWindow(SW_SHOW);
  m_pMainWnd->UpdateWindow();
 现在if (!pThread->InitInstance())的工作已经完成,按照MAIN函数的内容,接下来该:nReturnCode = pThread->Run()了
 此时应该调用CMyApp的Run()函数,但是在CMyApp类中,根本没有声明或定义这样一个函数,根据多态性的原来,指针迁升,指向CWinApp::Run(),其代码位于APPCORE.CPP中:
 
 int CWinApp::Run()
 {
  if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
  {
   // Not launched /Embedding or /Automation, but has no main window!
   TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
   AfxPostQuitMessage(0);
  }
  return CWinThread::Run();
 }
 
 最后你会发现,它由调用了一个CWinThread::Run(),此时你就看不到CWinThread::Run()的代码了(至少笔者没有找到,因为微软只提供了部分MFC代码。)但是你可以在MSDN中找到CWinThread::Run()的描述:
 Run 控制线程的函数。包含消息泵。一般不重写。
 再具体点就是:
 Run acquires and dispatches Windows messages until the application receives a WM_QUIT message. If the thread's message queue currently contains no messages, Run calls OnIdle to perform idle-time processing. Incoming messages go to the PreTranslateMessage member function for special processing and then to the Windows function TranslateMessage for standard keyboard translation. Finally, the DispatchMessage Windows function is called.
 Run is rarely overridden, but you can override it to implement special behavior.
 This member function is used only in user-interface threads.
 原来它把消息循环包装了一下,在MFC中称为消息映射(message map)的东西!至于消息映射的具体细节本人会另写文章说明!
 OK,MFC不再神秘,掌握了它的来龙去脉,再看其他的MFC书籍的时候,就知道我该怎么做?为什么我要这样做?起到了知其然又知其所以然的效果,这就是我所追求的技术境界。
 
 

posted @ 2006-02-18 12:04 lewislau 阿木 阅读(3998) | 评论 (3)编辑 收藏

2006年2月1日 #

修炼之道
    首先声明我不是一个“人才”,我是一个垃圾,不过我还在苦心修炼成为所谓的“人才”!在我“修炼”过程中顿有所悟,写些随笔,随便涂鸦之笔。
    但凡世间万物,无一能够逃脱一个“道”字!我不是道教的FANS,但是我还是对其所谓的“道”,存有敬意!可惜小生修行太浅,还说不出一个所以然出来。可是,每每回想以前自己走过的点点滴滴,若有所悟?!
    每次看书学习技术的时候,总觉得少了些什么?!
    从现在开始我应该每天问自己:“技术进步了吗?!思想呢?文化呢?”
    技术,是大家都非常想追求的。思想是可以交流的。文化呢?不要说不知道文化为何物?中华五千年的传统文化,各位都修炼的如何?!我知道自己已经缺失了这一课程,所以从现在开始我要努力补上。
    随便涂鸦之笔,只是提醒各位,技术之路漫长而遥远,就算你走到尽头也不一定称的上“合格的人才”。
    我们经常说“提高知识文化”,彷佛大家没有深刻的体味这句话:知识是知识,文化是文化,两者是不相同的!
    所以在此提醒各位“在键盘上耕耘未来的人”,在你修炼技术的同时,别忘了文化修为的提高,当然思想也是很重要的!
posted @ 2006-02-01 23:00 lewislau 阿木 阅读(452) | 评论 (1)编辑 收藏

仅列出标题