《Visual C++技术内幕》读书笔记

Visual C++技术内幕

David J. Kruglinski

 
屋檐下的水滴-- 读书笔记系列

第一章     Microsoft WindowVisual C++

1.   Windows操作系统运行程序时,它首先调用程序中的WinMain函数,该函数用来完成某些特殊的任务,其中最重要的任务就是创建该应用程序的主窗口。基于MS-DOS的程序和Windows的程序之间的一个最根本的差别,就在于前者是通过调用操作系统的功能来获得用户输入,而后者则是通过操作系统发送的消息来处理用户输入。

2.   许多Windows消息都经过了严格的定义,并适用于所有的程序。所有的消息都有两个32位的参数,可以保存如光标位置、键盘码这样的信息。在用户进行菜单选择或者对话框按钮单击等操作时,系统又会发送WM_COMMAND命令消息给适当的窗口。

3.   许多MS-DOS程序都直接往视频存储区或打印机端口输送数据,其不利之处是必须提供相应的驱动程序。Windows(已经提供了各种驱动程序)提供一个称为图形设备接口GUI的抽象接口,程序可以通过调用GUI函数和硬件打交道,而这些函数会自动参考被称为设备环境DC的数据结构。Windows会自动将设备环境结构映射到相应的物理设备,并提供正确的IO指令。

4.   进行Windows程序设计时,可用一些特定的格式将有用的数据存储在资源文件中,这样,连接起就可以把C++的二进制代码和二进制资源文件结合起来生成可执行文件。

5.   Windows允许动态连接,即一些特定结构的库可以在运行过程中被装入和连接,并且多个程序可共享同一个动态连接库,这样可大大节省内存和磁盘空间。同时,动态连接库还可以大大提供程序的模块灵活性,因为我们可以单独编译和调试动态连接库。

6.   Visual C++包括两套完整的Windows应用程序开发系统。它还包括ActiveX模板库ATL,可用来开发在Internet上使用的ActiveX控件。ATL编程既不是Win32C编程,也不是MFC编程。ATL是一个完全独立于MFC的工具,用它可以建立ActiveX控件。可用MFC建立ActiveX控件,也可用ATL建立ActiveX控件,但ATL控件相对来说又小又快,适合在Internet上使用。

7.   以下是Visual C++应用程序的大致创建过程:

8.   make文件保存了编译器和连接器的参数选项,还表述了所有源文件之间的关系。Make程序首先读取make文件,然后激活编译器、汇编器、资源编译器和连接器以便产生最后的输出,通常是可执行文件。

9.   项目project是一些相互关联的文件的集合,这些文件经过编译、连接,然后组合在一起形成可执行的Windows应用程序或者DLL。项目文件不但指定了所有文件之间的依赖关系,而且还定义了编译和连接选项。

10.以下是Developer Studio会建立的一些中间文件及描述:

File Extension

Description

APS

Supports ResourceView

BSC

Browser information file

CLW

Supports ClassWizard

DEP

Dependency file

DSP

Project file*

DSW

Workspace file*

MAK

External makefile

NCB

Supports ClassView

OPT

Holds workspace configuration

PLG

Builds log file

11.每一个项目通常都有一个文本格式的资源描述RC文件,用来描述项目的菜单、对话框、字符串以及热键资源。在资源编辑器以外对RC文件进行编辑是不提倡的。资源编辑器也可以处理EXEDLL文件,因此可以用剪贴搬来窃取资源,如其它程序的位图和图标等。

12.Visual ++资源编译器从资源编辑器读取一个ASCII RC文件,并生成一个二进制的RES文件给连接器。

13.连接器读入由C/C++编译器产生的OBJ文件、资源编译器产生的RES文件,同时处理MFCLIB库文件、运行时库代码及Windows代码,然后产生项目的EXE文件。增量连接选项incremental link option可以在源程序变化不大的情况下减少执行的时间。MFC头文件中包含的#pragma语句(特殊的编译指令)已指定了所要求的库文件。

14.调试器和Developer Studio紧密配合可保证端点被保存在磁盘上。为了能够对程序进行调试,在创建程序时,必须设置相应的编译器和连接器选项,以便产生相应的可调试信息。

2002-7-14

第二章     Microsoft基本类库应用程序框架

1.   类库是一个可以在应用程序中使用的相互关联的C++类的集合。应用程序框架是一种类库的超集,它定义了程序的结构。

2.   按照惯例,MFC库类名用大写字母“C”打头。

3.   Windows总是要求每个应用程序都有WinMain函数,它被隐藏在应用程序框架内部。程序运行时,Windows会自动调用应用程序框架内部的WinMain函数,该函数会去查找该应用程序的全局构造对象,该对象是由CWinApp基类所派生出来的类的对象。WinMain通过该全局对象调用虚拟函数InitInstance,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。

4.   MFC库应用程序框架没有采用虚拟函数来处理消息,它通过一些宏来将特定的消息映射到派生类中相应的成员函数上。这样就避免了使用大的vtbl,并且能够在处理常规Windows消息的同时处理各种应用程序的命令消息。这种体制也允许某些非窗口类来控制命令消息。

5.   Document-View结构是应用程序的核心,它是在Smalltalk环境下的Model/View/Controller类的结构上发展出来的,其主要作用是将数据从用户对数据的观察中分离出来,其最大好处是允许对同一数据有多种视图。

6.   派生文档类一般用来完成对文档对象数据的读写工作,有关显示FileOpenFileSave对话框,打开、关闭和读写文件的这些工作,大都由应用程序来完成。视图基类通常表示一个包含于框架窗口中的窗口,而派生视图类则常用来和文档类相联系,负责应用程序的显示和打印机I/O

7.   千万不要认为文档对象仅仅是与一次就要全部读进内存的磁盘文件相关联,若文档是一个真正的数据库的话,就需要有选择地对某些文档类的成员函数进行重载。

2002-7-15

第三章     从“Hello, world!”着手学习AppWizard

1.   资源对于基于Windows的应用程序来说是非常重要的,可以利用ResourceView来所见即所得地开发应用程序的资源。

2.   从用户的角度来看,SDI应用程序只有一个窗口,每次只能读取一个文档;MDI应用程序则可以拥有和不同文档相对应的多个“子窗口”。

3.   选择Tools菜单中的Source Browser可以浏览应用程序。要使生效,可从Project菜单选择Settings,在C/C++选项卡中选择Generate Browse Info复选框,并在Browse Info选项卡中选择Build Browse Info File复选框。

4.   OnDrawCView类中的一个虚拟成员函数,每次视窗需要被重新绘制时,应用程序框架都会自动调用该函数。但是,如果程序中的某个函数修改了数据,则它必须通过调用视图所继承的Invalidate或者InvalidateRect成员函数来通知Windows发出WM_PAINT消息以便触发对OnDraw的调用。

5.   Windows不允许直接访问显示硬件,而必须通过和窗口相关联的“设备环境”跟显示硬件进行通讯。MFC中,设备环境是由C++CDC类对象来表示的,可以调用CDC的很多成员函数来完成各种各样的绘图操作。

6.   窗口类中真正的虚拟函数在MFC库中不到万不得以是不用的。OnDraw函数不及要支持屏幕输出还要支持打印机输出,也就是说OnPaintOnPrint都会调用到OnDraw,于是同样的绘图代码既可以用于屏幕输出,还可以用于打印机输出。

7.   rc文件包含了应用程序的Windows资源,它包含语句#include “afxres.h”以及#include “afxres.rc”用以把适合于所有应用程序的一些通用MFC资源包含进来,其中包括字符串、图形按钮、打印及OLE所需要的一些元素。若使用MFC库的共享DLL版本,则通用资源保存在MFC.DLL中。Rc文件还包含语句#include “resource.h”

8.   如果用资源编辑器来增加常量,那么新增的内容会立刻出现在项目相应的resource.h中,文件首先定义三个重要的常量:IDR_MAINFRAME用于标示菜单、图标、字符串列表和加速键表,IDR_***TYPE(星号为项目名)用于标示默认文档图表,IDD_ABOUTBOX用于标示About对话框。从View菜单中选择Resource Symbols可以看到这三个常量。

9.   可以在Project Settings对话框的General选项卡中改变Debug以及Release输出文件和中间文件的目录。

10.  如果设置了Enable Tracing复选框标记,工具TRACER会在/Microsoft Visual Studio/VC98/MFC/SRC下的Afx.ini文件的[Diagnostics]部分插入语句TraceEnable = 1

11.  Page 38有和预编译头文件有关的论述。

2002-7-16

第四章     基本事件处理、映射模式和滚动视图

1.   有两种情况可以优化Windows的绘制过程。第一,通过InvalidateRectWindows只更新无效矩形内部的像素;第二,OnDraw函数可以调用CDC的成员函数GetClipBox得到无效矩形的大小,只更新该区域内的像素。OnDraw不仅要对窗口内所有的绘制进行处理,而且还要适应无效矩形的限制。

2.   CRectCPointCSizeWindowsRECTPOINTSIZE结构派生。CRect 定义了重载操作符LPRECT()LPCRECT()CRect::PtInRect以及CRgn::PtInRegion都是用来测试一个点是否在指定区域内。CRgn::CreateEllipticRgn以及CRgn::CreateEllipticRgn都可用来创建一个矩形区域。

3.   MFC库应用程序框架有一个适用于大多数窗口类型的简单窗口类和窗口过程函数。该函数根据参数传过来的窗口句柄,在MFC的句柄映射表handle map中查找到对应的C++窗口对象指针。然后,该窗口过程函数用MFC运行时类系统来决定窗口对象的C++类。下一步,从消息分发映射函数生成的静态表中找到消息处理函数,再用正确的窗口对象调用消息处理函数。

4.   以显示像素为绘图单位的坐标称为设备坐标,而当前映射模式下的坐标则称为逻辑坐标。映射模式也就是坐标系,用以和设备环境相联系。还有称为物理坐标的坐标系。

5.   Windows提供了一组非常重要的固定比例映射模式,其x轴向右,y轴向上。它们的唯一差别在于实际的比例因子。其中,MM_TWIPS常常用于打印机,1磅相当于20twip单位。Windows提供了两种可变比例映射模式MM_ISOTROPIC和,允许该变它们比例因子和坐标原点。在MM_ISOTROPIC下,纵横比总是1:1,但在MM_ANISOTROPIC下,xy的比例因子可以独立变化。

6.   在设置了设备环境的映射模式以及相应的参数后,CDCLptoDPDptoLP函数就可用来在逻辑坐标和设备坐标之间进行转换。至于物理坐标和逻辑坐标之间的转换则时程序员的事情。

7.   CDC的成员函数都以逻辑坐标作为其参数,CWnd的成员函数都以设备坐标作为其参数;所有选中测试hit-test操作都应该考虑设备坐标,区域定义应采用设备坐标,某些像CRect::PtInRect之类的函数只有在采用设备坐标参数时才保证有正确的结果;需要长期使用的值应该用逻辑坐标或物理坐标来保存。

8.   应用程序框架在调用CView::OnDraw之前调用CView::OnPrepareDC虚拟函数,后者体内比前者体内适合设置映射模式。其它需要设置正确映射模式的的消息控制函数,必须包含对后者的调用。

9.   CScrollView支持滚动条的滚动,但不支持键盘的滚动操作,不过,在其中实现键盘滚动也很容易。Windows本身没有将滚动条和窗口联系在一起,这正是CScrollView类要解决的。CScrollView类能通过处理滚动条发送过来的WM_HSCROLLWM_VSCROLL消息而将视扣在窗口中滚动。

10.窗口比所见到的视口要大。通过CWndScrollWindowSetViewportOrg函数,CScrollView类允许将视口原点移动到任何位置。

11.CView的虚拟函数OnInitialUpdate很重要,是视图窗口完全建立之后框架调用的第一个函数,框架在第一次调用OnDraw前会调用该函数,因此它是设置滚动视图逻辑尺寸和映射模式最合适的地方。可以通过调用CScrollView::SetScrollSize函数来设置这些参数,CScrollView::OnPrepareDC会自动根据传给SetScrollSize的第一个参数来设置映射模式。

12.Windows发送WM_KEYDOWNWM_KEYUP消息时用的是虚拟键盘码,在它们到达窗口之前会被翻译。按下一个ANSI字符键,则翻译函数将检查键盘的Shift键状态,然后发送一个WM_CHAR消息,并附带正常的键码。光标键和功能键没有码,它们不被翻译。因此,处理字符则映射WM_CHAR消息,处理其他按键则映射WM_KEYDOWN消息。MFC会提供字符码或虚拟键盘码作为消息控制函数的参数。

13.WM_CREATEWindows发送给视图的第一个消息,在框架调用Create函数时发送该消息,此时窗口创建还未完成,窗口不可见,故那些依赖于窗口处于完全激活状态的Windows函数不应出现在OnCreate中,而应在OnIntialUpdate中。

14.WM_CLOSE在用户选择关闭窗口或父窗口被关闭时发送,可在派生类视图中重新定义OnClose来控制关闭过程。只有在确认了关闭窗口是非常安全的情况下,才可调用基类的OnClose函数,有它来继续处理关闭过程。此时视图对象和相应的窗口仍处于活动状态。

15.WM_QUERYENDSESSION在用户退出Windows时发送用以通知所有正在运行的应用程序,如果重新定义了OnClose函数,那么也应该重新定义OnQueryEndSession函数。

16.WM_DESTROYWindows发送WM_CLOSE消息之后紧接着被发出。收到该消息时,程序将假定此时视窗已完全消失,但它和子窗口还仍然处于活动状态。可在OnDestroy中对依赖于当前窗口存在的所有东西做清除工作。注意调用基类的OnDestroy函数。

17.WM_NCDESTROY时窗口被取消后Windows发送的最后一个消息。此时所有子窗口都已被关闭,故可在OnNcDestroy中做一些不依赖于该窗口存在的处理工作。注意调用基类的OnNcDestroy函数。

2002-7-17

第五章     图形设备接口GDI、颜色及字体

1.   Windows的设备环境是GDI的关键元素,它代表了物理设备。设备环境类的基类CDC包含了绘图的所有成员函数,除了CMetaFileDC类以外,所有的派生类只有构造函数和析构函数有所不同。对显示器来说,常用的派生类有CClientDC类和CWindowsDC,而其它设备来说,则可以构造一个CDC对象。对显示器和打印机设备环境来说,应用程序会直接将句柄附在对象上,而对于其它设备环境来说,为了将对象与句柄相联系,在构造完对象之后,还必须调用一个成员函数。

2.   创建一个CclientDC对象,则用户不可能在客户区域之外绘图。视图窗口没有非客户区域,因此CWindowDC更适合框架窗口,而不是视图窗口。

3.   Microsoft Windows限制了可用的设备环境的数目,如果不成功删除设备环境对象,则程序在退出之前会有内存泄漏。通过调用CWnd::GetDC来获得设备环境的指针,必须注意要通过调用CWnd::ReleaseDC来释放设备环境。

4.   CPaintDC类是非常特殊的,它的构造、析构函数都是针对显示用的。如果要重新编写视图的OnPaint函数,就需要使用CPaintDC类,并将对象指针传递到OnDraw函数。

5.   CGdiObject是所有GDI对象类的基类,其派生类有CBitmapCBrushCFontCPaletteCPenCRgn。如果构造一个CGdiObject派生类对象,则最后必须将它从设备环境中分离出来以便删除。通常是在用SelectObject选进GDI对象时,将原来的GDI对象保存起来,任务完成后再恢复原来的对象。

6.   使用CDC::SelectStockObject可以把一个库存对象选进设备环境,由于它们是系统的一部分,所以用不着删除它们。

7.   对于显示设备环境来说,在每个消息控制函数的入口处,设备环境都是未初始化的,当函数退出后,其体内所进行的所有GDI选择都不再有效,因此,每次都必须从头设置设备环境。对于其它设备环境而言,设置可持续的长久一些,但由于SelectObject函数所返回的GDI对象指针的临时性造成了其复杂性。不能简单将这一指针保存在类的数据成员中,而应该借助于CGdiObject::GetSafeHdc将其转化为句柄,这也是唯一可以持久存在的GDI标示,可以使用CGdiObject::FromHandle加以恢复。

8.   CDC::GetDeviceCaps可以返回各种显示参数。

9.   使用图案刷子,在窗口滚动过程中必须使用CDC::SetBrushOrg重新设置原点,否则图案裁掉部分就会对不齐,看起来不美观。

10.CWnd::SetCapture用于捕获鼠标,这样即使鼠标移出了窗口,WM_MOUSEMOVE消息还是可以发送给窗口。在鼠标被捕获期间,SetCursor函数设置的类型为HCUSOR的光标资源一直处于激活状态。LoadCursor可用来建立一个光标资源。Win32 ReleaseCapture用于取消对鼠标的捕获。

第六章     modal对话框和Windows通用控件

1.   控件通过向上级对话框发送消息来响应用户的动作。

2.   以下是CMyDialog::DoModal的执行过程:

CDialog::DoModal
    CMyDialog::OnInitDialog
        …additional initialization…
        CDialog::OnInitDialog
            CWnd::UpdateData(FALSE)
                CMyDialog::DoDataExchange
    user enters data…
    user clicks the OK button
    CMyDialog::OnOK
        …additional validation…
        CDialog::OnOK
            CWnd::UpdateData(TRUE)
                CMyDialog::DoDataExchange
            CDialog::EndDialog(IDOK)

3.   可以编写自己的DDX函数,并连进Developer Studio中,可以参考MFC Technical Note #26

4.   通过获得OnOKOnCancel处理以改进对话框程序时,注意取消相应按钮的Default Button属性的复选标记。

5.   可以利用CWnd::GetDlgItemCWnd::GetDlgCtrlIDCWnd指针和控件ID之间进行转化。由于没有调用控件对象的构造函数,使用 CWnd::GetDlgItem框架会返回一个临时指针,不能存储供以后使用。

6.   每个控件在显示之前会立刻向其父对话框发送WM_CTLCOLOR消息,对话框本身也会发送该消息。映射该消息可以设定控件和对话框文本的前景色和背景色,还可以为它们的非文本区域选择一个刷子。

7.   在对话框窗口里画图,必须重写OnPaint,通过CWnd::GetDC获取控件的设备环境。可以通过利用CWnd::Invalidate/CWnd::UpdateWindow这一调用序列防止窗口重绘。

8.   可以在运行时在对话框中加入控件:先为类添加一个控件数据成员,然后为控件加一个ID常量,接着在OnInitDialog中调用控件的Create成员函数,最后手工映射必要的消息控制函数。

9.   Windows通用控件的代码在COMCTL32.DLL中,其中包括每个控件的窗口过程函数以及注册窗口类的代码。程序初始化对话框时,将使用对话框资源文件中符号化的类名连接到DLL里的窗口过程函数。

2002-7-20

第七章     Modeless对话框和Windows通用对话框类

1 创建modal对话框和创建modeless对话框之间的区别:

 

Modal Dialog

Modeless Dialog

Constructor used

Constructor with resource ID param

Default constructor (no params)

Function used to create window

DoModal

Create with resource ID param

2   有两种方式来发送Windows消息:CWnd::SendMessage可以立刻导致对消息控制函数的调用,CWnd::PostMessage则将消息放进Windows消息队列。

3.     Windows来说,对话框实际上属于应用程序的框架窗口,而不属于视图。

4.     WM_USER使用户自定义消息中可利用的第一个消息ID

5.     对于modeless对话框,注意一定不要调用CDialog::OnOkCDialog::OnCancel,必须在派生类中重写这些虚拟函数;否则,当使用Esc键、回车键或鼠标单击某按钮时,就会激发对象应急类函数的调用,进而调用WindowsEndDialog函数,而该函数只适合modal对话框!对于modeless对话框,必须调用DestroyWindow函数。如果需要的话,还必须调用UpdateData将数据从控件传到数据成员。

6.     除了主框架窗口之外,对于几乎所有类型的窗口,DestroyWindow都不会将C++对象删除。

7.     所有的Windows通用对话框都从CCommonDialg派生而来,列表如下:

Class

Purpose

CColorDialog

Allows the user to select or create a color

CFileDialog

Allows the user to open or save a file

CFindReplaceDialog

Allows the user to substitute one string for another

CPageSetupDialog

Allows the user to input page measurement parameters

CFontDialog

Allows the user to select a font from a list of available fonts

CPrintDialog

Allows the user to set up the printer and print a document

8.     嵌套对话框技术:创建一个对话框资源模板,其中有一个“洞”,典型情况是一个IDstc32=0x45f的分组框组控件;在程序里设置一些参数以使用创建的对话框资源模板;还必须截住COMDLG32的消息循环,以便抢先通知。

第八章     使用ActiveX控件

1.   VBXVB控件,大部分用C语言实现,其标准建立在16位段式结构基础上,不适合32位环境。ActiveX控件之前常称为OLE控件,即OCX,它基于MicrosoftCOM技术,可以代替以前的VBXActiveX控件可以用C++语言实现,还可以有MFC库的支持或者ActiveX模板库ATL的支持。

2.   普通控件给对话框发送通知命令消息。如果希望控件执行一个动作,可以调用C++控件类的成员函数,实际上该函数也是给控件发送一个Windows消息。普通控件的另一种情况为定制控件,它由程序员创建,其行为与普通控件一样,也给父窗口发送WM_COMMAND消息,并接受用户定义的消息。

3.   包含控件的窗口称为包容器。

4.   ActiveX控件最突出的特点是其属性和方法。属性有符号化的名字,这些名字对应着内部的整数索引。客户可通过一个整数索引存取相应的属性值。ActiveX控件的方法与函数很类似。

5.   ActiveX控件不像普通控件那样,发送以WM_打头的通知消息给它的包容器,而是“激发事件”。事件有一个符号化的名字及一组任意次序的参数,它实际上是有控件调用的包容器函数。对于客户类来说,事件与控件的通知消息是一样的。

6.   实际上,ActiveX控件可能没有窗口。调用Create时会先载入控件代码,并发一个实地激活命令,然后ActiveX控件再建立它自己的窗口,不过客户程序最好不要直接使用该控件的hWnd句柄。

7.   通常一个或多个ActiveX控件会保存在一个扩展名为OCX的动态连接库中,包容器程序根据Windows注册表,利用复杂的COM技术在需要的时候装入改动态连接库。显然,如果要发布一个利用ActiveX控件建立起来的程序,那么就必须包含相应的OCX文件,而且还得提供一个合适的安装程序。

8.   安装ActiveX控件的步骤:先把控件的动态连接库文件以及相关的文件如帮助文件help和许可文件LIC拷贝到硬盘上,最好放在系统目录下;然后在Windows的注册表中进行登记注册;最后在用到该控件的每一个项目中安装该控件。

9.   所有的ActiveX控件属性,包括设计时的属性,在运行时都是可以访问的。

10.AppWizardActiveX控件支持:程序类的成员函数InitInstance中插入AfxEnableControlContainer();同时在相应项目的StdAfx.h中插入#include <afxdisp.h>

11.通过在函数OnInitDialog中加入AfxOleLockControl(someActivexControl.GetClsid());可以把ActiveX控件锁定在内存中,这样,除非程序退出或者调用了AfxOleUnlockControlActiveX控件将总在内存中。

2002-7-21

第二十一章 动态连接库

1.   类是创建时buildtime的模块,而DLL是运行时runtime的模块,可单独测试。

2.   DLL是进程的一部分,经编译后装到一个预制的基地址,如果跟其他的DLL没有冲突的话,该文件就被映射到进程中相同的虚拟地址上。

3.   DLL包含一个导出函数表,该表包含了函数在DLL内的地址。客户可以通过符号化的函数名字和序号(可选)来调用这些函数。重建DLL后不需重建客户程序,除非改变了函数名或参数序列。

4.   DLL中必须显示声明导出函数:_declspec(dllexport) int MyFunction(int n); 客户程序中必须显示声明导入函数:_declspec(dllimport) int MyFunction(int n);  若是DLL使用了C++而又想避开编译器对导出函数使用name-mangling技术,那么两条语句前都要加上extern “C”

5.   默认情况下,编译器使用__cdecl参数传递参数,即调用程序从栈里弹出参数。使用__stdcall参数意味着被调用的函数将直接从栈里弹出。

6.   对于隐式连接,客户需导入声明、为连接器指定导入库LIB文件,并在可执行路径里至少调用了DLL导出函数中的一个函数。对于显示连接,客户需要的是调用::LoadLibrary::GetProcAddress

7.   Windows调用DLL时,它首先调用连接器指定的主入口函数_DllMainCRTStartup。该函数首先调用全局对象的构造函数,然后调用全局函数DllMain

8.   DllMain不仅在连接到进程时被调用,而且在断开进程连接和其它相应时候也被调用。DllMain在独立线程被启动和终止时被调用,参数dwReason指明了调用原因。

9.   进程中的每一个DLL都被一个32位的HINSTANCE值(实例句柄)所标示,进程本身也有一个HINSTANCE值,它们代表了DLL或者EXE的起始虚拟地址。EXEDLL可以拥有各自的资源。::FindResource带一个HINSTANCE参数,可用于装载资源。可用::GetModuleHandle获得DLL或者EXEHINSTANCE值。

10.扩展DLL支持C++接口,并要求客户程序被动态连接到相同版本的MFC库,该库RELEASE版本对应文件为mfc42.dll。正规DLL可以导出C风格的函数,却不能导出C++类。

11.利用AppWizard创建扩展DLL框架,程序员自己必须要做的是将宏AFX_EXT_CLASS加到类声明中。

12.如果编写一个需要被本地化的应用程序,可以把与语言相关的所有资源放到一个MFC正规DLL中,生成不同的DLL模块,再利用AfxGetResourceHandle::GetModuleHandleAfxSetResourceHandle装载特定的资源。

13.mfc42.dll作为进程的一部分被装入时,它把数据放在一些可靠的全局变量里。如果从一个正规MFC DLL调用进入mfc42.dll,则全局变量并不同步。解决问题的方法是在正规DLL的所有导出函数前插入 AFX_MANAGE_STATE(AfxGetStaticModuleState());该代码对于静态连接无任何影响。

14.对于自定义控件DLL,客户知道控件窗口类的名字字符串,用类名构造自窗口,所有的代码包括WndProc函数都在DLL里,客户只需在创建子窗口前装入DLL

15.自定义控件通过发送特定的WM_COMMAND通知消息,来与父窗口通讯,如GetParent()

->SendMessage(WM_COMMAND, (ID_NOTIFYCODE << 16) | GetDlgCtrlID, (LONG)GetSafeHwnd());

客户方映射消息:ON_CONTROL(ID_NOTIFYCODE, IDC_MYCONTROL, OnClickedMyControl);

afx_msg void OnClickedMyControl();

2002-08-03

第二十三章 组件对象模型COM

1.   David J. Kruglinski认为,ActiveX是在老的OLEInternet发生碰撞后建立起来的事物,它不仅包括建立在COM上的Windows特性,而且包括Microsoft Internet Information Server族和WinInet编程接口。

2.   COM是一种协议,它建立并描述了一种软件模块同另一种软件模块之间的连接,当这种连接建立起来之后,两个模块就可以通过接口来通讯。

3.   COM接口是一个C++基类,被声明为struct。它定义了一组纯虚函数,但没有定义数据成员、构造函数以及析构函数。从一个接口开始,通过它可以得到同一对象的其它接口。可以将接口理解成两个模块之间达成的协议,但关键在于接口定义的不变性。

4.   为对付两个接口有相同成员函数名的情况,MFC使用了嵌套类。

5.   METHOD_PROLOGUE是一个MFC宏,它使用C语言的offset操作符来获得基类的this指针pThis

6.   为获得接口指针,COM定义了一个IUnkown接口,所有的接口都从它派生而来。IUnkown接口的纯虚拟成员函数QueryInterface根据接口ID参数返回相应的接口。

7.   COM有非常严格的协议来删除对象,IUnkown的另两个虚拟函数AddRefRelease是关键。每个COM类都由一个数据成员,MFC库中为m_dwRef,用来统计对象的“用户”数目,只有当该数据成员为0时,对象本身才会被删除。在MFC基于COM的程序中,对象的构造函数将m_dwRef设为1,故首次创建完对象之后没有必要调用AddRef

8.   我们无法直接调用目标类的构造函数,只能让组件模块来决定如何构造对象,为此组件提供了类厂class factory,并将目标类对象的具体创建步骤封装了起来。COM所谓的类厂实际上是指对象厂object factory。术语“组件对象”表示对象及与其相连的代码。

9.   ::CoCreateInstance函数结合了::CoGetClassObjectIClassFactory::CreateInstance的功能。客户程序调用::CoGetClassObject时,COM会通过在注册表中查找一个128位类ID值来建立连接,该ID值是独一无二的。COM还支持另外一种注册表数据库项,可使用::CLSIDFromProgID来读取数据库,将可读的程序ID转换成对应的类ID

10.除数据存储对象DAO外,COM函数的所有字符串参数都是类型为OLECHAR *Unicode字符串指针。对于普通字符串,如果需要双字节字符串,可以在其前面加前缀L字符。

11.COleObjectFactory::UpdateRegistryAll会找到程序中的所有全局类场对象,并对他们的名字和类ID注册。进程外组件模块中的对象在运行时通过调用COMCoRegisterClassObject函数被注册,注册信息被Windows DLL保存在内存中。

12.EXE组件处于不同的进程中,甚至可能在不同的机器上,COM通过一个远程过程调用RPC管理其中的细节。在RPC中,客户调用一个称为代理proxy的特殊DLL。客户调用组件函数时,该代理向组件程序发送一个消息以提醒存根stub,该消息由一个隐藏的窗口来处理。这种通过数据流来传递参数的机制称为调度marshaling。使用如IUnknownIClassFactory这样的标准接口,实现调度的代码和存根代码由Windows OLE32 DLL提供,而创建自己的接口需要亲自编写调度代码。因此,几乎没有软件公司开发自己的接口。

13.重要的MFC接口宏:

头文件:BEGIN_INTERFACE_PART, STDMETHOD_, END_INTERFACE_PART

实现文件:BEGIN_INTERFACE_MAP, INTERFACE_PART, END_INTERFACE_MAP,

STDMETHODIMP_

按照COM要求,STDMETHOD_STDMETHODIMP_ 使用_stdcall参数传递规则来声明和实现函数,并允许指定第一个参数作为函数返回值类型,STDMETHODSTDMETHODIMP则假定为HRESULT的返回值类型。

14.MFC使用DECLARE_OLECREATEIMPLEMENT_OLECREATE声明和创建一个COleObjectFactory类的全局对象,其中使用指定的唯一CLSID

15.MFC COM客户程序需要在StdAfx.h中加入#include <afxole.h>并在程序类InitInstance成员函数开始处加入AfxOleInit();

16.COM使用包容containment和集合aggregation来代替继承。

2002-8-4

屋檐下的水滴-- 读书笔记系列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值