debug版本的 mfc 程序,可以使用 trace 输出调试信息。
或者使用afxDump
try catch的使用,catch有两个参数,第一个为错误类型,第二个是错误对象。
简而言之就是通过使用应用框架(Application Framework)快速开发。
CObject:
数据处理类别(collection classes)
理解为一些容器类
其他类
CWinThread:MFC 程序中的一个执行线程
CWinApp:整个MFC 应用程序。派生于CWinThread
CWnd: 所有窗口,不论是主框窗口、子框窗口、对话框、控制组件、view 视窗,都有一个对应的C++ 类。这些C++ 类统统派生于CWnd,也就是说,派生于CWnd 的类才能收到WM_
窗口消息(WM_COMMAND
除外)。CWnd 对象有一个成员变量m_hWnd
,就放着对应的窗口handle
。所以,只要有一个CWnd 对象或CWnd 对象指针,就可以轻易获得其窗口handle
:
HWND hWnd = pWnd->m_hWnd;
CCmdTarget:- CWnd 的父类。继承它的类才能够处理命令消息WM_COMMAND
GDI 类
DC 类
Menu 类
Document:文档
View:界面
视图UI 对象,例如工具栏CToolBar、状态栏CStatusBar、对话框CDialogBar。
函数 | 功能 |
---|---|
AfxWinInit | 被WinMain调用的一个函数,用做MFC GUI程序初始化的一部份 |
AfxBeginThread | 开始一个线程 |
AfxEndThread | 结束一个线程 |
AfxFormatString1 | 类似printf 将字符串格式化 |
AfxFormatString2 | 类似printf 将字符串格式化 |
AfxMessageBox | 类似Windows API 函数MessageBox |
AfxOutputDebugString | 把调试信息输出到编译器的输出窗口 |
AfxGetApp | 取得application object(CWinApp 衍生对象)的指针 |
AfxGetMainWnd | 取得程序主窗口的指针。 |
AfxGetInstance | 取得程序的instance handle 。 |
AfxRegisterClass | 以自定的WNDCLASS 注册窗口类别;用来注册窗口类的函数,用已注册的窗口类可以创建一个窗口 |
RTTI:(Run-Time Type Identification,运行时类型识别)
Serialization:序列化
Dynamic object creation:动态的对象生成
宏名称 | 提供机能 |
---|---|
DECLARE_DYNAMIC | 执行时期类别信息 |
IMPLEMENT_DYNAMIC | 执行时期类别信息 |
DECLARE_DYNCREATE | 动态生成 |
IMPLEMENT_DYNCREATE | 动态生成 |
DECLARE_SERIAL | 对象内容的文件读写 |
IMPLEMENT_SERIAL | 对象内容的文件读写 |
DECLARE_OLECREATE OLE | 对象的动态生成 |
IMPLEMENT_OLECREATE OLE | 对象的动态生成 |
Message Mapping:消息映射
Command Routing: 命令传递
宏名称 | 提供机能 |
---|---|
DECLARE_MESSAGE_MAP | 声明消息映射表数据结构 |
BEGIN_MESSAGE_MAP | 开始消息映射表的建立 |
ON_COMMAND | 增加消息映射表中的项目 |
ON_CONTROL | 增加消息映射表中的项目 |
ON_MESSAGE | 增加消息映射表中的项目 |
ON_OLECMD | 增加消息映射表中的项目 |
ON_REGISTERED_MESSAGE | 增加消息映射表中的项目 |
ON_REGISTERED_THREAD_MESSAGE | 增加消息映射表中的项目 |
ON_THREAD_MESSAGE | 增加消息映射表中的项目 |
ON_UPDATE_COMMAND_UI | 增加消息映射表中的项目 |
END_MESSAGE_MAP | 结束消息映射表的建立 |
和Win32 程序(SDK 程序)共同使用的数据类型
数据类型 | 含义 |
---|---|
BOOL | Boolean 值(布尔值,不是TRUE 就是FALSE) |
BSTR | 32-bit 字符指针(LPWSTR) |
BYTE | 8-bit 整数,无符号 |
COLORREF | 32-bit 数值,代表一个颜色值 |
DWORD | 32-bit 整数,无符号 |
LONG | 32-bit 整数,带符号 |
LPARAM | 32-bit 数值,做为窗口函数或callback 函数的一个参数 |
LPCSTR | 32-bit 指针,指向一个常数字符串 |
LPSTR | 32-bit 指针,指向一个字符串 |
LPCTSTR | 32-bit 指针,指向一个常数字符串。此字符串可移植到Unicode 和DBCS(双字节字集) |
LPTSTR | 32-bit 指针,指向一个字符串。此字符串可移植到Unicode 和DBCS(双位组字集) |
LPVOID | 32-bit 指针,指向一个未指定类型的资料 |
LPRESULT | 32-bit 数值,做为窗口函数或callback 函数的回返值 |
UINT | 在Win16 中是一个16-bit 无符号整数,在Win32 中是一个32-bit 无符号整数。 |
WNDPROC | 32-bit 指针,指向一个窗口函数 |
WORD | 16-bit 整数,无符号 |
WPARAM | 窗口函数的callback 函数的一个参数。在Win16 中是16 bits,在Win32中是32 bits。 |
下面这些是MFC 独特的数据类型:
数据类型 | 含义 |
---|---|
POSITION | 一个数值,代表collection 对象(例如数组或串行)中的元素位置。常使用于MFC collection classes。 |
LPCRECT | 32-bit 指针,指向一个不变的RECT 结构。 |
常用的宏定义:
#define NULL 0
#define far //Win32 不再有far 或near memory model,而是使用所谓的flat model。pascall 函数调用习惯
#define near //也被stdcall 函数调用习惯取而代之。
#define pascal __stdcall
//在Windows programming演化过程中曾经出现的PASCAL、CALLBACK、WINAPI、APIENTRY
//现在都代表相同的意义,就是stdcall函数调用习惯。
#define cdecl _cdecl
#define CDECL _cdecl
#define CALLBACK __stdcall //
#define WINAPI __stdcall //
#define WINAPIV __cdecl //
#define APIENTRY WINAPI //
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#define FAR far
#define NEAR near
#define CONST const
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef float FLOAT;
typedef FLOAT *PFLOAT;
typedef BOOL near *PBOOL;
typedef BOOL far *LPBOOL;
typedef BYTE near *PBYTE;
typedef BYTE far *LPBYTE;
typedef int near *PINT;
typedef int far *LPINT;
typedef WORD near *PWORD;
typedef WORD far *LPWORD;
typedef long far *LPLONG;
typedef DWORD near *PDWORD;
typedef DWORD far *LPDWORD;
typedef void far *LPVOID;
typedef CONST void far *LPCVOID;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned int *PUINT;
/* Types use for passing & returning polymorphic values */
typedef UINT WPARAM;
typedef LONG LPARAM;
typedef LONG LRESULT;
typedef DWORD COLORREF;
typedef DWORD *LPCOLORREF;
typedef struct tagRECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
typedef const RECT FAR* LPCRECT;
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
typedef struct tagSIZE
{
LONG cx;
LONG cy;
} SIZE, *PSIZE, *LPSIZE;
Windows C Runtime 函数库
(runtime 是一个通用抽象的术语,指的是计算机程序运行的时候所需要的一切代码库,框架,平台等)
文件名称 | 说明 |
---|---|
LIBC.LIB | C Runtime 函数库的静态联结版本 |
MSVCRT.LIB | C Runtime 函数库的动态联结版本 |
MSVCRTD.LIB | ‘D’ 表示使用于Debug 模式 |
DLL Import 函数库
文件名称 | 说明 |
---|---|
GDI32.LIB | for GDI32.DLL |
USER32.LIB | for USER32.DLL |
KERNEL32.LIB | for KERNEL32 DLL |
MFC 函数库 (AFX 函数库)
WINDOWS.H:大合集。
STDAFX.H:预编译头文件,用于包含其他头文件
AFXWIN.H:MFC程序必须包含的头文件,内含所有MFC类。(其中包含了AFX.H
,AFX.H
又包含了AFXVER_.H
,AFXVER_.H
又包含了AFXV_W32.H
,AFXV_W32.H
又包含了WINDOWS.H
)
AFXEXT.H:使用工具栏、状态栏需要包含的头文件
AFXDLGS.H:对话框
AFXCMN.H:win95通用组件
AFXCOLL.H:Collections Classes
AFXDLLX.H:extension DLL
AFXRES.H:MFC标准资源
预编译头 就是将.H 文件第一次编译后的结果贮存起来,第二次再编译时就可以直接从磁盘中取出来用。有效减少大型应用程序的编译时间。
新建空项目,增加如下文件:
resource.h
需要在最后面增加一个回车
#define IDM_ABOUT 100
hello.rc
#include "resource.h"
#include "afxres.h"
JJHouRIcon ICON DISCARDABLE "JJHOUR.ICO"
AFX_IDI_STD_FRAME ICON DISCARDABLE "JJHOUR.ICO"
MainMenu MENU DISCARDABLE
{
POPUP "&Help"
{
MENUITEM "&About HelloMFC...", IDM_ABOUT
}
}
AboutBox DIALOG DISCARDABLE 34, 22, 147, 55
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About Hello"
{
ICON "JJHouRIcon", IDC_STATIC, 11, 17, 18, 20
LTEXT "Hello MFC 4.0", IDC_STATIC, 40, 10, 52, 8
LTEXT "Copyright 1996 Top Studio", IDC_STATIC, 40, 25, 100, 8
LTEXT "J.J.Hou", IDC_STATIC, 40, 40, 100, 8
DEFPUSHBUTTON "OK", IDOK, 105, 7, 32, 14, WS_GROUP
}
stdafx.h
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently,
// but are changed infrequently
#include // MFC core and standard components
stdafx.cpp
// stdafx.cpp : source file that includes just the standard includes
// Hello.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
hello.h
class CMyWinApp : public CWinApp
{
public:
BOOL InitInstance(); //
};
//---------------------------------------------------------------
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd(); // constructor
afx_msg void OnPaint(); // for WM_PAINT
afx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)
private:
DECLARE_MESSAGE_MAP() // Declare Message Map
static VOID CALLBACK LineDDACallback(int, int, LPARAM);//注意: callback 函数必须是"static",才能去除隐藏的'this' 指针。
};
hello.cpp
#include "stdafx.h"
#include "hello.h"
#include "resource.h"
CMyWinApp theApp; // application object
//---------------------------------------------------------------
// CMyWinApp's member
//---------------------------------------------------------------
BOOL CMyWinApp::InitInstance()
{
m_pMainWnd = new CMyFrameWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
//---------------------------------------------------------------
// CMyFrameWnd's member
//---------------------------------------------------------------
CMyFrameWnd::CMyFrameWnd()
{
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu"); // "MainMenu" 定义于 RC 档
}
//---------------------------------------------------------------
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
//---------------------------------------------------------------
void CMyFrameWnd::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(rect);
dc.SetTextAlign(TA_BOTTOM | TA_CENTER);
//沿着定义的起始点与结束点组成的直线,重复执行指定的LINEDDAPROC回调函数。在线条的每个坐标点(结束点除外)上执行一次回调函数。
::LineDDA(rect.right / 2, 0, rect.right / 2, rect.bottom / 2,(LINEDDAPROC)LineDDACallback, (LPARAM)(LPVOID)&dc);
}
//---------------------------------------------------------------
VOID CALLBACK CMyFrameWnd::LineDDACallback(int x, int y, LPARAM lpdc)
{
static char szText[] = "Hello, MFC,textdraw";
((CDC*)lpdc)->TextOut(x, y, szText, sizeof(szText) - 1);
for (int i = 1; i < 5000000; i++); // 纯粹是为了延迟下降速度,以利观察
}
//---------------------------------------------------------------
void CMyFrameWnd::OnAbout()
{
CDialog about("AboutBox", this); //"AboutBox" 定义于RC 文档
about.DoModal();
}
需要在工程目录中增加一个图标文件JJHOUR.ICO。
工程配置修改如下:
预处理器的内容不增加也可以编译通过
其中说明:
#define CALLBACK __stdcall // 一种函数调用习惯
#define afx_msg // intentional placeholder故意安排的一个空位置。也许以后版本会用到。
CWinApp 代表程序本体,包含了WinMain
。
CFrameWnd代表一个主框窗口(Frame Window),包含了WndProc
。
CWinApp部分成员:
class CWinApp : public CWinThread
{
// Attributes
// Startup args (do not change)
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPTSTR m_lpCmdLine;
int m_nCmdShow;
// Running args (can be changed in InitInstance)
LPCTSTR m_pszAppName; // human readable name
LPCTSTR m_pszRegistryKey; // used for registry entries
public: // set in constructor to override default
LPCTSTR m_pszExeName; // executable name (no spaces)
LPCTSTR m_pszHelpFilePath; // default based on module path
LPCTSTR m_pszProfileName; // default based on app name
public:
// hooks for your initialization code
virtual BOOL InitApplication();
// overrides for implementation
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual int Run();
virtual BOOL OnIdle(LONG lCount);
};
传统上SDK 程序的WinMain
(windows程序入口) 所完成的工作现在由CWinApp
的三个函数完成:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
CWinThread部分成员
class CWinThread : public CCmdTarget
{
// Attributes
CWnd* m_pMainWnd; // main window (usually same AfxGetApp()->m_pMainWnd)
CWnd* m_pActiveWnd; // active main window (may not be m_pMainWnd)
// only valid while running
HANDLE m_hThread; // this thread's HANDLE
DWORD m_nThreadID; // this thread's ID
int GetThreadPriority();
BOOL SetThreadPriority(int nPriority);
// Operations
DWORD SuspendThread();//挂起线程
DWORD ResumeThread();//唤醒线程
// Overridables
// thread initialization
virtual BOOL InitInstance();
// running and idle processing
virtual int Run();
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL PumpMessage(); // low level message pump
virtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing
public:
// valid after construction
AFX_THREADPROC m_pfnThreadProc;
...
};
CFrameWnd实现窗口过程的函数如下:
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd();
afx_msg void OnPaint();//处理 WM_PAINT
afx_msg void OnAbout();//处理 WM_COMMAND 的 IDM_ABOUT
DECLARE_MESSAGE_MAP()
};
通过宏DECLARE_MESSAGE_MAP
、 BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
、
END_MESSAGE_MAP()
实现消息与对应函数的映射。
首先查看mfc的代码WinMain.cpp
int AFXAPI AfxWinMain (...)
{
CWinApp* pApp = AfxGetApp();
AfxWinInit(...);
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
AfxWinTerm();
}
然后是我们模拟的例子hello.cpp
CMyWinApp theApp; // application object
BOOL CMyWinApp::InitInstance()
{
m_pMainWnd = new CMyFrameWnd();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
CMyFrameWnd::CMyFrameWnd()
{
Create(NULL, "Hello MFC", ...,"MainMenu");
}
void CMyFrameWnd::OnPaint() { ... }
void CMyFrameWnd::OnAbout() { ... }
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_WM_PAINT()
END_MESSAGE_MAP()
theApp
就是一个应用程序对象(Application object)。每一个MFC 应用程序都有一个,而且也只有这么一个。当执行hello.cpp时,这个全局对象被创建,执行其构造函数,此构造函数在父类CWinApp中:
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
m_pszAppName = lpszAppName;
// initialize CWinThread state
AFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();
pThreadState->m_pCurrentWinThread = this;
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// initialize CWinApp state
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_pCurrentWinApp = this;
// in non-running state until WinMain
m_hInstance = NULL;
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
...
}
theApp
创建后就会调用WinMain
函数。WinMain函数实际上是通过调用AfxWinMain函数来完成它的功能的。
_tWinMain
调用 AfxWinMain
(APPMODUL.CPP)。
int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
ASSERT_VALID(pApp);
if (!pApp->InitApplication())
goto InitFailure;
ASSERT_VALID(pApp);
// Perform specific initializations
if (!pApp->InitInstance())
{
if (pApp->m_pMainWnd != NULL)
{
TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
pApp->m_pMainWnd->DestroyWindow();
}
nReturnCode = pApp->ExitInstance();
goto InitFailure;
}
ASSERT_VALID(pApp);
nReturnCode = pApp->Run();
ASSERT_VALID(pApp);
InitFailure:
AfxWinTerm();
return nReturnCode;
}
以上代码简化下就是:
int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
AfxWinTerm();
return nReturnCode;
}
AfxGetApp
是一个全局函数,定义于(afxwin1.inl)中
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
{ return afxCurrentWinApp; }
afxCurrentWinApp
定义于(afxwin.h*)中
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
AfxGetApp 其实就是取得CMyWinApp 对象指针即theApp
所以afxwinmain就相当于:
CWinApp* pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
也就是顺序调用:
CMyWinApp::InitApplication();
CMyWinApp::InitInstance();
CMyWinApp::Run();
考虑重写就是:
CWinApp::InitApplication(); //因为 CMyWinApp 并没有改写InitApplication
CMyWinApp::InitInstance(); //因为 CMyWinApp 改写了 InitInstance
CWinApp::Run(); //因为 CMyWinApp 并没有改写Run
AfxWinInit
是winmain中首先调用的,代码如下
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// set resource handles
AFX_MODULE_STATE* pState = AfxGetModuleState();
pState->m_hCurrentInstanceHandle = hInstance;
pState->m_hCurrentResourceHandle = hInstance;
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
// initialize thread specific data (for main thread)
if (!afxContextIsDLL)
AfxInitThread();
return TRUE;
}
其中 AfxInitThread
:
void AFXAPI AfxInitThread()
{
if (!afxContextIsDLL)
{
// attempt to make the message queue bigger
for (int cMsg = 96; !SetMessageQueue(cMsg) && (cMsg -= 8); )
;
// set message filter proc
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,
_AfxMsgFilterHook, NULL, ::GetCurrentThreadId());
// intialize CTL3D for this thread
_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
if (pCtl3dState->m_pfnAutoSubclass != NULL)
(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());
// allocate thread local _AFX_CTL3D_THREAD just for automatic termination
_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;
}
}
BOOL CWinApp::InitApplication()
{
if (CDocManager::pStaticDocManager != NULL)
{
if (m_pDocManager == NULL)
m_pDocManager = CDocManager::pStaticDocManager;
CDocManager::pStaticDocManager = NULL;
}
if (m_pDocManager != NULL)
m_pDocManager->AddDocTemplate(NULL);
else
CDocManager::bStaticInit = FALSE;
return TRUE;
}
即调用hello.cpp中重写的 InitInstance
, 在该处创建主窗口。
应用程序一定要改写虚拟函数InitInstance
,因为它在CWinApp
中只是个空函数。
CMyFrameWnd::CMyFrameWnd
{
//调用据CFrameWnd::Create
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL,"MainMenu");
}
CFrameWnd::Create 的参数如下
BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle = WS_OVERLAPPEDWINDOW,
const RECT& rect = rectDefault,
CWnd* pParentWnd = NULL,
LPCTSTR lpszMenuName = NULL,
DWORD dwExStyle = 0,
CCreateContext* pContext = NULL
);
参数说明:
WNDCLASS
窗口类型,可以为NULL。#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | WS_CAPTION |WS_SYSMENU | WS_THICKFRAME |WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
Create(NULL,"Hello MFC",WS_OVERLAPPEDWINDOW,
CRect(40, 60, 240, 460), // 起始位置 (40,60),寬 200,高 400)
NULL,
"MainMenu");
其简略实现如下:
//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);
hMenu = ::LoadMenu(hInst, lpszMenuName);
}
m_strTitle = lpszWindowName; // save title for later
CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext);
return TRUE;
}
CFrameWnd
没有 CreateEx
函数,所以调用继承来的 CWnd::CreateEx
;
//WINCORE.CPP
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
PreCreateWindow(cs);
AfxHookWindowCreate(this); //
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);
...
}
PreCreateWindow
是虚函数,CWnd
和CFrameWnd
之中都有定义,这里调用是CFrameWnd::PreCreateWindow
//WINFRM.CPP
// CFrameWnd second phase creation
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
}
...
}
宏中AfxDeferRegisterClass
定义如下:
//AFXIMPL.H
#define AfxDeferRegisterClass(fClass) \
((afxRegisteredClasses & fClass) ? TRUE : AfxEndDeferRegisterClass(fClass))
如果变量afxRegisteredClasses
的值显示系统已经注册了fClass
这种窗口类别,MFC 就啥也不做;否则就调用AfxEndDeferRegisterClass(fClass)
注册。
// in AFXWIN.H
#define afxRegisteredClasses AfxGetModuleState()->m_fRegisteredClasses
注册函数如下:
BOOL AFXAPI AfxEndDeferRegisterClass(short fClass)
{
BOOL bResult = FALSE;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
if (fClass & AFX_WND_REG)
{
// Child windows - no brush, no icon, safest default class styles
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWnd;
bResult = AfxRegisterClass(&wndcls);
if (bResult)
pModuleState->m_fRegisteredClasses |= AFX_WND_REG;
}
else if (fClass & AFX_WNDOLECONTROL_REG)
{
// OLE Control windows - use parent DC for speed
wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpszClassName = _afxWndOleControl;
bResult = AfxRegisterClass(&wndcls);
if (bResult)
pModuleState->m_fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
}
else if (fClass & AFX_WNDCONTROLBAR_REG)
{
// Control bar windows
wndcls.style = 0; // control bars don't handle double click
wndcls.lpszClassName = _afxWndControlBar;
wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
bResult = AfxRegisterClass(&wndcls);
if (bResult)
pModuleState->m_fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;
}
else if (fClass & AFX_WNDMDIFRAME_REG)
{
// MDI Frame window (also used for splitter window)
wndcls.style = CS_DBLCLKS;
wndcls.hbrBackground = NULL;
bResult = RegisterWithIcon(&wndcls, _afxWndMDIFrame,AFX_IDI_STD_MDIFRAME);
if (bResult)
pModuleState->m_fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
}
else if (fClass & AFX_WNDFRAMEORVIEW_REG)
{
// SDI Frame or MDI Child windows or views - normal colors
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
bResult = RegisterWithIcon(&wndcls, _afxWndFrameOrView,AFX_IDI_STD_FRAME);
if (bResult)
pModuleState->m_fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
}
else if (fClass & AFX_WNDCOMMCTLS_REG)
{
InitCommonControls();
bResult = TRUE;
pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;
}
return bResult;
}
其中的的常数定义:
#define AFX_WNDCLASS(s) \
_T("Afx") _T(s) _T("42") _STATIC_SUFFIX _UNICODE_SUFFIX _DEBUG_SUFFIX
#define AFX_WND AFX_WNDCLASS("Wnd")
#define AFX_WNDCONTROLBAR AFX_WNDCLASS("ControlBar")
#define AFX_WNDMDIFRAME AFX_WNDCLASS("MDIFrame")
#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")
#define AFX_WNDOLECONTROL AFX_WNDCLASS("OleControl")
所以debug编译时这5个窗口的名称是
"AfxWnd42d"
"AfxControlBar42d"
"AfxMDIFrame42d"
"AfxFrameOrView42d"
"AfxOleControl42d"
AfxEndDeferRegisterClass
注册中使用的函数:
static BOOL AFXAPI RegisterWithIcon(WNDCLASS* pWndCls,
LPCTSTR lpszClassName, UINT nIDIcon)
{
pWndCls->lpszClassName = lpszClassName;
HINSTANCE hInst = AfxFindResourceHandle(
MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON);
if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL)
{
// use default icon
pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
}
return AfxRegisterClass(pWndCls);
}
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;
if (GetClassInfo(lpWndClass->hInstance,
lpWndClass->lpszClassName, &wndcls))
{
// class already registered
return TRUE;
}
::RegisterClass(lpWndClass);
...
return TRUE;
}
不同类的PreCreateWindow
成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类。如果我们指定的窗口类是NULL
,那么就使用系统默认类。
// in WINCORE.CPP
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
{
AfxDeferRegisterClass(AFX_WND_REG);
...
cs.lpszClass = _afxWnd; //(这表示CWnd 使用的窗口类别是_afxWnd)
}
return TRUE;
}
// in WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
...
cs.lpszClass = _afxWndFrameOrView; //(这表示CFrameWnd 使用的窗口类别是_afxWndFrameOrView)
}
...
}
// in WINMDI.CPP
BOOL CMDIFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG);
//(这表示CMDIFrameWnd 使用的窗口类别是_afxWndMDIFrame)
...
cs.lpszClass = _afxWndMDIFrame;
}
return TRUE;
}
// in WINMDI.CPP
BOOL CMDIChildWnd::PreCreateWindow(CREATESTRUCT& cs)
{
...
return CFrameWnd::PreCreateWindow(cs);
//(这表示CMDIChildWnd 使用的窗口类别是_afxWndFrameOrView)
}
// in VIEWCORE.CPP
BOOL CView::PreCreateWindow(CREATESTRUCT & cs)
{
if (cs.lpszClass == NULL)
{
AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);
//(这表示CView 使用的窗口类别是_afxWndFrameOrView)
...
cs.lpszClass = _afxWndFrameOrView;
}
...
}
选择vs自带安装工具:
选中并拖动 查找工具 图标到指定窗口上,可以查看窗口的信息
但是这里没有看到示例的Afx:x:y:z:w
格式窗口名称。
此格式中:
x: 窗口风格(window style)的hex
y: 窗口鼠标光标的hex 值 值
z: 窗口背景颜色的hex 值
w: 窗口图标(icon)的hex 值
这种情况如果修改窗口名称,需要在BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
中修改。
调用ShowWindow
显示窗口
调用UpdateWindow
向新窗口发送WM_PAINT
消息
窗口注册→窗口产生并显示→调用UpdateWindow
→产生WM_PAINT
消息,等待被处理。
此时该执行pApp->run()
,由于没有重写该方法(一般也不用重写),所以相当于调用CMyWinApp::Run()
//APPCORE.CPP
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting
application.\n");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
// THRDCORE.CPP
int CWinThread::Run()
{
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
return FALSE;
}
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
获得的消息后SDK 程序的作法是调用DispatchMessage,把消息丢给窗口函数。而MFC在AfxEndDeferRegisterClass
源代码中注册四种窗口类之前已经指定窗口函数为:
wndcls.lpfnWndProc = DefWindowProc;
虽然窗口函数被指定为DefWindowProc
成员函数,但事实上消息是一个名为AfxWndProc
的全局函数处理。
WinMain 由MFC 提供,窗口类由MFC 注册完成,窗口函数也由MFC提供。
CMyFrameWnd
加上DECLARE_MESSAGE_MAP
:class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd();
afx_msg void OnPaint();
afx_msg void OnAbout();
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_COMMAND(IDM_ABOUT, OnAbout)
END_MESSAGE_MAP()
如上,就把WM_PAINT
导到OnPaint
函数, 把WM_COMMAND(IDM_ABOUT)
导到OnAbout
函数去了。
Message Map 机制中对于消息与函数间的映射关系也规定以下三种:
宏名称 | 映射的消息 | 消息处理函数(名称已由系统定好) |
---|---|---|
ON_WM_CHAR | WM_CHAR | OnChar |
ON_WM_CLOSE | WM_CLOSE | OnClose |
ON_WM_CREATE | WM_CREATE | OnCreate |
ON_WM_DESTROY | WM_DESTROY | OnDestroy |
ON_WM_LBUTTONDOWN | WM_LBUTTONDOWN | OnLButtonDown |
ON_WM_LBUTTONUP | WM_LBUTTONUP | OnLButtonUp |
ON_WM_MOUSEMOVE | WM_MOUSEMOVE | OnMouseMove |
ON_WM_PAINT | WM_PAINT | OnPaint |
命令消息(WM_COMMAND)的一般映射规则:
ON_COMMAND(
例如:
ON_COMMAND(IDM_ABOUT, OnAbout)
ON_COMMAND(IDM_FILENEW, OnFileNew)
ON_COMMAND(IDM_FILEOPEN, OnFileOpen)
ON_COMMAND(IDM_FILESAVE, OnFileSave)
Notification 消息(由控件产生,例如BN_xxx)的映射机制的宏分为几种(因为控件本就分为几种):
控件 | 宏名称 | 消息处理函数 |
---|---|---|
Button | ON_BN_CLICKED(,) | memberFxn |
ComboBox | ON_CBN_DBLCLK(,) | memberFxn |
Edit | ON_EN_SETFOCUS(,) | memberFxn |
ListBox | ON_LBN_DBLCLK(,) | memberFxn |
如果没有指定消息映射函数,消息会在基类中处理,这个消息在基类流窜动作称为「Message Routing」
CMyWinApp theApp
)CMyWinApp::InitInstance
new 一个 CMyFrameWnd 对象
。Create
,产生主窗口。我们在Create 参数中指定的窗口类别是NULL, 于是MFC 根据窗口种类, 自行为我们注册一个名为"AfxFrameOrView42d" 的窗口类。ShowWindow
,显示窗口UpdateWindow
,于是发出 WM_PAINT。Run
,进入消息循环LineDDA函数:
void WINAPI LineDDA(int, int, int, int, LINEDDAPROC, LPARAM);
利用前四个参数指定屏幕上任意两点的(x,y) 座标,此函数将以Bresenham 算法计算出通过两点之直线中的每一个屏幕图素座标;每计算出一个坐标,就通知由LineDDA 第五个参数所指定的callback 函数。
LineDDA 的第六个(最后一个)参数可以视应用程序的需要传递一个32 位指针.
LineDDA 的 callback 函数:
typedef void (CALLBACK* LINEDDAPROC)(int, int, LPARAM);
LineDDA 并不属于任何一个MFC 类别,因此调用它必须使用C++ 的"scope operator"(也就是::)
::LineDDA(rect.right/2, 0, rect.right/2, rect.bottom/2,(LINEDDAPROC) LineDDACallback, (LPARAM) (LPVOID) &dc);
其中LineDDACallback 是我们准备的callback 函数,必须在类中先有声明:
class CMyFrameWnd : public CFrameWnd
{
...
private:
static VOID CALLBACK LineDDACallback(int,int,LPARAM);
};
类的成员函数是一个callback 函数, 你必须声明它为"static",才能把C++ 编译器加到函数的一个隐藏参数this 去掉
CWinThreadd::OnIdle函数:
virtual BOOL OnIdle(LONG lCount);
lCount 是系统传进来的一个值,表示自从上次有消息进来,到现在,OnIdle 已经被调用了多少次。
lCount 会持续累增,直到CWinThread::Run 的消息循环又获得了一个消息,此值才重置为0
测试重写onIdle
重写app的OnIdle函数:
窗口类增加IdleTimeHandler
函数:
class CMyFrameWnd : public CFrameWnd
{
public:
CMyFrameWnd(); // constructor
afx_msg void OnPaint(); // for WM_PAINT
afx_msg void OnAbout(); // for WM_COMMAND (IDM_ABOUT)
void IdleTimeHandler(LONG lCount); // we want it call by CMyWinApp::OnIdle
...
};
app OnIdle函数的定义:
BOOL ChelloworldApp::OnIdle(LONG lCount)
{
// TODO: 在此添加专用代码和/或调用基类
ChelloworldDlg* pWnd = (ChelloworldDlg*)m_pMainWnd;
pWnd->IdleTimeHandler(lCount);
return TRUE;
return CWinApp::OnIdle(lCount);
}
窗口类函数CMyFrameWnd::IdleTimeHandler
的实现
void CMyFrameWnd::IdleTimeHandler(LONG lCount)
{
CString str;
CRect rect(10,10,200,30);
CDC* pDC = new CClientDC(this);
str.Format(_T("%010d"), lCount);
pDC->DrawText(str, &rect, DT_LEFT | DT_TOP);
}
运行CWinThread::Run,则需要pThread->InitInstance返回才行,但模态对话框程序有点特殊,会直接阻塞在DoMoDal函数中,然后运行自己的消息循环,CXXXApp::InitInstace在程序没结束时,不会返回,故OnIdle不会得到运行;我用的是基于文档的mfc向导。
当使用者按下【File/About 】菜单, 根据Message Map 的设定,WM_COMMAND(IDM_ABOUT)被送到OnAbout 函数去。我们首先在OnAbout 中产生一个CDialog 对象,名为about。接下来直接调用CDialog::DoModal,对话框就开始运行。
类 | 描述 |
---|---|
CCommonDialog | 以下各类别的父类别 |
CFileDialog | File 对话框(Open 或Save As) |
CPrintDialog | Print 对话框 |
CFindReplaceDialog | Find and Replace 对话框 |
CColorDialog | Color 对话框 |
CFontDialog | Font 对话框 |
CPageSetupDialog | Page Setup 对话框(MFC 4.0 新增) |
COleDialog | Ole 相关对话框 |
char szFileters[] = "Text fiels (*.txt)|*.txt|All files (*.*)|*.*||"
CFileDialog opendlg (TRUE, "txt", "*.txt", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters, this);
if (opendlg.DoModal() == IDOK)
{
filename = opendlg.GetPathName();
}
第五个参数szFilters 指定使用者可以选择的文件型态,
最后一个参数是父窗口。
当DoModal 回返,我们可以利用CFileDialog 的成员函数
GetPathName
取得完整的文件路径
也可以使用另一个成员函数GetFileName
取其不含路径的文件名称
或GetFileTitle
取得既不含路径亦不含扩展名的文件名称。
一开始是一个派生自CWinApp
的全局对象application object
,然后是一个隐藏的WinMain
函数,调用application object
的InitInstance
函数,将程序初始化。初始化动作包括构造一个窗口对象(CFrameWnd
对象),而其构造函数又调用CFrameWnd::Create
产生真正的窗口(并在产生之前要求MFC注册窗口类别)。窗口产生后WinMain
又调用Run
激活消息循环,将WM_COMMAND(IDM_ABOUT)
和WM_PAINT
分别交给成员函数OnAbout
和OnPaint
处理。
document窗口:如word、excel可以操作多份文件,每一个文件作为一个 document。
文件窗口
关于窗口
打印及预览窗口
Document 是数据
View 是数据的表现、视图
View 本身虽然已经是一个窗口,其外部却必须再包装一个外框窗口做为舞台。这样是为了让View 可以独立地放置于「MDI Document Frame 窗口」或「SDI Document Frame 窗口」或「OLE Document Frame 窗口」等各种应用之中。也可以说,Document Frame 窗口是View 窗口的一个容器。
文档内容、文档视图、文档的窗口是一个组合,程序打开一个资料,就会产生对应的三个对象:
BOOL CScribbleApp::InitInstance()
{
...
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_SCRIBTYPE,
RUNTIME_CLASS(CScribbleDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CScribbleView));
AddDocTemplate(pDocTemplate);
...
}
其中,构造函数:
CMultiDocTemplate::CMultiDocTemplate(
UINT nIDResource,
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass
);
中,参数:
nIDResource:这是一个资源ID,表示此一文件类型(文件格式)所使用的资源。
pDocClass: 这是一个指针,指向Document 类
pFrameClass: 这是一个指针,指向Child Frame 类
pViewClass : 这是一个指针,指向View 类
可以获取CDocTemplate文件类型用到的七个常量,如 filterExt:
CDocTemplate *pDoc1 = this->m_pDocument->GetDocTemplate();
CString strDefExt;
pDoc1->GetDocString(strDefExt, CDocTemplate::filterExt);
view中常用的相应函数:
OnDraw:画面重绘
OnLButtonDown:鼠标左键按下(作为鼠标事件的举例)
void CmultiDocTempView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//在类视图中,CmultiDocTempView类右键属性中,找到消息,选择需要重写的消息
CView::OnLButtonDown(nFlags, point);
MessageBox(_T("hello lbutton down"));
}
创建过程:
由 CMainFrame 的 LoadFrame 到 OnCreate。
工具栏相关 主要动作和设置:
//简单形式
m_wndToolBar.Create(this)
//向导产生的
m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC)
m_wndToolBar.LoadToolBar(IDR_MAINFRAME)
//或者
m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME)
LoadToolBar 知道如何把BITMAP 资源和TOOLBAR 资源搭配起来,完成工具栏的设定。如果不是使用资源工具来编辑工具栏,BITMAP 资源和TOOLBAR 资源就可能格数不符,那是不被允许的。
状态栏相关的动作:
m_wndStatusBar.Create(this)
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
主要讲述在initInstance
中的应用的三个函数:
BOOL CScribbleApp::InitInstance()
{
// Enable drag/drop open
m_pMainWnd->DragAcceptFiles();
// Enable DDE Execute open
EnableShellOpen();
RegisterShellFileTypes(TRUE);
}
CWnd::DragAcceptFile(BOOL bAccept=TRUE);
参数TRUE 表示你的主窗口以及每一个子窗口(文件窗口)都愿意接受来自Shell 的拖放文件。CFrameWnd 内有一个OnDropFiles
成员函数,负责对WM_DROPFIELS
消息做出反应,它会通知 application 对象的OnOpenDocument
,并夹带被拖放的文件的名称。
CWinApp::EnableShellOpen();
当使用者在Shell 中对着本程序的文件文件快按两下时,本程序能够打开文件并读内容。通常此函数后面跟随着RegisterShellFileTypes。(我理解为文件默认打开的应用程序注册。)
CWinApp::RegisterShellFileTypes();
此函数将向Shell 注册本程序的文件类型。有了这样的注册动作,使用者在Shell 的双击动作才有着力点。这个函数搜寻Document Template 串行中的每一种文件类型,然后把它加到系统所维护的registry中。
每一个派生自CCmdTarget 的类别都可以有自己的Message Map 以处理消息。
需要在头文件中声明DECLARE_MESSAGE_MAP
宏,
定义中BEGIN_MESSAGE_MAP
和END_MESSAGE_MAP
两个宏 之间,声明消息和对应的处理函数。
菜单消息及其映射函数
菜单内容 | 命令项ID | 预设的处理函数 |
---|---|---|
File | ||
New | ID_FILE_NEW | CWinApp::OnFileNew |
Open | ID_FILE_OPEN | CWinApp::OnFileOpen |
Close | ID_FILE_CLOSE | CDocument::OnFileClose |
Save | ID_FILE_SAVE | CDocument::OnFileSave |
Save As | ID_FILE_SAVEAS | CDocument::OnFileSaveAs |
ID_FILE_PRINT | CView::OnFilePrint | |
Pre&view | ID_FILE_PRINT_PREVIEW CView::OnFilePrintPreview | |
Setup | ID_FILE_PRINT_SETUP CWinApp::OnFilePrintSetup | |
“Recent File Name” | ID_FILE_MRU_FILE1~4 | CWinApp::OnOpenRecentFile |
Exit | ID_APP_EXIT | CWinApp::OnFileExit |
Edit | ||
Undo | ID_EDIT_UNDO | None |
Cut | ID_EDIT_CUT | None |
Copy | ID_EDIT_COPY | None |
Paste | ID_EDIT_PASTE | None |
View | ||
Toolbar | ID_VIEW_TOOLBAR | FrameWnd::OnBarCheck Yes |
Status | Bar ID_VIEW_STATUS_BAR | FrameWnd::OnBarCheck Yes |
Window(MDI only) | ||
New | Window ID_WINDOW_NEW | MDIFrameWnd::OnWindowNew Yes |
Cascade | ID_WINDOW_CASCADE | MDIFrameWnd::OnWindowCmd Yes |
Tile | ID_WINDOW_TILE_HORZ | MDIFrameWnd::OnWindowCmd Yes |
Arrange Icons | ID_WINDOW_ARRANGE | MDIFrameWnd::OnWindowCmd Yes |
Help | ||
About AppName | ID_APP_ABOUT | None |
响应函数
BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
END_MESSAGE_MAP()
CAboutDlg 是CDialog 的派生类
替换基类从CView 改为CEditView