MFC 相关


下面所指的书页位于深入浅出mfc第2版:
一:抽象方法,可以在所在类中调用:即尚未实现,就可调用;
二:抽象方法,可以父类中为实现,子类中覆盖为抽象;
三:声明为final,防止被子类覆盖;
volatile:只有第一次才彻底执行某一个它声明的对象,确保程序正确有序的处理它声明的对象;
RTTI:运行时类型鉴别;
static成员变量:必须初始化;(在类体外)
IDL(interface definition language):TLB(com组件函数声明放在该独立文件中,实现跨语言编程)是由一个描述接口的文件 IDL 经过编译产生的;
TLB:二进制的等价类型库文件 TLB;
ODL 文件和 IDL 类似,是MFC专门为自动化而描述的接口文件;

观察双接口 IDL 文件和 MFC 的 ODL 文件,每一个函数和属性都会有 [id(序号)....] ,即函数序号;
GetIDsOfNames:根据函数名称取得函数序号;

实现跨语言编程:每个语言都会生成vtable;
跨语言:VARIANT 数据类型;
现成的包装类 CComVariant、COleVariant、_variant_t。比如上面三个问题就可以这样书写:CComVariant v1(100),v2(true),v3("Hello,你好"); 

工具->选项;
create(虚)->createex->precreatewindow(虚);
调用约定:_stdcall pascal winapi callback 还有一个_cdecl:
// 常见的声明和赋值方法
CLSID CLSID_Excel = {0x00024500,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
struct __declspec(uuid("00024500-0000-0000-C000-000000000046")) CLSID_Excel;
class DECLSPEC_UUID("00024500-0000-0000-C000-000000000046") CLSID_Excel;

MDI多文档中:
app文件:m_pMainWnd(由CWinThread继承),m_nCmdShow(由CWinApp继承);m_szExeName(放置执行文件名);ProcessShellCommand(处理命令行选项);m_pDocManager;m_pCmdInfo(CCommandLineInfo*);
view文件:m_pDocument;GetDocument();OnUpdate()【虚函数,当doc数据发生改变时触发该函数,也是保持doc数据该view中显示一致性】;OnInitialUpdate()【虚函数,窗口用来初始化来自doc的信息,只做一次附着于doc时调用】;GetParentFrame()【获取其外层的窗口】;OnPrint(CPrintDC,CPrintInfo*)【打印,它调用OnDraw()】;GetClientRect(&rect)【获得该view口区域的大小】;doc文件:OnNewDocument()与Serialize();由Doc Template产生、管理;m_pDocTemplate;m_viewList【类型:CPtrList】;UpdateAllView()【保持doc数据各视图显示一致性】;GetTitle()【获取文档标题名】;
frame文件:m_pViewActive;GetActiveDocument()【实际返回CView::m_pDocument】;SetActiveView(CView*);
CWinTread:封装消息循环;
CWnd:m_hWnd;GetWindowText(str)【获取窗口标题?】;
CDC:pDC->m_hDC;
CCmdTarget:只有派生自该类,才能处理WM_COMMAND消息(消息映射/消息传递的大部分关键);
CMyWinApp的InitInstance()函数:实现窗口类注册(Create->CreateEx【实质上调用CreateWindowEx】->PreCreateWindow->AfxDeferRegisterClass),窗口显示和更新;
AfxWinMain()函数:很关键,完成app对象的InitInstance、InitApplication、Run调用;
由CreateWindowEx触发WM_CREATE消息---CMainFrame::OnCreate():实现工具栏和状态栏的建立工作;
CObject类成员函数:IsKindOf()和IsSerializable()【判断是否可以序列化,能序列化的类类型的版本号必须不为0xFFFF】;
可以接受消息的类:凡是派生自CCmdTarget的类;注:派生自CCmdTarget的类CWinThread没有消息映射宏,是个例外;CWinApp直接继承自CCmdTarget【afxwin.h】;
标准消息(WM_格式):必须是派生自CWnd的类接受;
消息映射AfxSig_符号:在afxmsg_.h中定义;
MDI多窗口视图:CFrameWnd::OnWindowNew()【winmdi.cpp,对应MDI风格时菜单:window/new window】;

MDI程序:
两个菜单:一个是有子窗口时的菜单IDR_SCRIBTYPE;一个是IDR_MAINFRAME(没有任何子窗口时的菜单);
一个doc源,多个doctemplate对象【app文件中,一个doctemplate指定一个view】 区别于 SplitterWnd的动态或静态创建的多子面板【静态:一个子面板一个view】;

MDI程序【多个doc源的情况】:
新建一个doc类;
一个doctemplate对象但是被多次调用构造;
第二次赋值时,注意四个参数都得变:


活跃子窗口【MDI程序中,CMDIChildWnd* p=MDIGetActive()】、活跃doc、活跃view、活跃pane【分割窗口】;

消息映射大局观:p431图9-6;
消息映射宏机制:有多态效果,但却未用虚函数;
消息循环:AfxWinMain()【winmain.cpp】->AfxWinInit()【appinit.cpp】,调用CWinApp::Run,调用CWinThread::PumpMessage;
p421:消息循环处理dispatchmessage都被发送到AfxWndProc:
CFrameWnd::LoadFrame->CFrameWnd::Create->CWnd::CreateEx
                     ->AfxHookWindowCreate(this)【生成CWnd派生类对象时调用该函数】->::SetWindowHookEx()【该函数有一参数_AfxCbtFilterHook,cbt过滤钩子函数】;
                     ->::CreateWindowEx,会调用_AfxCbtFilterHook()->_AfxStandardSubClass->SetWindowLong(hWnd,GWL_WNDPROC,&AfxWndProc);
消息处理函数:AfxWinInit()【appinit.cpp】->AfxWndProc【wincore.cpp】,调用_AfxCallWndProc(),调用pWnd->WindowProc()【虚函数】->OnWndMsg()【区分WM_COMMAND和WM_NOTIFY消息和标准消息】,DefWindowProc()【该函数有重载函数】->CallWindowProc()【跟_AfxCallWindowProc()参数一致】;
结构体AFX_MSGMAP_ENTRY、联合体MessageMapFunctions【afxmsg_.h】、枚举类型AfxSig【afxmsg_.h】:后两个决定了消息处理函数的返回值和参数;
ON_UPDATE_COMMAND_UI:与ON_COMMAND一样的消息传递路线;
tab order:布局/Tab Order;

消息处理次序:P427[如下],P430图9-5;
Frame窗口: 1.View
            2.Frame窗口本身
            3.CWinApp
View:       1.View本身
            2.Document
Document:   1.Document
            2.doc template


上面:
WM_COMMAND:交由OnCommand()【虚函数】处理->OnCmdMsg()【虚函数】;
改写虚函数的类决定着WM_COMMAND和WM_NOTIFY消息的流向;


AFX_PMSG:CCmdTarget派生类的成员函数类型;
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);


钩子函数:P420;





WH_CBT:CBT 含义是 基于计算机的训练滤网句柄;

以下在CMyWinApp的InitInstance()函数内实现CDocTemplate的成员变量的赋值:
CDocTemplate类起始:
CDocTemplate::OpenDocumentFile():调用CreateNewDocument()动态创建Document对象;
                                 调用CreateNewFrame()动态创建childFrame对象;
接着由CDocTemplate::CreateNewFrame()起始:
                                 调用CFrameWnd::LoadFrame()触发WM_CREATE消息;
                                 CFrameWnd::OnCreate();接着
                                 CFrameWnd::OnCreateHelper();接着
                                 CFrameWnd::OnCreateClient();接着
                                 CFrameWnd::CreateView()动态创建view对象;


CDocManager::m_templateList【类型:CPtrList】,AddDocTemplate()就是加入到该链表中;
CSingleDocTemplate::m_pOnlyDoc【类型:CDocument*】;
CMultiDocTemplate::m_docList【类型:CPtrList】;

AfxWinInit():完成pApp对象的成员的初始化操作;
InitApplication():完成文档模板的管理;

文档模板:管理Doc/View/Frame;
CSingleDocTemple、CMultiDocTemplate与SDI、MDI【风格,在P520区分】程序无关,即都可以在里面用;
子窗口对应着一个doc;//doc和子窗口的关系可以是一对多;

typeid()//操作符,返回type_info类型
isKindOf(const CRuntimeClass* pClass)//判断是某某类型或其父类,参数通常取RUNTIME_CLASS(类名)
在cpp加的头文件,在h中可以引用该头文件中的类;

const WCHAR * p = L"Hello,你好"; // 使用 UNICODE 字符集
LPCOLESTR p = L"Hello,你好"; // 意义同上
      
// 如果预定义了_UNICODE,则表示使用UNICODE字符集;如果定义了_MBCS,则表示使用 MBCS
const TCHAR * p = _T("Hello,你好"); 
LPCTSTR p = _T("Hello,你好"); // 意义同上
T表示使用一种中间类型;


m_pRecordset->CursorLocation = adUseClient;
m_pRecordset->Delete(adAffectCurrent);




BSTR 其实是一个指针类型,它的内存结构是:(输入程序片段 BSTR p = ::SysAllocString(L"Hello,你好");断点执行,然后观察p的内存);
BSTR的指针就是指向 UNICODE 串,因此 BSTR 和 LPOLESTR 可以在一定程度上混用,但一定要注意;
有函数 fun(LPCOLESTR lp),则你调用 BSTR p=...; fun(p); 正确
有函数 fun(const BSTR bstr),则你调用 LPCOLESTR p=...; fun(p); 错误!!!
封装类: CComBSTR 类;

ATL 7.0中的很多函数,VC6.0 下所带的 ATL 3.0 不支持;
ATL 7.0的BSTR包装类:CComBSTR;在 atlbase.h 中定义;
由于 ATL 转换宏使用栈作为临时空间,函数结束后会自动释放栈空间;
#include <atlconv.h>
USES_CONVERSION;  // 只需要调用一次,就可以在函数中进行多次转换


HRESULT Add([in] long n1, [in] long n2, [out] long *pnSum);  // IDL文件(注2)
STDMETHOD(Add)( long n1, long n2, long *pnSum);  // .h文件


CLSID clsid = {0x209ff,0,0,{0xc0,0,0,0,0,0,0,0x46}};
// 常见的声明和赋值方法
CLSID CLSID_Excel = {0x00024500,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
struct __declspec(uuid("00024500-0000-0000-C000-000000000046")) CLSID_Excel;
class DECLSPEC_UUID("00024500-0000-0000-C000-000000000046") CLSID_Excel;

MTS 支持事务处理,也就是是否支持 COM+ 功能;

typedef struct _GUID {
DWORD Data1; // 随机数
WORD Data2; // 和时间相关
WORD Data3; // 和时间相关
BYTE Data4[8]; // 和网卡MAC相关
} GUID;

typedef GUID CLSID;  // 组件ID
typedef GUID IID;    // 接口ID
#define REFCLSID const CLSID &

[out,retval]表示参数方向是输出,同时可以作为函数运算结果的返回值;
一个函数中,可以有多个[in]、[out],但[retval]只能有一个,并且要和[out]组合后在最后一个位置;

BSTR包含有字符串长度,因此实际的BSTR字符串内容中是可以存储L''\0''的,而函数 wcscat() 是以L''\0''作为复制结束标志,因此可能会丢失数据;

组件的重用方法有2个,聚合和包容;

stdole32.tlb
stdole2.tlb


使用第三方提供的组件程序时,可以命令行运行“regsvr32.exe 文件名”来注册;
顺便说一句,反注册的方法是“regsvr32.exe /u 文件名”;

2个智能指针的模板包装类,CComPtr<> 和 CComQIPtr<>;
CComQIPtr<> 唯一的缺点就是不能定义 IUnknown * 指针,优点是会自动帮我们调用QueryInterface()函数;
CComQIPtr < IUnknown > spUnk; // 错误!CComQIPtr不能定义IUnknown指针

CComQIPtr < IFun > spFun( pOtherInterface ); // 调用构造函数,内部接口指针赋值为
// 通过 pOtherInterface 这个普通接口指针调用QueryInterface()得到的IFun接口指针

CComQIPtr < IFun > spFun ( pUnknown ); // 调用构造函数,由IUnknown的QueryInterface()得到IFun接口指针;类似于:pUnknown->QueryInterface( IID_IFun, &sp ); // 也可以通过QueryInterface赋值

spFun.Release();   //正确
spFun->Release(); // 错!!!一定不要这么使用。
// 因为这个调用并不把内部指针清空,那么析构的时候会被再次释放(释放了两次);

组件升级了,也应该修改版本号:打开注册表文件(.rgs) 把有关ProgID的版本 "Mathe.1" 修改为"Mathe.2";
把IDL文件中的 version 和提示文字一并修改一下;

编译型语言;---"前绑定";
脚本语言是解释执行,不会知道具体的函数地址,自动化接口就为此诞生了---“后绑定”;

自动化组件,其实就是实现了 IDispatch 接口的组件;
IDispatch接口只支持自动化的参数类型;

ITypeLib::GetTypeInfo() ;
IDispatch::GetTypeInfo();

vc6.0 的ATL,编译器默认是不支持异常处理的;
Specify -GX”,解决方法是手工加上编译开关;

返回自己构造 HRESULT 错误值。方法是使用宏 MAKE_HRESULT(sev,fac,code);

接口其实就是一组相关函数的集合;
在COM中不使用“回调函数”而是使用“回调接口”;
回调接口,我们也叫“接收器(sink)接口”;
客户端传递接收器接口指针给COM。当发生事件时,COM调用接收器接口函数完成通知;
接收器(sink)接口指针(ICallBack *);


AfxOleInit();
增加第二个连接点的方法是要手工修改 IDL 文件;

DATA 类型就是是8字节的double,它的整数部分表示从 1899年12月30日开始的总天数,小数部分表示当天的时间已经渡过了一天的多少分之一。这个时间类型,用VARIANT表示,就是VT_DATE类型,MFC中用COleDateTime 表示。


_COM_SMARTPTR_TYPEDEF(IDispConnect, __uuidof(IDispConnect)); 定义了IDispConnectPtr;

不从 IPersistPropertyBag 派生,而是从 IPersistPropertyBagImpl<> 派生。在 ATL 中,系统帮我们已经完成了很多接口的默认实现,我们只要从 IxxxImpl<> 派生,然后再添加一些必要的映射和变量,就可以了;


bool m_bRequiresSave;// 这个成员变量,表示属性数据是否已经改变而需要保存;
BEGIN_PROP_MAP(Cxxx)
// 参数:"属性名称", 接口属性序号(见IDL文件), 属性页对话窗
PROP_ENTRY("str", 1, CLSID_NULL)
PROP_ENTRY("integer", 2, CLSID_NULL)
END_PROP_MAP()
//以上 IPersistXXXImpl 所必须的;


wininet;
isapi;
mapi;


ATL:active template library;

RTF格式:rich text format;


DECLARE_SERIAL;
DECLARE_OLECREATE;


RegisterClass:在AfxWinInit()中;
winmain.cpp中:AfxWinMain()中调用AfxWinInit()与AfxWinTerm()函数;
GetMessage()/DispatchMessage()在CWinApp::Run()中;


windows.h---windef.h


afxcoll.h:凡使用collection集合类等的mfc程序,都必须包含该头文件;
头文件:P263;

ASSERT(表达式);
ASSERT_VALID(变量);

inl文件:内联函数的格式文件;

CDocManager::pStaticDocManager;
CDocManager::pStaticInit;
pDocManager::AddDocTemplate(NULL);
与文档模板有关;

Create()与CreateEx():参数类型有些异同,特别是父窗口,和菜单参数;
LoadFrame():调用Create();

关于打印:
view继承自CEditView;
view::OnDraw()参数传递的是:CPrintDC;
view::OnPaint()功能:主要负责只输出到屏幕而不倒打印机的操作;

DocStringIndex:与文档模板有关的枚举型;7个用/n分割的子串;

引用枚举型的成员:直接类名::成员名;


shell:资源管理器;

拖拉文件消息:WM_DRAGFILES;wParam:存放文件名的全局内存;
要求程序有style:WS_EX_ACCEPTFILES;
该style只能由CreateWindowEx()参数指定;

doc template链的每一种文档类型:CMultiDocTemplate()的第一个参数;
win32:RegCreateKey()与RegSetValue()函数;
MFC:RegisterShellFileTypes(TRUE);

MDI的两组菜单:一个是IDR_MAINFRAME(没有任何子窗口时显示);一个是IDR_SCRIBETYPE(Scribble实例程序);

MDI重新排列子窗口:Cascade-卡片式;Tile-拼贴式;

MVC与MFC
M:Document;
V:View;
C:Doc Template;

UI管理:即view管理,交给frame完成;

程序支持:多个数据类型;
对应多个doc template;
一个管理一种;
注:管理数据类型的多少与MDI/SDI无关;
MDI/SDI:一次能打开的文件的数目不同;


内容倾印(dump)和错误诊断(Trace,Assert);
type-safe(即使用模板对于类型安全检验的支持);


派生自CObject的类,都具有以下性质:
文件读写、运行时类型识别、动态创建;


P350:序列化和倾印规则;
有的可以ar<<、ar>>;
有的必须调用Serialize();

Doc::OnNewDocument,Doc::OnOpenDocument调用以下:
自己的数据初始化;
Doc::InitDocument();

关于OnPaint()与OnDraw():
先调用OnPaint,再调用OnDraw;


异常对象:
cause成员;
Delete();//成员函数;


文件另存为:实质上调用doc::Serialize();
OpenDocumentFile():文件打开
SaveDocumentFile:文件保存
NewDocumentFile():文件新建;

RTTI:RTCI(class);
CRIT_RUNTIMECLASSLIST;P388;//运行类型别录

Serializable类的5大条件:
默认无参类构造函数;
继承自CObject;并且只需要改写ar对象有关的operator>>;(P401)//为了继承ar对象有关的>>、<<重载函数;可以例外:如:CString、CRect、CSize、CPoint类不派生自CObject,因为它们有自己的ar对象有关的>>、<<重载函数;
DELARE_SERIAL();
IMPLEMENT_SERIAL();
复写Serialize()函数;

参数类型:CObject*& pOb;(指针的引用);


自定义Serial宏:
对抽象类要想序列化,是必须的;
#define IMPLEMENT_SERIAL_MY(class_name,base_class_name,wSchema)\
       _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,NULL)\ //注:NULL是函数指针,指向CreateObject();
CArchieve& AFXAPI operator(CArchieve& ar,class_name* &pOb)
{
    pOb=(class_name*)ar.ReadObject(RUNTIME_CLASS(class_name));
    return ar;
}

UI对象:工具栏、菜单项等;
UI对象状态的维护:pCmdUI->Enable(BOOL);//灰-亮;
                  pCmdUI->SetCheck(BOOL);//打钩-不打钩;


IMPLEMENT_SERIAL()宏的第三个参数版本号,很重要,跟保存文档的打开有关,如果不一样的版本,则打不开;

滚动窗口:CScrollView:
OnPrepareDC(&dc);//通过调整GDI原点进而调整DC;对滚动很重要,先调用它,再调用OnDraw();
LPToDP(&rect);//逻辑坐标(document坐标)和设备装置坐标(窗口坐标);
GetClipBox(&rect);

DC:可以是一块内存,一个窗口,一个打印机等;

初始为逻辑坐标的环境/情况:
CView::OnUpdate();
初始为设备坐标的环境/情况:
CView::OnLButtonDown();//----------如何区分,一般doc中的成员信息使用逻辑坐标;而视图成员函数参数用的参数是设备坐标;
SetScrollSizes():P479;

SetCapture();//抓住鼠标;
GetCapturn();//获取当前抓住鼠标的窗口;


子窗口CMDIChildWnd:包容view的框架;
主窗口CMDIFrameWnd: 与CMainFrame一致的框架;
分割窗口CSplitterWnd:P485(紧贴于框架内缘的虚框);相当于MDI Client;

动态拆分窗口:【不同子pane同一个view】
子窗口包含成员:CSplitterWnd m_wndSplit;
然后实现虚函数:OnCreateClient:其内调用m_wndSplit.Create(this,2,2,CSize(),pContext);

静态拆分窗口:【不同子pane不同view】P531
CSplitterWnd成员:CreateStatic()【拆分第一个参数指定的窗口】;CreateView()【在分割窗口指定的行列位置动态创建一个视图】;IdFromRolCol()【有行列号获得一个ID】;GetPane(0,0)【返回值:CWnd*】;
eg:
m_wndSplitter.CreateStatic(this,1,2);
m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(视图类),CSize(100,0),CCreateContext*);
拆分之下再拆分:着手点CreateStatic()函数的第一个参数【两个CSplitterWnd对象】;

CCreateContext类成员:
m_pNewViewClass【当前工程视图的class*】;

关于打印:【viewprnt.cpp,appprnt.cpp,dlgprnt.cpp】P507图12-5;
【win.ini】的device语句包含打印有关的信息  相关于  CreateDC()【获取打印机DC】的前三个参数【驱动名称,打印机名称,端口名称】;
hPr:打印机DC句柄;
Escape();【api函数】传送命令给打印机;
FARPROC:函数指针类型;
MakeProcInstance()与FreeProcInstance():与函数指针有关;

中断函数AbortProc():
由GDI模块通知Printer Manager(即打印机),然后打印机就可以打印了;

获取DC:
GetDC();
CreateDC();
BeginPaint()的返回值;

打印机DC的获得:
方法一:
CreateDC();
方法二:
CDC dc;
dc.Attach(printInfo.m_pPD->m_pd.hDC);//附着在一个打印对话框的hDC上;
dc.Detach();//  最后;

CPrintDC类:
m_bPrinting;
SetAbortProc(函数名);
StartDoc(&docInfo);
EndDoc();
AbortDoc();
StartPage();//开始新的一页,而不是获取;

DOCINFO结构体:
cbSize;
lpszDocName;//文件名称;
lpszOutput;//输出设备【即打印机名称】名称;

GetCurrentMessage():获取当前消息;

CCommandLineInfo类:m_nShellCommand;m_strDriverName;m_strPrinterName;m_strPortName;
CPrintInfo类:m_pPD(CPrintDialog类型);m_bDirect【表示跳过打印对话框,直接打印】;m_bPreview;GetToPage()【获取终止页码】;GetFromPage()【获取起始页码】;m_nCurPage;GetMaxPage();m_nNumPreviewPages【预览显示的页数】;m_bContinuePrinting【可以继续打印,与动态打印页码有关,边打印边查看该变量】;GetDeviceCaps()【参数:HORZRES--获取水平宽】;m_rectDraw【与内容输出有关,页眉页脚占用它的一小部分】;
CPrintDialog类:m_pd(PRINTDIALOG结构体类型);GetPortName();GetDeviceName()【获得打印机名字】;GetPrinterDC();
PRINTDIALOG结构体成员:m_hDC;Flags;

CFileDialog:文件对话框类
m_ofn结构体成员:lpstrTitle;

CPrintingDialog类【打印状态对话框】:


文档标题与窗口标题;
AfxGetFileTitle();
AfxGetMainWnd();

OnPaint()【唯"显示器"】、OnPrint()【唯"打印机"】、OnDraw()【"打印"与"显示"皆可】的关系:

GDI资源:存放于heap中;


MFC编程:
使用模板类,应加<afxtempl.h>;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值