深入浅出MFC 第十四章 多线程程序设计------读书笔记

侯捷老师分别从两个方面介绍线程:①从操作系统层面看线程。②从程序设计角度看线程。
下面我们分别介绍:
一、从操作系统层面看线程
1.我们可以用Win32的CreateThread()函数创建一个线程。CreateThread()函数的有6个参数介绍如下:
CreateThread()函数在processthreadapi.h的原型如下:

WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateThread(
   _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
   _In_ SIZE_T dwStackSize,
   _In_ LPTHREAD_START_ROUTINE lpStartAddress,
   _In_opt_ __drv_aliasesMem LPVOID lpParameter,
   _In_ DWORD dwCreationFlags,
   _Out_opt_ LPDWORD lpThreadId
   );

参数1:是线程的安全防护属性,默认为NULL。参数2:线程堆栈的大小。参数3:线程函数的地址(执行事实)。参数4:传递给线程函数的参数,用户可自定义。参数5:线程创建的标记。默认为0,表示线程立即执行。也可以是CREATE_SUSPENED,表示挂起(暂停运行),后面使用ResumeThread()可恢复运行。参数6:线程的ID号。关于该函数的详细说明可以查看微软官方文档CreateThread的说明
2.接下来,提出了几个问题:
①操作系统如何造成这种多任务并行的现象?
答:
多个CPU情况:
如果在多个CPU的情况下,并且当前的操作系统要支持多任务才行(比如Windows NT) ,那么一个CPU就可以分到一个线程,多个线程就可以真正的同时执行,真正的做到多任务。这种操作系统特性成为Symmetric Mulitprocessing( SMP)。
一个CPU情况:
多个线程“同时执行”的幻觉是靠调度器来完成的。它在不同的线程之间来回切换。以Windows 95 和Windows NT 而言,一般情况下每一个线程的时间片一般都是20ms。
②线程对于操作系统的意义是什么?
多线程程序可以提高程序的执行效率。相比较于创建多个进程,在一个进程创建多个线程的速度更快,占用的资源更少。
③系统如何维护多个线程?
答:操作系统以一个数据结构Thread Database(TDB)来记录每一个线程的所有数据。包括线程的局部存储空间Thread Local Storage(TLS),消息队列,handle表格,地址空间,Memory Context等等。
④线程与其父亲大人(进程)的关系是如何维持?
对于这个问题,首先介绍3个概念:模块,线程,进程。
模块:一个DLL是一个模块,一个exe是一个模块。具体定义:一段可执行的程序,包括exe和DLL。其程序代码,数据,资源被加载到内存中,由系统建置一个数据结构来管理它,就是一个模块。
进程:进程强调的是拥有权(ownership)的集合。进程拥有地址空间由(Memory Context 决定),动态配置而来的内存,文件,线程等一系列模块。操作系统使用一个叫做Process Database(PDB)来维护一个进程。
线程:线程强调的是“执行事实”。
答:一开始进程是以一个主线程(Primary Thread)开始的,进程可以创建多个线程,让CPU在不同的时间内执行不同的代码。
⑤CPU只有一个,线程有多个如何摆平优先级与调度问题?
调度器切换线程的是通过线程的优先级来进行调度的。线程的优先级越高,被调度的几率越高,不是说优先级最低的线程永远不会被调用,优先级强调的是被调用的几率。为了避免优先级低的线程永远不会调用的情况发生,操作系统会动态调整线程的优先级,使优先级较低的线程获得被调用的几率增大。如果某个线程会获取消息,但是使用GetMessage()获取消息一直为空,即使他的优先级很高那么他也会被挂起。
在进程方面补充一幅图:在这里插入图片描述
3.Thread ConText(线程上下文):什么是线程上下文,侯捷老师给出的解释是一组缓存器值(包括指令IP)。原因是由于线程经常被暂停执行,会把CPU资源让出来。因此需要记录当前线程的状态,以便在后面恢复。
二、从程序设计角度看线程
1.首先介绍与线程有关的Win32API:

// AttachThreadInput:将某个线程的输入导向另一个线程
BOOL AttachThreadInput([in] DWORD idAttach,[in] DWORD idAttachTo,[in] BOOL  fAttach);
// CreateThread:产生一个线程
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    _In_opt_ __drv_aliasesMem LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
    );
 // ExitThread:结束一个线程
 WINBASEAPI
DECLSPEC_NORETURN
VOID
WINAPI
ExitThread(
    _In_ DWORD dwExitCode
    );
 // GetCurrentThread:取得当前线程的Handle
WINBASEAPI
HANDLE
WINAPI
GetCurrentThread(
    VOID
    );
// GetCurrentThreadId:取得当前线程的Id
WINBASEAPI
DWORD
WINAPI
GetCurrentThreadId(
    VOID
    );
// GetExitCodeThread:取得某以线程的结束代码(可用以决定线程是否已结束)
WINBASEAPI
_Success_(return != 0)
BOOL
WINAPI
GetExitCodeThread(
    _In_ HANDLE hThread,
    _Out_ LPDWORD lpExitCode
    );
// GetPriorityClass:取得某一进程的优先级等级
WINBASEAPI
DWORD
WINAPI
GetPriorityClass(
    _In_ HANDLE hProcess
    );
// GetQueueStatus: 传回某以一线程的消息队列状态
WINUSERAPI
DWORD
WINAPI
GetQueueStatus(
    _In_ UINT flags);
// GetThreadContext: 获取某一线程的context
WINBASEAPI
BOOL
WINAPI
GetThreadContext(
    _In_ HANDLE hThread,
    _Inout_ LPCONTEXT lpContext
    );
// GetThreadDesktop:获取某一线程的Desktop对象
WINUSERAPI
HDESK
WINAPI
GetThreadDesktop(
    _In_ DWORD dwThreadId);
// GetThreadPriority:获取某一线程的优先级
WINBASEAPI
int
WINAPI
GetThreadPriority(
    _In_ HANDLE hThread
    );
// GetThreadSelectorEntry:调试器专用,传回指定之线程的某个
// selector 的LDT记录项
WINBASEAPI
BOOL
WINAPI
GetThreadSelectorEntry(
    _In_  HANDLE hThread,
    _In_  DWORD dwSelector,
    _Out_ LPLDT_ENTRY lpSelectorEntry
    );
// ResumeThread:将某个冻结的线程恢复运行
WINBASEAPI
DWORD
WINAPI
ResumeThread(
    _In_ HANDLE hThread
    );
// SetPriorityClass:设定优先级等级
WINBASEAPI
BOOL
WINAPI
SetPriorityClass(
    _In_ HANDLE hProcess,
    _In_ DWORD dwPriorityClass
    );
    
// SetThreadPriority:设定线程优先级
WINBASEAPI
BOOL
WINAPI
SetThreadPriority(
    _In_ HANDLE hThread,
    _In_ int nPriority
    );
// Sleep:将某个线程暂时冻结,其他线程将获得执行权
 WINBASEAPI
VOID
WINAPI
Sleep(
    _In_ DWORD dwMilliseconds
    );
    
// SuspendThread:冻结(挂起)某个线程
WINBASEAPI
DWORD
WINAPI
SuspendThread(
    _In_ HANDLE hThread
    );
// TerminateThread:结束某个线程
WINBASEAPI
BOOL
WINAPI
TerminateThread(
    _In_ HANDLE hThread,
    _In_ DWORD dwExitCode
    );
// TlsAlloc:配置一个TLS(Thread Local Storage)
WINBASEAPI
DWORD
WINAPI
TlsAlloc(
    VOID
    );
// TlsFree:释放一个TLS    
WINBASEAPI
BOOL
WINAPI
TlsFree(
    _In_ DWORD dwTlsIndex
    );
// TlsGetValue:取得某个TLS的内容
WINBASEAPI
LPVOID
WINAPI
TlsGetValue(
    _In_ DWORD dwTlsIndex
    );
// TlsSetValue:设定某个TLS的内容
WINBASEAPI
BOOL
WINAPI
TlsSetValue(
    _In_ DWORD dwTlsIndex,
    _In_opt_ LPVOID lpTlsValue
    );
// WaitForInputIdle:等待,直到不再有消息进入到某个线程中
WINUSERAPI
DWORD
WINAPI
WaitForInputIdle(
    _In_ HANDLE hProcess,
    _In_ DWORD dwMilliseconds);

2.线程在MFC框架中又分为Worker Thread 和UI Thread的介绍
Worker Thread (工作线程):与使用者界面无关。使用CreateThread()函数创建的线程是一个工作线程。但是如果在往该线程中输入了消息,那么该线程就变成UI线程了。
UI Thread(界面线程):与使用者界面有关。如果线程代码中带有一个消息循环,那么该线程就是一个UI Thread。
注意:在创建线程时,把所有的UI(User Interface) 操作都放在主线程中,其他的各种复杂的运算都放在Worker Thread。

3.探索CWinThread
就像CWinApp代表一个程序本身一样,CWinThread 也代表着一个线程本身。我个人的理解是,CWinApp继承于CWinThread,那么程序本身就有一个主线程来维护了。
注意:一个程序只能有一个CWinApp对象,即theApp。但是并不代表一个程序只能有一个CWinThread对象。每当我们需要一个额外的线程时,推荐做法是:我们应该自定义继承自CWinThread的类,然后由该类的对象调用成员函数CreateThread()或者全局函数AfxBeginThread()产生一个线程。当然也可以直接使用::CreateThread()和_beginthreadex()函数产生一个线程。但是为什么推荐使用CWinThread对象呢,因为在CWinThread的成员函数CreateThread()内部完成了一些初始化。多说一句:实际上在CreateThread()函数和AfxBeginThread()函数内部都调用了_beginthreadex()函数。

4.Worker Thread和UI Thread产生的步骤:
①Worker Thread:不涉及操作界面。我们使用AfxBeginThread()函数来创建线程。AfxBeginThread()函数的声明如下:
我们使用第一种方式来创建Worker Thread线程。

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
	int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,
	DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
	int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,
	DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

由 AFX_THREADPROC 函数指针 定义线程入口函数的形式:

UINT ThreadFunc(LPVOID LParam);

②UI Thread:UI线程可不能关由线程函数来表示,因为他要处理消息,需要消息循环。我们应该先自定义继承自CWinThread的类。然后再由AfxBeginThread()函数,上面函数重载。直接使用类向导产生MFC类如下:
CMyThread.h

// CMyThread

class CMyThread : public CWinThread
{
	DECLARE_DYNCREATE(CMyThread)

protected:
	CMyThread();           // 动态创建所使用的受保护的构造函数
	virtual ~CMyThread();

public:
	virtual BOOL InitInstance();
	virtual int ExitInstance();

protected:
	DECLARE_MESSAGE_MAP()
};

CMyThread.cpp

// CMyThread
IMPLEMENT_DYNCREATE(CMyThread, CWinThread)
CMyThread::CMyThread()
{
}
CMyThread::~CMyThread()
{
}
BOOL CMyThread::InitInstance()
{
	// TODO:    在此执行任意逐线程初始化
	return TRUE;
}
int CMyThread::ExitInstance()
{
	// TODO:    在此执行任意逐线程清理
	return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CMyThread, CWinThread)
END_MESSAGE_MAP()
// CMyThread 消息处理程序
// 使用AfxBeginThread函数重载产生CWinThread 对象
CWinThread* pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread));

5.线程的结束
Worker Thread:①既然线程函数代表的是线程,执行到return语句即代表线程结束。
②调用AfxEndThread()函数也可以结束线程。
UI Thread:①因为有消息循环的关系,只有获取到WM_QUIT消息才能推出消息循环。我们可以使用::PostQuitMessage()函数发送WM_QUIT。
②在线程函数中调用AfxBeginThread()函数也可以退出线程。
其实AfxEndBeginThread()函数也是对_endThreadex()函数的封装。

6.线程与同步控制
讲了这么多,创建几个线程是很简单的事情,难的是实现线程之间的同步。多个线程之间的运行次序是未知的,就会产生所谓的race condition(资源竞争)。侯捷在书中讲了一个例子。
假设有两个线程A和线程B,线程A负责读取全局变量X的值,线程B负责写入X的值。如果B写完后,A再去读取。这样是对的,但是如果A先读取,B再写就不行了,X值是未知。
如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
要解决这些问题,必须协调各个线程的执行顺序。Windows中提供了4种办法。
1.Critical Section(临界区)。
2.Semaphore(信号量)
3.Event(事件对象)
4.Mutex(互斥量)
MFC也提供了4个对应的类
在这里插入图片描述
7.下面直接放上程本章的程序示例:
新建MFC多文档应用程序。mltithrd.cpp文件添加如下代码:

// 声明线程入口函数
UINT ThreadFunc(LPVOID ThreadArg);
// 声明5个UI线程
CWinThread* _pThread[5];
// 定义一个线程优先级的数组
DWORD _ThreadArg[5] = { HIGHEST_THREAD,    // 0x00
                        ABOVE_AVE_THREAD,  // 0x3F
                        NORMAL_THREAD,     // 0x7F
                        BELOW_AVE_THREAD,  // 0xBF
                        LOWEST_THREAD      // 0xFF
                       };  

在CMltithrdApp::InitInstance()函数中return 语句前添加如下代码。新建5个线程,5个线程使用同一个入口函数。并且 设置线程的优先级。

  int i;
        for (i= 0; i< 5; i++)
        {
            _pThread[i] = AfxBeginThread(ThreadFunc,
                                         &_ThreadArg[i],
            THREAD_PRIORITY_NORMAL,
                                         0,
                                         0,
                                         NULL);
        }

        // setup relative priority of threads
        _pThread[0]->SetThreadPriority(THREAD_PRIORITY_HIGHEST);
        _pThread[1]->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
        _pThread[2]->SetThreadPriority(THREAD_PRIORITY_NORMAL);
        _pThread[3]->SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
        _pThread[4]->SetThreadPriority(THREAD_PRIORITY_LOWEST);

最后定义线程入口函数:ThreadFunc()函数如下:

UINT ThreadFunc(LPVOID ThreadArg)
{
    CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
    DWORD dwArg = *(DWORD*)ThreadArg;
    CRect rect;
    HDC hDC;
    HANDLE hBrush, hOldBrush;
    int nThreadNo;
    char szBuf[80];
    DWORD dwThreadHits = 0;

    hDC = GetDC(pFrame->m_hWnd);
    ::GetClientRect(pFrame->m_hWnd,&rect);
    hBrush = CreateSolidBrush(RGB(dwArg, dwArg, dwArg));
    hOldBrush = ::SelectObject(hDC, hBrush);


    switch (dwArg)
    {
    case HIGHEST_THREAD:   nThreadNo = 0; break;
    case ABOVE_AVE_THREAD: nThreadNo = 1; break;
    case NORMAL_THREAD:    nThreadNo = 2; break;
    case BELOW_AVE_THREAD: nThreadNo = 3; break;
    case LOWEST_THREAD:    nThreadNo = 4; break;
    }

    wsprintf(szBuf, _T("T%d"), nThreadNo);
    TextOut(hDC,dwArg, rect.bottom - 150,szBuf,lstrlen(szBuf));
    wsprintf(szBuf, _T("P%d"), _ThreadArg[nThreadNo]);
    TextOut(hDC, dwArg, rect.bottom - 130, szBuf, lstrlen(szBuf));

    do
    {
        dwThreadHits++;

        Rectangle(hDC, dwArg, rect.bottom - (dwThreadHits / 10),
            dwArg + 0x40, rect.bottom);
        Sleep(10);
    } while (dwThreadHits < 1000);

    hBrush = SelectObject(hDC, hOldBrush);
    DeleteObject(hBrush);
    ReleaseDC(pFrame->m_hWnd,hDC);
    return 0;
}

这一章的代码示例,大家可以使用书中的代码示例。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入浅出MFC(第二版) 目录 第0章 你一定要知道(导读) 这本书适合谁 你需要什么技术基础 你需要什么软硬件环境 让我们使用同一种语言 本书符号习惯 本书例程的取得 范例程序说明 与前版本之差异 如何联络作者 第一篇 勿在浮砂筑高台 第1章 Win32程序基本概念 Win32程序开发流程 需要什么函数库(.LIB) 需要什么头文件(.H) 以消息为基础,以事件驱动之(message based,event driven) 一个具体而微的Win32程序 程序进入点WinMain 窗口类之注册与窗口之诞生 消息循环 窗口的生命中枢:窗口函数 消息映射(Message Map)的雏形 对话框的运行 模块定义文件(.DEF) 资源描述档(.RC) Widnows程序的生与死 空闲时间的处理:OnIdle Console程序 Console程序与DOS程序的差别 Console程序的编译链接 JBACKUP:Win32 Console程序设计 MFCCON:MFC Console程序设计 行程与线程(Process and Thread) 核心对象 一个行程的诞生与死亡 产生子行程 一个线程的诞生与死亡 以_beginthreadex取代CreateThread 线程优先级(Priority) 多线程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态成员(变量与函数) C++程序的生与死:兼谈构造函数与解构函数 四种不同的对象生存方式(in stack、in heap、global、local static) 执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC六大关键技术之仿真 MFC类层次结构 Frame 1范例程序 MFC程序的初始化过程 Frame 2范例程序 RTTI(执行期类型识别) 类别型录网与CRuntimeClass DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏 Frame 3范例程序 IsKindOf(类型识别) Frame 4范例程序 Dynamic Creation(动态生成) DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏 Frame 6范例程序 Persistence(永久保存)机制 Serialize(数据读写) DECLARE_SERIAL/IMPLEMENT_SERIAL宏 没有范例程序 Message Mapping(消息映射) Frame 7范例程序 Command Routing(命令传递) Frame 8范例程序 本章回顾 第二篇 欲善工事先利其器 第4章 Visual C++集成开发环境 安装与组成 四个重要的工具 内务府总管:Visual C++集成开发环境 关于project 关于工具设定 Source Browser Online Help 调试工具 VC++调试器 Exception Handling 程序代码产生器:AppWizard 东圈西点完成MFC程序骨干 威力强大的资源编辑器 Icon编辑器 Cursor编辑器 Bitmap编辑器 工具栏(Toolbar)编辑器 VERSIONINFO资源编辑器 字符串表格(Accelerator)编辑器 菜单(Menu)编辑器 加速键(Accelerator)编辑器 对话框(Dialog)编辑器 Console程序的项目管理 第三篇 浅出MFC程序设计 第5章 总观Application Framework 什么是Application Framework? 侯捷怎么说 我怎么说 别人怎么说 为什么使用Application Framework Microsoft Foundation Classes(MFC) 白头宫女话天宝:Visual C++与MFC 纵览MFC General Purpose classes CObject 数据处理类(collection classes) 杂项类 异常处理类(exception handling classes) Windows API classes Application framework classes High level Abstractions Afx全局函数 MFC宏(macros) MFC数据类型(data types) 第6章 MFC程序的生死因果 不二法门:熟记MFC类的层次结构 需要什么函数库? 需要什么头文件? 简化的MFC程序结构——以Hello MFC为例 Hello程序程序代码 MFC程序的来龙去脉(causal relations) 我只借用两个类:CWinApp和CFrameWnd CWinApp——取代WinMain的地位 CFrameWnd——取代WndProc的地位 引爆器——Application object 隐晦不明的WinMain AfxWinInit——AFX内部初始化操作 CWinApp::InitApplication CMyWinApp::InitInstance CFrameWnd::Create产生主窗口(并先注册窗口类) 奇怪的窗口类名称Afx:b:14ae:6:3e8f 窗口显示与更新 CWinApp::Run——程序生命的活水源头 把消息与处理函数连接在一起:Message Map机制 来龙去脉总整理 Callback函数 空闲时间(idle time)的处理:OnIdle Dialog与Control 通用对话框(Common Dialogs) 本章回顾 第7章 简单而完整:MFC骨干程序 不二法门:熟记MFC类层次结构 MFC程序的UI新风貌 Document/View支撑你的应用程序 利用Visual C++工具完成Scribble step0 骨干程序使用哪些MFC类? Document Template的意义 Scribble的Document/View设计 主窗口的诞生 工具栏和状态栏的诞生(Toolbar&Status bar) 鼠标拖放(Drag and Drop) 消息映射(Message Map) 标准菜单File/Edit/View/Window/Help 对话框 改用CEditView 第四篇 深入MFC程序设计 第8章 Document-View深入探讨 为什么需要Document-View(形而上) Document View Document Frame(View Frame) Document Template CDocTemplate管理CDocument/CView/CFrameWnd Scribble Step1的Document——数据结构设计 MFC Collection Classes的选用 CScribbleDoc的修改 文件:一连串的线线条与坐标点 Scribble Step 1的View:数据重绘与编辑 CScribbleView的修改 View的重绘操作:GetDocument和OnDraw ClassWizard的辅佐 WizardBar的辅佐 Serialize:对象的档案读写 Serialization以外的档案读写操作 台面上的Serialize操作 台面下的Serialize写档奥秘 台面下的Serialize读档奥秘 DYNAMIC/DYNCREATE/SERIAL三宏 Serializable的必要条件 CObject类 IsKindOf IsSerializable CObject::Serialize CArchive类 operator《和operator》 效率考虑 自定SERIAL宏给抽象类使用 在CObList中加入CStroke以外的类 Document与View交流——为Step4做准备 第9章 消息映射与命令传递 到底要解决什么 消息分类 万流归宗Command Target(CCmdTarget) 三个奇怪的宏,一张巨大的网 DECLARE_MESSAGE_MAP宏 消息映射网的形成:BEGIN…/ON…/END…宏 米诺托斯(Minotauros)与西修斯(Theseus) 二万五千里长征——消息的传递 直线上溯(一般Windows消息) 拐弯上溯(WM_COMMAND命令消息) 罗塞达碑石:AfxSig_xx的奥秘 Scribble Step2:UI对象的变化 改变菜单 改变工具栏 利用ClassWizard连接命令项识别码与命令处理函数 维护UI对象状态(UPDATE_COMMAND_UI) 本章回顾 第10章 MFC与对话盒 对话框编辑器 利用ClassWizard连接对话框与其专用类 对话框的消息处理函数 对话框数据交换与校验(DDX&DDV) 如何唤起对话框 本章回顾 第11章 View功能的加强与重绘效率的提高 同时修改多个Views:UpdateAllViews和OnUpdate 在View中定义一个hint 把hint传给OnUpdate 利用hint增加重绘效率 可卷动的窗口:CScrollView 大窗口中的小窗口:Splitter 切分窗口的功能 切分窗口的程序概念 切分窗口的实现 本章回顾 第12章 打印与预览 概述 打印操作的后台原理 MFC默认的打印机制 Scribble打印机制的增强 打印机的页和文件的页 配置GDI绘图工具 尺寸与方向:关于映射方式(坐标系统) 分页 页眉与页脚 动态计算页码 打印预览(Print Preview) 本章回顾 第13章 多重文件与多重显示 MDI和SDI 多重显示(Multiple Views) 窗口的动态切分 窗口的静态切分 CreateStatic和CreateView 窗口的静态三叉切分 Graph范例程序 静态切分窗口之观念整理 同源子窗口 CMDIFrameWnd::OnWindowNew Text范例程序 非标准做法的缺点 多重文件 新的Cocument类 新的Document Template 新的UI系统 新文件的档案读写操作 第14章 MFC线程序设计 从操作系统层面看线程 三个观念:模块、行程和线线程优先级(Priority) 线程调度(Scheduling) Thread Context 从程序设计层面看线程 Worker Threads和UI Threads 错误观念 正确态度 MFC线程序设计 探索CWinThread 产生一个Worker Thread 产生一个UI Thread 线程的结束 线程与同步控制 MFC线程序例程 第15章 定制一个AppWizard 到底Wizard是什么? Custom AppWizard的基本操作 剖析AppWizard Components Dialog Templates和Dialog classes Macros Directives 动手修改Top Studio AppWizard 利用资源编辑器修改IDD_CUSTOM1对话框画面 利用ClassWizard修改IDD_CUSTOM1对话框的对应类CCustomlDlg 改写OnDismiss虚拟函数,在其中定义macros 修改text template Top Studio AppWizard执行结果 更多的信息 第16章 站上众人的肩膀——使用Components&activeX Controls 什么是Component Gallery 使用Components Splash screen system Info for About Dlg Tip of the Day Components实际运用:ComTest程序 修改ComTest程序内容 使用ActiveX Controls ActiveX Control基础观念:Properties、Methods、Events ActiveX Controls的五大使用步骤 使用ActiveX Control:OcxTest程序 第五篇 附录 附录A 无责任书评:从摇篮到坟墓Windows的完全学习 无责任书评:MFC四大天王 附录B Scribble Step 5完整原始码 附录C Visual C++5.0MFC范例程序一览 附录D 以MFC重建DBWIN收起
这本书适合谁 深入浅出MFC是一本介绍MFC(Microsoft Foundation Classes)程序设计技术的书籍。对于Windows 应用软件的开发感到兴趣,并欲使用Visual C++ 整合环境的视觉开发工具,以MFC程序基础的人,都可以从此书获得最根本最重要的知识与实例。 如果你是一位对Application Framework和面向对象(Object Oriented)观念感兴趣的技术狂热份子,想知道神秘的Runtime Type Information、Dynamic Creation、Persistence、Message Mapping 以及Command Routing 如何实现,本书能够充分满足你。事实上,依我之见,这些核心技术与彻底学会操控MFC乃同一件事情。 全书分为四篇: 第一篇【勿在浮砂筑高台】提供进入 MFC 核心技术以及应用技术之前的所有技术基础,包括: Win32 程序观念 :message based,event driven,multitasking, multithreading, console programming。 C++ 重要技术:类与对象、this 指针与继承、静态成员、虚函数与多态、模板(template)类、异常处理(exception handling)。 MFC 六大技术之简化仿真(Console 程序) 第二篇【欲善工事先利其器】提供给对Visual C++ 整合环境全然陌生的朋友一个导引。这一篇当然不能取代 Visual C++ User's Guide 的地位,但对整个软件开发环境有全盘以及概观性的介绍,可以让初学者迅速了解手上掌握的工具,以及它们的主要功能。 第三篇【浅出 MFC 程序设计】介绍一个 MFC 程序的生死因果。已经有 MFC 程序经验的朋友,不见得不会对本篇感到惊艳。根据我的了解,太多人使用 MFC 是「只知道这么做,不知道为什么」;本篇详细解释 MFC 程序之来龙去脉,为初入 MFC 领域的读者奠定扎实的基础。说不定本篇会让你有醍醐灌顶之感。 第四篇【深入 MFC 程序设计】介绍各式各样 MFC 技术。「只知其然不知其所以然」的不良副作用,在程序设计的企图进一步开展之后,愈来愈严重,最终会行不得也!那些最困扰我们的 MFC 宏、MFC 常数定义,不得一窥堂奥的 MFC 黑箱作业,在本篇陆续曝光。本篇将使您高喊:Eureka! 阿基米得在洗澡时发现浮力原理,高兴得来不及穿上裤子,跑到街上大喊:Eureka(我找到了)。 范例程序方面,第三章有数个Console程序(DOS-like 程序,在Windows系统的DOS Box 中执行),模拟并简化Application Framework 六大核心技术。另外,全书以一个循序渐进的Scribble 程序(Visual C++ 所附范例),从第七章开始,分章探讨每一个MFC应用技术主题。第13章另有三个程序,示范 Multi-View 和 Multi-Document 的情况。 14章~16 章是第二版新增内容,主题分别是MFC线程序设计、Custom AppWizard、以及如何使用Component Gallery 提供的ActiveX controls 和components。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值