第二次读《深入浅出MFC》比第一次轻松多了。这里记录一下,我自认为的重点的内容,包括一些经常疑惑的细节。
Win32中CALLBACK被定义为_stdcall,是一种函数调用习惯,关系到参数进入到堆栈的次序,以及处理堆栈的责任归属,其它的函数调用习惯还有_pascal和_cdecl。
Windows程序如何加载进内存?
当Windows的”外壳”(shell,例如Windows3.1的文件管理器或Windows9x的资源管理器)侦测到使用者意欲执行一个Windows程序,于是调用加载器把该程序加载,然后调用C startup code,后者再调用WinMain,开始执行程序。WinMain的四个参数由操作系统传递进来。
思考:汇编程序如何加载进内存?
窗口诞生的流程?
RegisterClass(规定外貌和行为) –>CreateWindow(根据外貌和行为创建窗口)->Show window(让窗口显示出来)
窗口的消息循环各函数的作用?
GetMessage抓取消息,TranslateMessage是为了将键盘消息转化,DispatchMessage会将消息传递给窗口函数去处理。因为消磁发生之时,os已根据当时状态,为它标明了所属窗口,而窗口所属的窗口类(结构体)又已经明白标示了窗口函数。
窗口的消息映射如何实现?
模拟Message映射
定义一个MSGMAP_ENTRY结构和一个dim宏
Struct MSGMAP_ENTRY{
UINT nMessage;
LONG (*pfn)(HWND,UINT,WPARAM,LPARAM);
}
#define dim(x) (sizeof(x)/sizeof(x[0]))第二个元素pfn是一个函数指针,以此指针所指的函数处理nMessage消息。这正是面向对象观念中的“数据”和“处理的方法”封装起来的一种具体实现。
接下来组织两个数组_messageEntries[]和_commandEntries[],把程序中欲处理的消息以及消息处理历程的关联性简历起来:
Struct MSGMAP_ENTRY _messageEntries[] =
{
WM_CREATE,OnCreate,
WM_PAINT,OnPaint,
WM_SIZE,OnSize,
WM_COMMAND,OnCommand,
WM_SETFOCUS,OnSetFocus,
WM_CLOSE,OnClose,
WM_DESTROY,OnDestroy
} 消息,消息处理程序
//command-ID与处理例程之对照表格
Struct MSGMAP_ENTRY _commandEntries=
{
IDM_ABOUT, OnAbout,
IDM_FILEOPEN,OnFileOpen,
IDM_SAVEAS,OnSaveAs,
}WM_COMMAND命令项,命令处理程序
对话框如何运行的?
对话框分两种:
令其父窗口无效,知道对话框结束的模态对话框。(模态对话框的激活和结束靠的是DialogBox和EndDialog两个API函数。)
父窗口与对话框共同运行的非模态对话框。
对话框需要两样东西:1、对话框模板,在RC文件中定义的一个对话框外貌。2、对话框函数。对话框函数,类似与窗口函数,但是它通常只处理WM_INITDIALOG和WM_COMMAND两个消息。
对话框的控件是如何与对话框沟通的?
对话框中的各个控件也都是小小窗口,各有自己的窗口函数,他们以消息与其管理者(父窗口,也就是对话框)沟通。而所有的控件传来的消息都是WM_COMMAND,再由其参数分辨哪一种控件以及哪一种通知消息(notification)。
模块定义文件.DEF是什么?
Windows程序需要一个模块定义文件,将模块名称、程序段和数据段的内存特性、模块堆大小、堆栈大小、所有callback函数名称等等记录下来。
但是在VisualC++的IDE中不许特别准备.DEF文件,因为模块定义文件中的设定值都有默认值。
空闲时间指“系统中没有任何消息等待处理”的时间。空闲时间常常产生,所以后台工作最适宜在空闲时间完成。空闲时间的处理OnIdle()。
PeekMessage和GetMessage的不同之处?--存疑
他们都是到消息队列中抓消息,如果抓不到,程序的主执行线程(一个UI执行线程)会被os挂起,当os再次回来照顾这一执行线程时,发现消息队列中仍然是空的,这时两个API函数行为就不同对了。
GetMessage:会过门不入,于是os再去照顾其他人
PeekMessage:会取回控制权(CPU的控制权),使程序得以执行一段时间,从而使消息循环进入到OnIdle函数中。
Console程序与DOS程序的差别
编写方式:Console程序,如果程序是以main为进入点,调用C ruantime 函数和“不牵扯GUI”的Win32 API函数,console窗口将成为其标准输入和输出装置(cin和cout)
DOS程序,相似,区别在于不可能调用Win32API函数。
可运行文件格式:DOS程序是所谓的MZ格式,Console程序的格式则和所有的Win32程序一样,都是所谓的PE(Portable Executable)格式。
注意:编辑时指定/MT选项,表示使用多线程版本的C runtime函数库。如果不这样做,会出现这样的链接错误:error LINK2001:unresolved external symbol _endthreadex ~_beginthreadex等,表示它找不到_endthreadex和_beginthreadex,虽然我们的程序中可能并没有调用它们,但是MFC有调用。
核心对象?
核心对象是系统的一种资源(这种说法对GDI对象也适用),系统对象一旦产生,任何应用程序都可以开启并使用该对象,系统给予核心对象一个计数值(usage count)作为管理之用。
核心对象包括一下几种:event、mutex、semaphore、file、file-mapping、process、thread,都是用CreateXXX的API函数产生,都会获得一个handle作为标识;每被使用一次,其对应的计数值就加一,核心对象的结束方式相当一致,调用CloseHandle即可。
需知handle其实是指针的指针。
一个进程的诞生与死亡过程
执行一个程序必然就产生一个进程。
a.shell调用CreateProcess激活App.exe
b.系统产生一个进程内核对象,使用计数值加1
c.系统为此进程产生4GB的地址空间
d.加载器将必要的码加载到上述地址空间中,包括App.exe的程序,资料以及所需要的dll。它们被记录在可执行文件PE格式的.idata section中。
e.系统为此进程建立一个执行线程,即主线程。执行线程才是CPU时间的分配对象。
f.系统调用C Runtime函数库的StartUp code
g.startup code调用APP程序的WinMain函数
h.APP程序开始运作
i.使用者关闭APP,退出消息循环,退出WinMain函数
j.返回到startup code
k.返回到系统,系统调用ExitProcess结束进程。
可以说,通过这种方式执行起来的所有Windows程序都是shell的子进程,父进程和子进程本来是有关系的,但是shell在调用CreateProcess时已经把这种关系简短了。
简历新进程之前,系统必须做两个核心对象,也就是进程对象和线程对象,因为在CreateProcess中会涉及到其安全属性,并且在最后一个参数的结构体指针所指的结构体中包含了这两个核心对象的handle。
一个线程自己结束自己的生命可用ExitProcess,进程想就结束另外一个进程的生命用TerminateProcess。
TerminateProcess的缺点在于一般进程结束时,系统会通知该进程所开启(所使用)的所有DLLs,但如果用TerminateProcess结束一个进程,系统不会做这件事。
一个线程的诞生与死亡
一个进程建立起来后,主线程也产生了,所以一个Windows程序一开始就有了一个线程。
调用CreateThread产生额外的线程,其完成如下工作:
1、 配置“线程对象”,其handle将成为CreateThread的返回值
2、 设定计数值为1
3、 配置线程的context
4、 保留线程的堆栈
5、 将context的堆栈指针缓存器SS和置零指针缓存起IP设定妥当。
线程结束也有两种,同上自己结束用ExitThread,结束别人的线程用TerminateThread。但是TerminateThread太过毒辣,若非必要还是少用。
_beginthreadex取代CreateThread,及_endthreadex取代ExitThread
为了保证多线程情况下的安全,C runtime函数库必须为每一个线程做一些登记工作,若非如此,C runtime函数库就不知道要为每一个线程配置一块新的内存,作为线程的区域变量用。因此,CreateThread有一个名为_beginthreadex的外包函数来进行额外的登记工作。
注意函数名称的下划线符号,它必须存在,因为这不是个标准的ANSI C runtime函数。
_beginthreadex的参数和CreateThread完全相同,但其类型已经被净化了,不再有Win32类型封装(微软希望能直接用其他平台,不需要与windows有关,但由于需要CloseHandle关闭线程,所以仍需载入windows.h头文件)。
针对win32的ExitThread也有一个对应的C runtime函数_endthreadex,只需——beingthreadex的第六个参数传回来的ID值。
线程优先级(略)