关闭

VC技术内幕笔记1-3

1011人阅读 评论(0) 收藏 举报
第三章:从“Hello,world!”着手学习AppWizard


1,视图:从用户角度看,视图是一个普通的窗口;从程序员角度看,试图是一个从MFC库中CView类所派生的类的对象。视图对象的行为完全由类的成员函数和数据成员决定,其中包括派生类的特定成员和基类的继承而来的成员。

2,CEx03aView类最重要的基类是CWnd和CView。CWnd提供CEx03aView的窗口属性,而CView提供它和应用程序框架其它部分之间的联系,特别是和文档及框架窗口之间的联系。

3,视图类中OnDraw成员函数:OnDraw成员函数被设计成一个虚成员函数,每次当视窗需要被重绘时候,应用程序都要调用OnDraw函数。
当用户改变了窗口尺寸,或当前窗口恢复了先前被遮盖的部分,或当前应用程序改变了窗口的数据的时候,窗口都需要被重绘。
当用户改变了窗口尺寸,或窗口需要恢复被遮盖的部分,则应用程序自动去调用OnDraw函数。
但注意,如果程序中某个函数修改了窗口数据,则它必须通过调用视图所继承的Invalidate(InbalidateRect)成员函数来通知Windows,调用Invalidate后会触发对OnDraw函数的调用。

4,WINDOW中不允许直接访问显示硬件,必须通过和窗口相关联的“设备环境”跟显示硬件通讯。MFC中设备环境由CDC类对象来表示。

5,利用Win32 Debug模式 和 Win32 Release模式开发应用程序。
(默认情况不出现BUILD工具栏,可从TOOLS菜单选择Customize项,设置显示Build工具栏,VC6中也可在工具栏上直接点击右键选择Build工具栏)

  Win32 Release模式 Win32 Debug模式   
源码调试 不能   编译器连接器都允许
MFC诊断宏 不能(定义了NDEBUG) 可以(定义了_DEBUG)
库连接  MFC Release库  MFC Debug库
编译优化 速度优化(学习版不能) 没有优化(快速编译)

我们应在Win32 Debug模式下开发应用程序,然后在发布或交付前用Win32 Release模式重建应用程序。in32 Release模式下建立的EXE文件又小又快。

6,诊断宏TRACE宏对监测程序状态特别有用。这些宏要求在允许追踪的情况下才有效,允许追踪是默认设置。
(问题:TRACE宏的应用事例与运行原理。宏TRACE的输出在在哪显示呢?在output输出框下选择Debug项,调试运行程序即可看到TRACE宏输出。)

7,预编译头文件代表了编译器在源代码特定行上进行‘快照’。在MFC库程序中,快照常紧接在#include "stdafx.h"后进行。
文件StdAfx.h包含了MFC库头文件的#include语句。该文件的内容取决于运行AppWizard时候所做的选择。
文件StdAfx.cpp只包含#include "stdafx.h"语句。StdAfx.cpp文件用来在相应项目的目录下产生预编译头文件(PCH文件)。编译开关/Yc只用于StdAfx.cpp文件,导致产生预编译头文件(PCH),而其它源文件的/Yu编译开关使可以使用一个存在的PCH文件。开关/Fp用来指定专门的PCH文件名,默认情况下为相应目标输出文件目录下与项目同名扩展名为PCH的文件。
(VC6下具体设置参见:[Project Settings属性框] [c/c++]属性页[Project Options]、[Source File Options]项)
(注意:在VC6 [Project Settings属性框]中查看StdAfx.cpp文件[Source File Options]项时候,可以看到[Project Settings, and /Yc"stdafx.h"],这里说明一下,StdAfx.cpp文件的[ /Yc"stdafx.h" ]的设置覆盖了[Project Options]设置中的[/Yu"stdafx.h"])

你可以通过这个链接引用该篇文章:http://npuwanglei.bokee.com/tb.b?diaryId=11642274

VC 技术内幕(第四版)笔记(1~2章)

分类:VC学习

第一章:Microsoft Windows 和 Visual C++

1,Windows应用程序中一定要有WinMain函数,用来完成某些特殊的任务,其中最主要的是创建应用次序的主窗口。

2,DOS下,所有次序的目标模块在创建过程中都被静态连接起来。而Windows允许动态连接,即一些特定结构的库(DLL)可以在运行过程中被装入和连接,并且被多个应用程序共享。

3,项目是一些相互关联的源文件的集合,这些源文件经过编译、连接,然后被组合在一起形成可执行的Wwindows应用程序或DLL。

4,几个重要Developer Studio创建的中间文件:
CLW (支持ClassWizard ) DSP (项目文件)
DSW (工作空间文件)  NCB (支持ClassView)
OPT (保持工作空间的配置) PLG (建立日志文件)

5,AppWizard是一个代码生成器,它会按照用户通过对话框指定的特性、类名、及源代码文件名来产生Windows应用程序工作架构。AppWizard所产生的代码只是一些最基本的代码,它完成的功能完全由应用程序的基类所决定。

 

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


1,按照惯例,MFC库类名用大写“C”打头。

2,类CMyApp的对象theApp就代表一个应用程序。当运行该应用程序的时候,WINDOWS自动调用程序框架内部的WinMain函数,WinMain函数会去查找该应用程序的全局构造对象(theApp),该对象是有CWinApp类派生类的对象。C++中,全局对象在主程序被运行之前就已经构造好了。

3,当WinMain发现该应用程序对象(theApp)时,会自动调用虚拟成员函数CWinApp::InitInstance(),该函数会进一步调用相应的函数来完成主窗口的构造和显示工作。由于基类CWinApp中不知道我们实际需要的什么样的窗口,所以我们必须在派生出的应用程序类CMyApp中重载InitInstance()函数(CMyApp::InitInstance()),由多态性,当调用CWinApp::InitInstance()时候,会自动转向MyApp::InitInstance().

4,WinMain函数调用完InitInstance()函数后,就调用CWinApp::Run函数,CWinApp::Run函数被隐藏在基类中负责传递应用程序的消息给相应的窗口,从而维护应用程序的运转。

5,关闭应用程序(对单一框架程序来说),首先CMyFrame 对象被删除,然后退出Run,进而退出WinMain,最后删除CMyApp对象(theApp)。

6,一些Windows类库在基类为鼠标事件消息以及其他一些标准消息定义一些虚函数,如果需要,派生类会对这些函数进行重载。
MFC库应用程序框架没有采用虚函数来处理Windows消息,而是通过一些宏来将特定的消息映射到派生类中相应的成员函数上。(原因:P21。C++类对程序中用到的每一个派生类都要求有一张虚函数分发表VTABLE,在BTABLE中每个虚函数都需要有一个4字节的入口项,而不管该函数会不会在派生类中被重载。)
MFC消息控件函数要求提供函数原型,函数体,以及在消息映射中的入口。

7,文档-视图结构将数据从用户对数据的观察中分离出来,这样就允许对同一数据可以有多个视图。
MFC库应用程序中,文档和视图是由C++类的实例来描述的。
文档基类代码通常和FILE OPEN,FILE SAVE菜单项关联,而派生文档类则一般用来完成对文档对象数据的实际读写工作。
视图基类通常表示一个包含于框架窗口中的窗口,而派生视图类则常用来和稳当类相联系,负责应用程序的显示和打印机I/O。
派生视图类及其基类共同处理WINDOWS的消息,而MFC库则协调文档,视图,框架窗口,以及应用程序之间的相互作用关系,这种协调多用虚函数来实现。
文档对象既可以与一次可全部读入内存的磁盘文件相关联,也可以同数据库相关联。

你可以通过这个链接引用该篇文章:http://npuwanglei.bokee.com/tb.b?diaryId=11642254

(6)菜单编程

分类:VC学习

1,弹出菜单(Pop-up)是不能用来作命令响应的。

2,MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类--Doc类--CMainFrame类--App类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
具体:
当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。

3,消息的分类:标准消息,命令消息,通告消息。
[标准消息]:除WM_COMMAND之外,所有以WM_开头的消息。
[命令消息]:来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。
在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
[通告消息]:由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。
说明:
1)从CWnd派生的类,都可以接收到[标准消息]。
2)从CCmdTarget派生的类,都可以接收到[命令消息]和[通告消息]。

4,一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。对菜单栏的子菜单由左至右建立从0开始的索引。对特定子菜单的菜单项由上至下建立了从0开始的索引。访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。
相关重要函数:
CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜单栏对象指针。
CMenu* GetSubMenu(  ) ;//CMenu::GetSubMenu获得指向弹出菜单对象指针
UINT CheckMenuItem( );//CMenu::CheckMenuItem Adds check marks to or removes check marks from menu items in the pop-up menu.
BOOL SetDefaultItem();//CMenu::SetDefaultItem Sets the default menu item for the specified menu.
BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。
UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。
BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单。
HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle.
说明:
1)在计算子菜单菜单项的索引的时候,分隔栏符也算索引的。
2)int GetSystemMetrics()获取系统信息度量。可以用它来获取菜单标题的尺寸从而设置位图标题菜单中位图的大小。
3)在MFC中MFC为我们提供了一套命令更新机制,所有菜单项的更新都是由这套机制来完成的。所以要想利用CMenu::EnableMenuItem来自己控制菜单使用或不使用变灰等,必须要在CMainFrame的构造函数中将变量m_bAutoMenuEnable设置为FALSE。
4)Create a CMenu object on the stack frame as a local, then call CMenu’s member functions to manipulate the new menu as needed. Next, call CWnd::SetMenu to set the menu to a window, followed immediately by a call to the CMenu object’s Detach member function. The CWnd::SetMenu member function sets the window’s menu to the new menu, causes the window to be redrawn to reflect the menu change, and also passes ownership of the menu to the window. The call to Detach detaches the HMENU from the CMenu object, so that when the local CMenu variable passes out of scope, the CMenu object destructor does not attempt to destroy a menu it no longer owns. The menu itself is automatically destroyed when the window is destroyed.
5)You can use the LoadMenuIndirect member function to create a menu from a template in memory, but a menu created from a resource by a call to LoadMenu is more easily maintained, and the menu resource itself can be created and modified by the menu editor.
6)EXAMPLE:
CMenu menu;//定义为局部对象
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach();// 这里menu对象作为一个局部对象。使用Detach()从menu对象中分离窗口菜单句柄,从而当menu对象析构的时候窗口菜单资源不随之销毁。

5,命令更新机制:
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。
在后台操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管并创建一个CCmdUI对象和第一个菜单项相关联,调用对象成员函数DoUpdate()(注:这个函数在MSDN中没有找到说明)发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。此后同一个CCmdUI对象又设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。
更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
说明:
1)可以手工或用ClassWizard来给菜单项添加UPDATE_COMMAND_UI消息响应,利用响应函数中传进来的CCmdUI对象指针可完成设置菜单项可使用,不可使用,变灰,设置标记菜单等操作。

6,如果要想让工具栏上的某个图标与菜单项的某个菜单相关联,只需要将图标的ID设置为该菜单项的ID。
工具栏图标的索引记数顺序是:从做至右从0开始,分隔符也算索引号。

7,利用向项目中添加VC的POPMENU控件:Project->Add to Project->Components and Controls..
系统增加的内容:A,一个菜单资源;B,在派生View类中增加了OnContextMenu()函数
说明:
1)CWnd::OnContextMenu Called by the framework when the user has clicked the right mouse button (right clicked) in the window. You can process this message by displaying a context menu using the TrackPopupMenu.
2)BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
//CMenu::TrackPopupMenu Displays a floating pop-up menu at the specified location and tracks the selection of items on the pop-up menu. A floating pop-up menu can appear anywhere on the screen.

8,利用调用TrackPopupMenu函数,手工添加弹出菜单:
1)用资源管理器添加一个菜单资源
2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用TrackPopupMenu函数便完成任务(但要注意:鼠标响应函数传进来的坐标是客户区坐标,而TrackPopupMenu函数中使用的是屏幕坐标,在调用TrackPopupMenu前要调用ClientToScreen客户区坐标到屏幕坐标的转换)
事例代码:
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
说明:
CWnd::ClientToScreen(..);//将一个坐标点或一个矩形区域坐标转换成屏幕坐标。
CMenu::TrackPopupMenu(..);//在指定位置以指定的方式显示弹出菜单。
CWnd::ScreenToClient(..);
//Converts the screen coordinates of a given point or rectangle on the display to client coordinates.

9,当弹出菜单属于框架窗口的时候(可在TrackPopupMenu函数参数中设置),弹出菜单上的消息,在路由的时候,仍然遵循View-DOC-MainFrame-APP的响应顺序。

10,动态菜单编程:
所有的资源对象都有一个数据成员保存了资源的句柄。
CMenu::AppendMenu //Appends a new item to the end of a menu.
CMenu::CreatePopupMenu //Creates an empty pop-up menu and attaches it to a CMenu object.
CMenu::InsertMenu
//Inserts a new menu item at the position specified by nPosition and moves other items down the menu.
CMenu::GetSubMenu //Retrieves a pointer to a pop-up menu.
CWnd::GetMenu //Retrieves a pointer to the menu for this window.
CMenu::DeleteMenu //Deletes an item from the menu.

11,手动给动态菜单项添加响应函数:
在Resource.h中可以添加资源的ID
在头文件中写消息函数原型
在代码文件中添加消息映射和添加消息响应函数
说明:
可以先创建一个临时的菜单项,设置它的ID和动态菜单项的一致,然后对它用向导进行消息响应,然后删除临时菜单。
再在代码文件中把消息映射放到宏外(注意一定要放到宏外面,因为CLASSWIZARD发现菜单删除了,同时要把其宏对里的消息映射也删除掉的)

12,CWnd::DrawMenuBar
//Redraws the menu bar. If a menu bar is changed after Windows has created the window, call this function to draw the changed menu bar

CWnd::GetParent //get a pointer to a child window’s parent window (if any).
CWnd::Invalidate //注意其参数的作用

13,集合类:
CStringArray,CStringArray,CDWordArray,CPtrArray,CStringArray,CUIntArray,CWordArray
其中成员函数:
CArray::GetAt
CArray::Add

14,命令消息是到OnCommand函数的时候完成路由的。
由于CWnd::OnCommand 是个虚函数,可以在框架类中重写OnCommand函数,从而可以截获菜单消息使它不再往下(VIEW类)路由。
例:
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
 // TODO: Add your specialized code here and/or call the base class
 int MenuCmdId=LOWORD(wParam);//取命令ID
 CMenu2View *pView=(CMenu2View*)GetActiveView();//获取当前VIEW类指针
 if(MenuCmdId>=IDM_PHONE1 && MenuCmdId<IDM_PHONE1+pView->m_strArray.GetSize())//消息范围判断
 {
  CClientDC dc(pView);
  dc.TextOut(0,0,pView->m_strArray.GetAt(MenuCmdId-IDM_PHONE1));
  return TRUE;
   //函数返回,避免调用CFrameWnd::OnCommand函数,在CFrameWnd::OnCommand中截获的消息会交由VIEW类处理
 }
 return CFrameWnd::OnCommand(wParam, lParam);
  //调用基类OnCommand函数,在CFrameWnd::OnCommand中截获的消息会交由VIEW类处理
}
 
MSDN说明:
virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );
//The framework calls this member function when the user selects an item from a menu, when a child control sends a notification message, or when an accelerator keystroke is translated.
OnCommand processes the message map for control notification and ON_COMMAND entries, and calls the appropriate member function.
Override this member function in your derived class to handle the WM_COMMAND message. An override will not process the message map unless the base class OnCommand is called.

15,LOWORD与HIWORD宏
WORD LOWORD(
  DWORD dwValue  // value from which low-order word is retrieved
);
WORD HIWORD(
  DWORD dwValue  // value from which high-order word is retrieved
);

//The LOWORD macro retrieves the low-order word from the given 32-bit value.
//The HIWORD macro retrieves the high-order word from the given 32-bit value.


16,CFrameWnd::GetActiveView
CView* GetActiveView( ) const;//获取当前视窗口指针(单文档框架中)

17,源文件是单独参与编译的。

你可以通过这个链接引用该篇文章:http://npuwanglei.bokee.com/tb.b?diaryId=11642146

(5)文本编程

分类:VC学习

1,创建插入符:
void CreateSolidCaret( int nWidth, int nHeight );//创建插入符
void CreateCaret( CBitmap* pBitmap );//创建位图插入符
void ShowCaret( );//显示插入符
void HideCaret( );//隐藏插入符
static void PASCAL SetCaretPos( POINT point );//移动插入符号
说明:
1)创建插入符要在窗口创建完成之后,CreateSolidCaret函数创建的插入符被初始化为隐藏,所以需要调用ShowCaret()将其显示。
2)使用CreateCaret函数创建位图插入符的时候,不能使用局部的位图对象关联位图资源。(与资源相关联的C++对象,当它析构的时候会同时把与它相关联的资源销毁。)

2,获取当前字体信息的度量:CDC::GetTextMetrics
BOOL GetTextMetrics( LPTEXTMETRIC lpMetrics ) const;
说明:
typedef struct tagTEXTMETRIC {  /* tm */
    int  tmHeight;//字体高度。Specifies the height (ascent + descent) of characters.
    int  tmAscent;//基线以上的字体高度
    int  tmDescent;//基线以下的字体高度
    int  tmInternalLeading;
    int  tmExternalLeading;
    int  tmAveCharWidth;//字符平均宽度
    int  tmMaxCharWidth;
    int  tmWeight;
    BYTE tmItalic;
    BYTE tmUnderlined;
    BYTE tmStruckOut;
    BYTE tmFirstChar;
    BYTE tmLastChar;
    BYTE tmDefaultChar;
    BYTE tmBreakChar;
    BYTE tmPitchAndFamily;
    BYTE tmCharSet;
    int  tmOverhang;
    int  tmDigitizedAspectX;
    int  tmDigitizedAspectY;
} TEXTMETRIC;

3,OnDraw函数:
virtual void OnDraw( CDC* pDC )
当窗口(从无到有或尺寸大小改变等)要求重绘的时候,会发送WM_PAIN消息,调用OnDraw函数进行重绘。

4,获取字符串的高度和宽度(区别字符串的长度):
CDC::GetTextExtent
CSize GetTextExtent( LPCTSTR lpszString, int nCount ) const;
CSize GetTextExtent( const CString& str ) const;
说明:
The CSize class is similar to the Windows SIZE structure。
typedef struct tagSIZE {
    int cx;//the x-extent
    int cy;//the y-extent
} SIZE;


5,路径层:
BOOL BeginPath( );
//在这作图定义路径层剪切区域
BOOL EndPath( );
BOOL SelectClipPath( int nMode );//调用这个函数来使当前路径层剪切区域与新剪切区域进行互操作。  
//在这覆盖作图(包含前定义的路径层区域)定义新的剪切区域

说明:
1)SelectClipPath Selects the current path as a clipping region for the device context, combining the new region with any existing clipping region by using the specified mode. The device context identified must contain a closed path.
////
nMode:RGN_AND,RGN_COPY,RGN_DIFF,RGN_OR,RGN_XOR
RGN_AND   The new clipping region includes the intersection (overlapping areas) of the current clipping region and the current path.
RGN_COPY   The new clipping region is the current path.
RGN_DIFF   The new clipping region includes the areas of the current clipping region, and those of the current path are excluded.
RGN_OR   The new clipping region includes the union (combined areas) of the current clipping region and the current path.
RGN_XOR   The new clipping region includes the union of the current clipping region and the current path, but without the overlapping areas.
2)应用:当作图的时候,如果想要在整幅图形其中的某个部分和其它部分有所区别,我们可以把这部分图形放到路径层当中,然后指定调用指定互操作模式调用SelectClipPath( int nMode )函数来使路径层和覆盖在其上新绘图剪切区域进行互操作,达到特殊效果。

6,关于文本字符串一些函数:
COLORREF GetBkColor( ) const;//得到背景颜色
virtual COLORREF SetBkColor( COLORREF crColor );//设置背景颜色
BOOL SetTextBkColor( COLORREF cr );//设置文本背景颜色
virtual COLORREF SetTextColor( COLORREF crColor );//设置文本颜色
virtual BOOL TextOut( int x, int y, LPCTSTR lpszString, int nCount );//输出文本
BOOL TextOut( int x, int y, const CString& str );
CString Left( int nCount ) const;//得到字符串左边nCount个字符
int GetLength( ) const;//得到字符串长度

7,字体CFont::CFont
CFont( );//构造函数
//Constructs a CFont object. The resulting object must be initialized with CreateFont, CreateFontIndirect, CreatePointFont, or CreatePointFontIndirect before it can be used.
选用字体事例代码组:
CClientDC dc(this);
CFont font;//构造字体对象
font.CreatePointFont(300,"华文行楷",NULL);//初始化字体对象,与字体资源相关联
CFont *pOldFont=dc.SelectObject(&font);//将新字体选入DC
...
dc.SelectObject(pOldFont);//恢复原字体
说明:
1)构造字体对象时候,必须初始化。(初始化是将字体对象与字体资源相关联)。
2)初始化对象时候,选用的字体也可以是系统字体,但不一定都有效,据测试选用。

8,在MFC中CEditView 和 cRichEditView类已经完成了初步的文字处理。可以让应用程序的View类以CEditView 和 cRichEditView类为基类。

9,平滑变色
CDC::TextOut()是一个字母一个字母的输出,达不到平滑效果。
CDC::DrawText():将文字的输出局限于一个矩形区域,超出矩形区域的文字都被截断。利用这一特点,可每隔些时间增加矩形大小,从而可实现人眼中的平滑效果。
CWnd::SetTimer():设置定时器。按设定的时间定时发送WM_TIMER消息。

说明:
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );
//nIDEvent定时器标示,nElapse消息发送间隔时间,void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD)设置回调函数,如果设置则由设置的回调函数处理WM_TIMER消息,如果没有设置回调函数设为NULL,这发送的WM_TIMER消息压入消息队列,交由相关联的窗口处理(添加WM_TIMER消息处理函数OnTimer())。

afx_msg void OnTimer( UINT nIDEvent );
//响应WM_TIMER消息,nIDEvent为消息对应定时器标示(可以设置不同的定时器发送WM_TIMER消息)

 

 
问题:
1,在CCareView类中添加WM_CREATE消息响应函数OnCreate(),WM_CREATE消息是在什么时候被检查到而被响应的呢?
(猜测:添加WM_CREATE消息后,消息被压入消息队列,然后经过消息循环进行分发到具体窗口,从而进行响应)

2,现有一文本文件内容已经读入串STR中,要求在视图客户区按原先文本文件中的格式输出。
问题是,利用CDC的TextOut()来在CView类派生类窗口中输出串时,忽略了串中的TAB、回车换行等格式,无论串有多长均在一行上输出。
这其中是CDC类成员函数TextOut()忽略串中格式的,还是CView类派生类窗口设置从中做怪呢?怎么解决?

你可以通过这个链接引用该篇文章:http://npuwanglei.bokee.com/tb.b?diaryId=11642119

(4)MFC消息映射机制的剖析

分类:VC学习

一,消息映射机制

1,消息响应函数:(例:在CDrawView类响应鼠标左键按下消息)
 1)在头文件(DrawView.h)中声明消息响应函数原型。
//{{AFX_MSG(CDrawView)   //注释宏
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG   //注释宏
说明:
在注释宏之间的声明在VC中灰色显示。afx_msg宏表示声明的是一个消息响应函数。
 2)在源文件(DrawView.cpp)中进行消息映射。
BEGIN_MESSAGE_MAP(CDrawView, CView)
 //{{AFX_MSG_MAP(CDrawView)
 ON_WM_LBUTTONDOWN()
 //}}AFX_MSG_MAP
 // Standard printing commands
 ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
 ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
说明:
在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。
宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。
宏ON_WM_LBUTTONDOWN()定义如下:
#define ON_WM_LBUTTONDOWN() /
 { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, /
  (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
 3)源文件中进行消息响应函数处理。(DrawView.cpp中自动生成OnLButtonDown函数轮廓,如下)
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 CView::OnLButtonDown(nFlags, point);
}
说明:
可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。

2,消息响应的方式:
1)在基类中针对每种消息做一个虚函数,当子类对消息响应时候,只要在子类中重写这个虚函数即可。缺点:MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚表,这样浪费内存,故MFC没有采取这中方式而采取消息映射方式。
2)消息映射方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。
说明:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);

 

二,有关绘图

1,使用SDK获取DC句柄:
HDC hdc;
hdc=::GetDc(m_hWnd);//获取DC句柄
MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
LineTo(hdc,point.x,point.y);
::ReleaseDC(m_hWnd,hdc);//释放DC

2,利用CDC类指针和CWin类成员函数获取DC。
CDC *pDC=GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);

3,利用CClientDC对象。(CClientDC类从CDC类派生来的)
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
The CClientDC class is derived from CDC and takes care of calling the Windows functions GetDC at construction time and ReleaseDC at destruction time. This means that the device context associated with a CClientDC object is the client area of a window.

4,利用CWindowDC对象。(CWindowDC类从CDC类派生来的)
CWindowDC dc(this);//
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
说明:
The CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that a CWindowDC object accesses the entire screen area of a CWnd (both client and nonclient areas).

5,GetParent()得到父窗口指针;GetDesktopWindow()得到屏幕窗口指针。

6,利用画笔改变线条颜色和类型:
CPen pen(PS_DOT,1,RGB(0,255,0));//构造画笔对象
CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//将画笔选入DC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复先前的画笔

7,使用画刷(通常利用画刷去填充矩形区域):
使用单色画刷
CBrush brush(RGB(255,0,0));//构造画刷对象
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的画刷去填充矩形区域

使用位图画刷
CBitmap bitmap;//构造位图对象(使用前需要初试化)
bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象
CBrush brush(&bitmap);//构造位图画刷
CClientDC dc(this);
dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图画刷去填充矩形区域

使用透明画刷
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针
CClientDC dc(this);
CBrush *pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC
dc.Rectangle(CRect(m_ptOrigin,point));
dc.SelectObject(pOldBrush);//释放透明画刷
说明:
The GetStockObject function retrieves a handle to one of the predefined stock pens, brushes, fonts, or palettes.
HGDIOBJ GetStockObject(
  int fnObject   // type of stock object
);

Returns a pointer to a CBrush object when given a handle to a Windows HBRUSH object.
static CBrush* PASCAL FromHandle( HBRUSH hBrush );//FromHandle是一个静态方法,故可用CBrush::FromHandle()形式调用。
注意点:
1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。
2)静态方法中,不能引用非静态的数据成员和方法。
3)静态数据成员需要在类外单独做初始化,形式如: 变量类型 类名::变量名=初始值;

8,CDC::SetROP2方法:
int SetROP2( int nDrawMode );
Sets the current drawing mode.

你可以通过这个链接引用该篇文章:http://npuwanglei.bokee.com/tb.b?diaryId=11641992

(3)MFC程序框架的剖析

分类:VC学习

1,寻找WinMain人口:
在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。
路径:MFC|SRC|APPMODUL.CPP:
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
{
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
注意:(#define _tWinMain   WinMain)

2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。
所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。

3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。
CWinApp构造函数:MFC|SRC|APPCORE.CPP
CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下:
(在CWinApp类定义中, CWinApp(LPCTSTR lpszAppName = NULL); )
注意:CWinApp()函数中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
(this指向的是派生类CTEApp对象,即theApp)
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}

4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。
AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP:
在AfxWinMain()函数中:
CWinApp* pApp = AfxGetApp();
说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。
//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
// { return afxCurrentWinApp; }

调用pThread->InitInstance()
说明:pThread也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。

nReturnCode = pThread->Run();
说明:pThread->Run()完成了消息循环。

5,注册窗口类:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//进入程序
->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试)

6,PreCreateWindow()://主要是注册窗口类
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
 if( !CFrameWnd::PreCreateWindow(cs) )
  return FALSE;
 return TRUE;
}
说明:
CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
 if (cs.lpszClass == NULL)
 {
  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
   //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册
  cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
   //把注册后的窗口类名赋给cs.lpszClass
 }

 if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
  cs.style |= FWS_PREFIXTITLE;

 if (afxData.bWin4)
  cs.dwExStyle |= WS_EX_CLIENTEDGE;

 return TRUE;
}

其中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。
#define VERIFY(f)          ASSERT(f)
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
define AFX_WNDFRAMEORVIEW_REG          0x00008
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。
//#define AFX_WNDFRAMEORVIEW  AFX_WNDCLASS("FrameOrView")

7,创建窗口:
Create()函数路径:MFC|SRC|WINFRM.CPP:
CFrameWnd::Create(...){
 ...
 CreateEx(...);//从父类继承来的,调用CWnd::CreateEx().
 ...
}

CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP
BOOL CWnd::CreateEx(...){
 ...
 if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。
 {
  PostNcDestroy();
  return FALSE;
 }
 ...
 HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
  cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
  cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

 ...
}
说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。
HWND CreateWindowEx(
  DWORD dwExStyle,     
  LPCTSTR lpClassName, 
  LPCTSTR lpWindowName,
  DWORD dwStyle,       
  int x,               
  int y,               
  int nWidth,          
  int nHeight,         
  HWND hWndParent,     
  HMENU hMenu,         
  HINSTANCE hInstance, 
  LPVOID lpParam       
);
typedef struct tagCREATESTRUCT { // cs
    LPVOID    lpCreateParams;
    HINSTANCE hInstance;
    HMENU     hMenu;
    HWND      hwndParent;
    int       cy;
    int       cx;
    int       y;
    int       x;
    LONG      style;
    LPCTSTR   lpszName;
    LPCTSTR   lpszClass;
    DWORD     dwExStyle;
} CREATESTRUCT;

8,显示和更新窗口:
CTEApp类,TEApp.cpp中
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口
m_pMainWnd->UpdateWindow();//更新窗口
说明:
class CTEApp : public CWinApp{...}
class CWinApp : public CWinThread{...}
class CWinThread : public CCmdTarget
{
 ...
public:
 CWnd* m_pMainWnd;
 ...
...
}

9,消息循环:
int AFXAPI AfxWinMain()
{ ...
 // Perform specific initializations
 if (!pThread->InitInstance()){...}
 //完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。
 nReturnCode = pThread->Run();
 //继承基类Run()方法,调用CWinThread::Run()来完成消息循环
 ...
}
////////////////////////////////////////////////////////////////
CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP
int CWinThread::Run()
{ ...
  // phase2: pump messages while available
  do//消息循环
  {
   // pump message, but quit on WM_QUIT
   if (!PumpMessage())//取消息并处理
    return ExitInstance();
   ...
  } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
 ...
}
说明:
BOOL PeekMessage(,,,,)函数说明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.

/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
 ...
 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息
 {...}
 ...
 // process this message
 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
  ::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换
  ::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。)
 }
 return TRUE;
}

9,文档与视结构:
可以认为View类窗口是CMainFram类窗口的子窗口。
DOCument类是文档类。
DOC-VIEW结构将数据本身与它的显示分离开。
文档类:数据的存储,加载
视类:数据的显示,修改

10,文档类,视类,框架类的有机结合:
在CTEApp类CTEApp::InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
 IDR_MAINFRAME,
 RUNTIME_CLASS(CTEDoc),
 RUNTIME_CLASS(CMainFrame),       // main SDI frame window
 RUNTIME_CLASS(CTEView));
AddDocTemplate(pDocTemplate);//增加到模板

 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1342043次
    • 积分:20833
    • 等级:
    • 排名:第365名
    • 原创:810篇
    • 转载:12篇
    • 译文:1篇
    • 评论:227条
    最新评论
    ATL/WTL
    Big Deal
    Book