MFC第九天

MFC文件操作

1.相关类

CFile类  封装了文件句柄以及相关操作的API

CFileFind类   封装了文件查找相关的类

2 CFile类的使用

2.1打开或者创建文件

2.2文件读写    通常放到宜昌处理结果中

2.3关闭文件

CFileFind

查找指定目录下的所有文件和文件夹

开始查找

virtual BOOL FindFile( LPCTSTR pstrName = NULL, DWORD dwUnused = 0 );

查找下一个文件

virtual BOOL FindNextFile( );

GetXXX  或者  IsXXX,获取文件信息并判断。

CFileFind::Close ,结束查找

序列化(串行化)

方便文件读写,避免类型转换

将数据以二进制流的方式保存到文件,或者重文件读取.

相关类

CFile

CArchive 归档类,执行具体的数据读写操作。使用该类替换CFile类的Read,Write函数

写操作“<<”

读操作“>>”

序列化的操作步骤

CFile::Open

定义CArchive类的对象

>>或<<,执行读写操作

关闭CArchive对象

CFile::Close()

对象的序列化。

将对象的所有数据以二进制流写入文件。

反序列化  从文件先读取类的信息,创建对象,然后读取对象的成员初始化新创建的对象。

对象的序列化以动态创建为必要条件。

定义一个支持序列化的类。

必须是CObject的子类

添加序列化的声明宏和实现宏

重写Serialize函数,在函数中序列化对象的成员。

1,_init_Cstudent  结构体变量,作用是调用AfxClassInfo

在函数中,获取应用程序的模块信息,将CStudent的运行时信息的地址保存到m_classList链表

中(m_classList是模块状态信息的成员)。

2,operator>>  友元函数加载CStudent类的对象

对象序列化过程。

序列化

调用ar.WriteObject()函数

在函数中,首先调用GetRuntimeClass函数获取CStudent类的运行时信息。

调用WriteClass函数,将类的信息写入到文件.

在store函数中依次将类的,版本,长度,名称 写入文件。

调用Serialize函数,函数中依次将CStudent类的成员变量写入到文件。

反序列化

ar.ReadObject

ReadClass->Load

Serialize

函数跟踪

	_AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
	{ ar.WriteObject(pOb); return ar; }

void CArchive::WriteObject(const CObject* pOb)
{
	// object can be NULL
	ASSERT(IsStoring());    // proper direction

	DWORD nObIndex;
	ASSERT(sizeof(nObIndex) == 4);
	ASSERT(sizeof(wNullTag) == 2);
	ASSERT(sizeof(wBigObjectTag) == 2);
	ASSERT(sizeof(wNewClassTag) == 2);

	// make sure m_pStoreMap is initialized
	MapObject(NULL);

	if (pOb == NULL)
	{
		// save out null tag to represent NULL pointer
		*this << wNullTag;
	}
	else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)
		// assumes initialized to 0 map
	{
		// save out index of already stored object
		if (nObIndex < wBigObjectTag)
			*this << (WORD)nObIndex;
		else
		{
			*this << wBigObjectTag;
			*this << nObIndex;
		}
	}
	else
	{
		// write class of object first
		CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
		WriteClass(pClassRef);

		// enter in stored object table, checking for overflow
		CheckCount();
		(*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;

		// cause the object to serialize itself
		((CObject*)pOb)->Serialize(*this);
	}
}

void CArchive::CheckCount()
{
	if (m_nMapCount >= nMaxMapCount)
		AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
}

_AFX_INLINE CArchive& CArchive::operator<<(WORD w)
	{ if (m_lpBufCur + sizeof(WORD) > m_lpBufMax) Flush();

void CRuntimeClass::Store(CArchive& ar) const
	// stores a runtime class description
{
	WORD nLen = (WORD)lstrlenA(m_lpszClassName);
	ar << (WORD)m_wSchema << nLen;
	ar.Write(m_lpszClassName, nLen*sizeof(char));
}

CRuntimeClass* CObject::GetRuntimeClass() const
{
	return RUNTIME_CLASS(CObject);
}

void CArchive::WriteClass(const CRuntimeClass* pClassRef)
{
	ASSERT(pClassRef != NULL);
	ASSERT(IsStoring());    // proper direction

	if (pClassRef->m_wSchema == 0xFFFF)
	{
		TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.\n",
			pClassRef->m_lpszClassName);
		AfxThrowNotSupportedException();
	}

	// make sure m_pStoreMap is initialized
	MapObject(NULL);

	// write out class id of pOb, with high bit set to indicate
	// new object follows

	// ASSUME: initialized to 0 map
	DWORD nClassIndex;
	if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)
	{
		// previously seen class, write out the index tagged by high bit
		if (nClassIndex < wBigObjectTag)
			*this << (WORD)(wClassTag | nClassIndex);
		else
		{
			*this << wBigObjectTag;
			*this << (dwBigClassTag | nClassIndex);
		}
	}
	else
	{
		// store new class
		*this << wNewClassTag;
		pClassRef->Store(*this);

		// store new class reference in map, checking for overflow
		CheckCount();
		(*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;
	}
}

#define DECLARE_SERIAL(class_name) 	_DECLARE_DYNCREATE(class_name) 	AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);

#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) 	CObject* PASCAL class_name::CreateObject() 		{ return new class_name; } 	_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, 		class_name::CreateObject) 	AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); 	CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) 		{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); 			return ar; } 

struct CFileStatus
{
	CTime m_ctime;          // creation date/time of file
	CTime m_mtime;          // last modification date/time of file
	CTime m_atime;          // last access date/time of file
	LONG m_size;            // logical size of file in bytes
	BYTE m_attribute;       // logical OR of CFile::Attribute enum values
	BYTE _m_padding;        // pad the structure to a WORD
	TCHAR m_szFullName[_MAX_PATH]; // absolute path name

#ifdef _DEBUG
	void Dump(CDumpContext& dc) const;
#endif
};

class CTimeSpan
{
public:

// Constructors
	CTimeSpan();
	CTimeSpan(time_t time);
	CTimeSpan(LONG lDays, int nHours, int nMins, int nSecs);

	CTimeSpan(const CTimeSpan& timeSpanSrc);
	const CTimeSpan& operator=(const CTimeSpan& timeSpanSrc);

// Attributes
	// extract parts
	LONG GetDays() const;   // total # of days
	LONG GetTotalHours() const;
	int GetHours() const;
	LONG GetTotalMinutes() const;
	int GetMinutes() const;
	LONG GetTotalSeconds() const;
	int GetSeconds() const;

// Operations
	// time math
	CTimeSpan operator-(CTimeSpan timeSpan) const;
	CTimeSpan operator+(CTimeSpan timeSpan) const;
	const CTimeSpan& operator+=(CTimeSpan timeSpan);
	const CTimeSpan& operator-=(CTimeSpan timeSpan);
	BOOL operator==(CTimeSpan timeSpan) const;
	BOOL operator!=(CTimeSpan timeSpan) const;
	BOOL operator<(CTimeSpan timeSpan) const;
	BOOL operator>(CTimeSpan timeSpan) const;
	BOOL operator<=(CTimeSpan timeSpan) const;
	BOOL operator>=(CTimeSpan timeSpan) const;

#ifdef _UNICODE
	// for compatibility with MFC 3.x
	CString Format(LPCSTR pFormat) const;
#endif
	CString Format(LPCTSTR pFormat) const;
	CString Format(UINT nID) const;

	// serialization
#ifdef _DEBUG
	friend CDumpContext& AFXAPI operator<<(CDumpContext& dc,CTimeSpan timeSpan);
#endif
	friend CArchive& AFXAPI operator<<(CArchive& ar, CTimeSpan timeSpan);
	friend CArchive& AFXAPI operator>>(CArchive& ar, CTimeSpan& rtimeSpan);

private:
	time_t m_timeSpan;
	friend class CTime;
};

class CFile : public CObject
{
	DECLARE_DYNAMIC(CFile)

public:
// Flag values
	enum OpenFlags {
		modeRead =          0x0000,
		modeWrite =         0x0001,
		modeReadWrite =     0x0002,
		shareCompat =       0x0000,
		shareExclusive =    0x0010,
		shareDenyWrite =    0x0020,
		shareDenyRead =     0x0030,
		shareDenyNone =     0x0040,
		modeNoInherit =     0x0080,
		modeCreate =        0x1000,
		modeNoTruncate =    0x2000,
		typeText =          0x4000, // typeText and typeBinary are used in
		typeBinary =   (int)0x8000 // derived classes only
		};

	enum Attribute {
		normal =    0x00,
		readOnly =  0x01,
		hidden =    0x02,
		system =    0x04,
		volume =    0x08,
		directory = 0x10,
		archive =   0x20
		};

	enum SeekPosition { begin = 0x0, current = 0x1, end = 0x2 };

	enum { hFileNull = -1 };

// Constructors
	CFile();
	CFile(int hFile);
	CFile(LPCTSTR lpszFileName, UINT nOpenFlags);

// Attributes
	UINT m_hFile;
	operator HFILE() const;

	virtual DWORD GetPosition() const;
	BOOL GetStatus(CFileStatus& rStatus) const;
	virtual CString GetFileName() const;
	virtual CString GetFileTitle() const;
	virtual CString GetFilePath() const;
	virtual void SetFilePath(LPCTSTR lpszNewName);

// Operations
	virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags,
		CFileException* pError = NULL);

	static void PASCAL Rename(LPCTSTR lpszOldName,
				LPCTSTR lpszNewName);
	static void PASCAL Remove(LPCTSTR lpszFileName);
	static BOOL PASCAL GetStatus(LPCTSTR lpszFileName,
				CFileStatus& rStatus);
	static void PASCAL SetStatus(LPCTSTR lpszFileName,
				const CFileStatus& status);

	DWORD SeekToEnd();
	void SeekToBegin();

	// backward compatible ReadHuge and WriteHuge
	DWORD ReadHuge(void* lpBuffer, DWORD dwCount);
	void WriteHuge(const void* lpBuffer, DWORD dwCount);

// Overridables
	virtual CFile* Duplicate() const;

	virtual LONG Seek(LONG lOff, UINT nFrom);
	virtual void SetLength(DWORD dwNewLen);
	virtual DWORD GetLength() const;

	virtual UINT Read(void* lpBuf, UINT nCount);
	virtual void Write(const void* lpBuf, UINT nCount);

	virtual void LockRange(DWORD dwPos, DWORD dwCount);
	virtual void UnlockRange(DWORD dwPos, DWORD dwCount);

	virtual void Abort();
	virtual void Flush();
	virtual void Close();

// Implementation
public:
	virtual ~CFile();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif
	enum BufferCommand { bufferRead, bufferWrite, bufferCommit, bufferCheck };
	virtual UINT GetBufferPtr(UINT nCommand, UINT nCount = 0,
		void** ppBufStart = NULL, void** ppBufMax = NULL);

protected:
	BOOL m_bCloseOnDelete;
	CString m_strFileName;
};

class CArchive
{
public:
// Flag values
	enum Mode { store = 0, load = 1, bNoFlushOnDelete = 2, bNoByteSwap = 4 };

	CArchive(CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL);
	~CArchive();

// Attributes
	BOOL IsLoading() const;
	BOOL IsStoring() const;
	BOOL IsByteSwapping() const;
	BOOL IsBufferEmpty() const;

	CFile* GetFile() const;
	UINT GetObjectSchema(); // only valid when reading a CObject*
	void SetObjectSchema(UINT nSchema);

	// pointer to document being serialized -- must set to serialize
	//  COleClientItems in a document!
	CDocument* m_pDocument;

// Operations
	UINT Read(void* lpBuf, UINT nMax);
	void Write(const void* lpBuf, UINT nMax);
	void Flush();
	void Close();
	void Abort();   // close and shutdown without exceptions

	// reading and writing strings
	void WriteString(LPCTSTR lpsz);
	LPTSTR ReadString(LPTSTR lpsz, UINT nMax);
	BOOL ReadString(CString& rString);

public:
	// Object I/O is pointer based to avoid added construction overhead.
	// Use the Serialize member function directly for embedded objects.
	friend CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb);

	friend CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb);
	friend CArchive& AFXAPI operator>>(CArchive& ar, const CObject*& pOb);

	// insertion operations
	CArchive& operator<<(BYTE by);
	CArchive& operator<<(WORD w);
	CArchive& operator<<(LONG l);
	CArchive& operator<<(DWORD dw);
	CArchive& operator<<(float f);
	CArchive& operator<<(double d);

	CArchive& operator<<(int i);
	CArchive& operator<<(short w);
	CArchive& operator<<(char ch);
	CArchive& operator<<(unsigned u);

	// extraction operations
	CArchive& operator>>(BYTE& by);
	CArchive& operator>>(WORD& w);
	CArchive& operator>>(DWORD& dw);
	CArchive& operator>>(LONG& l);
	CArchive& operator>>(float& f);
	CArchive& operator>>(double& d);

	CArchive& operator>>(int& i);
	CArchive& operator>>(short& w);
	CArchive& operator>>(char& ch);
	CArchive& operator>>(unsigned& u);

	// object read/write
	CObject* ReadObject(const CRuntimeClass* pClass);
	void WriteObject(const CObject* pOb);
	// advanced object mapping (used for forced references)
	void MapObject(const CObject* pOb);

	// advanced versioning support
	void WriteClass(const CRuntimeClass* pClassRef);
	CRuntimeClass* ReadClass(const CRuntimeClass* pClassRefRequested = NULL,
		UINT* pSchema = NULL, DWORD* pObTag = NULL);
	void SerializeClass(const CRuntimeClass* pClassRef);

	// advanced operations (used when storing/loading many objects)
	void SetStoreParams(UINT nHashSize = 2053, UINT nBlockSize = 128);
	void SetLoadParams(UINT nGrowBy = 1024);

// Implementation
public:
	BOOL m_bForceFlat;  // for COleClientItem implementation (default TRUE)
	BOOL m_bDirectBuffer;   // TRUE if m_pFile supports direct buffering
	void FillBuffer(UINT nBytesNeeded);
	void CheckCount();  // throw exception if m_nMapCount is too large

	// special functions for reading and writing (16-bit compatible) counts
	DWORD ReadCount();
	void WriteCount(DWORD dwCount);

	// public for advanced use
	UINT m_nObjectSchema;
	CString m_strFileName;

protected:
	// archive objects cannot be copied or assigned
	CArchive(const CArchive& arSrc);
	void operator=(const CArchive& arSrc);

	BOOL m_nMode;
	BOOL m_bUserBuf;
	int m_nBufSize;
	CFile* m_pFile;
	BYTE* m_lpBufCur;
	BYTE* m_lpBufMax;
	BYTE* m_lpBufStart;

	// array/map for CObject* and CRuntimeClass* load/store
	UINT m_nMapCount;
	union
	{
		CPtrArray* m_pLoadArray;
		CMapPtrToPtr* m_pStoreMap;
	};
	// map to keep track of mismatched schemas
	CMapPtrToPtr* m_pSchemaMap;

	// advanced parameters (controls performance with large archives)
	UINT m_nGrowSize;
	UINT m_nHashSize;
};

源代码

// MFCSerial.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

void Store(){
	CFile file;
	if(!file.Open("c:/Serial.dat",CFile::modeCreate|CFile::modeWrite)){
		printf("文件打开失败");
		return ;
	}
	CArchive archive(&file,CArchive::store);
	archive<<100<<12.25<<"Hello CArchive";
	archive.Close();
	file.Close();
}
void Load(){
	CFile file;
	if(!file.Open("c:/Serial.dat",CFile::modeRead)){
		printf("文件读取失败!");
		return ;
	}
	CArchive ar(&file,CArchive::load);
	int num=0;
	double dou=0.0;
	CString cStr;
	ar>>num>>dou>>cStr;
	printf("%d,%lf,%s\n",num,dou,cStr);
	ar.Close();
	file.Close();
}
void CFileSerial(){

}

int main(int argc, char* argv[])
{
//	Store();
	Load();
	return 0;
}
// MFCObjectSerial.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

class CStudent:public CObject{

public:
	CStudent();
	CStudent(CString m_sName,UINT m_iAge);
	virtual void Serialize(CArchive& ar);
	void Show();
public:
	CString m_sName;
	UINT m_iAge;
//DECLARE_SERIAL(CStudent)
_DECLARE_DYNCREATE(CStudent)
AFX_API friend CArchive& AFXAPI operator>>
(CArchive& ar, CStudent* &pOb);
};
//

//
// IMPLEMENT_SERIAL(CStudent,CObject,1)
	 //
	 CObject* PASCAL CStudent::CreateObject(){
		 return new CStudent;
	 }
//	_IMPLEMENT_RUNTIMECLASS(CStudent, CObject, 1, CStudent::CreateObject) 

  AFX_DATADEF CRuntimeClass CStudent::classCStudent = { 	  "CStudent", sizeof(class CStudent), 1, CStudent::CreateObject, 	RUNTIME_CLASS(CObject), NULL }; 	CRuntimeClass* CStudent::GetRuntimeClass() const 	{ return RUNTIME_CLASS(CStudent); } 

	AFX_CLASSINIT _init_CStudent(RUNTIME_CLASS(CStudent));
	CArchive& AFXAPI operator>>(CArchive& ar, CStudent* &pOb){
		pOb = (CStudent*) ar.ReadObject(RUNTIME_CLASS(CStudent));
		return ar;
	}
	 //
CStudent::CStudent(){}
CStudent::CStudent(CString m_sName,UINT m_iAge){
	this->m_sName=m_sName;
	this->m_iAge=m_iAge;
}
void CStudent::Serialize(CArchive& ar){
	CObject::Serialize(ar);
	if(ar.IsStoring()){
		ar<<m_sName<<m_iAge;
	}else{
		ar>>m_sName>>m_iAge;
	}
}
void CStudent::Show(){
	printf("学生姓名:%s\n学生年龄:%d\n",m_sName,m_iAge);
}
//

void ObjStore(CStudent &stu){
	CFile file;
	if(!file.Open("c:/stu.dat",CFile::modeCreate|CFile::modeReadWrite)){
		printf("文件创建失败!\n");
		return;
	}
	CArchive ar(&file,CArchive::store);
	ar<<&stu;
	ar.Close();
	file.Close();
}
void ObjLoad(){
	CFile file;
	if(!file.Open("c:/stu.dat",CFile::modeRead)){
		printf("文件打开失败!\n");
		return ;
	}
	CArchive ar(&file,CArchive::load);
	CStudent *stu=NULL;
	ar>>stu;	//封装反序列化全部过程
	ar.Close();
	file.Close();
	if(stu){
		stu->Show();
	}

}
int main(int argc, char* argv[])
{
	CStudent stu("aaa",20);
//	ObjStore(stu);
	ObjLoad();
	return 0;
}
时间: 2024-11-16 11:07:00

MFC第九天的相关文章

《深入浅出MFC》第七章 简单而完整:MFC骨干程序

不二法门:熟记MFC类层次结构.经常使用的主要MFC类:CWinApp, CWnd<-CView, CWnd<-CFrameWnd, CFrameWnd<-CMDIFrameWnd, CFrameWnd<-CMDIChildWnd, CWnd<-CDialog, CWnd<-CControlBar, CControlBar<-CStatusBar, CControlBar<-CToolBar, CCmdTarget<-CDocument, CCmdTa

深入浅出MFC——消息映射与命令传递(六)

1. 消息分类: 2. 万流归宗——Command Target(CCmdTarget): 3. "消息映射"是MFC内建的一个信息分派机制.通过三个宏(DECLARE_MESSAGE_MAP/BEGIN.../ON.../END...)完成消息网的建构. 4. 注意:CWinThread派生自CCmdTarget,但没有DECLARE_/BEGIN_/END_宏组. 5. 消息映射与虚函数: 6.

MFC控件使用技巧:List Control

1)每列内容过长,显示不完整 只有加载数据的情况下,才会出现水平滚动条 解决方案: 可以添加如下一个空的内容项: m_List.InsertItem(0,NULL);//为了显示进度条 2)不允许点击修改第一列(当然最多能够让我们改动的也只有他了) MFC默认情况下可以修改第一列,其他的不允许修改(需要定制DrawItem) 解决方案: Edit Labels 属性设置为 False 3)报表的形式表示 View 属性设置为 Report 4)注意:不要和列表框控件混淆(英文名: List Bo

【mfc】用对话框分页实现用户登录

所谓的对话框分页就是点击完一个对话框的按钮,切换到另一个对话框, 这样的对话框多用于一些需要用户登录才能够进行操作的软件, 下面就用对话框分页来实现用户登录系统 一.基本目标 有如下的程序,输入用户名与密码,如果用户名为admin,密码为123456,那么则成功登录,切换到一个有"欢迎登录"与"关闭"按钮的对话框 如果用户名或者密码输入错误则弹出提示, 点击关闭能够关闭这个程序,不弹出用户登录的对话框. 二.制作过程 1.首先如同之前的<[mfc]对于对话框程

七日Python之路--第九天

众所周知,代码这东西不是看出来的.程序这东西只哟一个标准. 下面找点开源的东西看看,学习一下大婶们的犀利编码...... 推荐一下: 虽然有点老了:http://www.iteye.com/topic/405150,还有就是GitHub上面搜索一下Django就能出来很多,当然还有OSChina.只是有个问题,就是Django版本不同,具体的内容可能会有些不同,但大概还是相同的.领略即可,然后书写自己的代码. 首要的还是官方文档. 看着还是有些难度的.偶然发现一个不错的Blog:http://w

MFC中给控件添加变量,DoDataExchange中

DoDataExchange函数其实是一项数据动态绑定技术.比如你在写动态按钮过程中须对按钮添加变量时,怎么添加?控件类已经写好了,其变量是已经固定的.你要添加新的变量就要用到DoDataExchange函数. 你要在对话框的构造函数里面初始化一个变量,再用DoDataExchange函数将它绑定到你的动态按扭中,比如:DDX_Check(pDX, IDC_CHECK1, m_Lesson1);这就是将m_Lesson1(这是一个外部变量,其定义在对话框的构造函数里)绑定到IDC_CHECK1中

MFC消息映射的原理:笔记

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵).那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多. 嗯-怎么办呢?于是各大c++名库(比如QT,MFC,VCL-)在消息映射的实

MFC-消息分派

前言 由于工作需要,这几天学了一点MFC,在AFX里看到很多熟悉的东西,如类型信息,序列化,窗口封装和消息分派.几乎每个界面库都必须提供这些基础服务,但提供的手法却千差万别.MFC大量地借用了宏,映射表来实现,而VCL则更多的在语言级别上给与支持.这其实是很容易理解的,因为C++是一个标准,不会因某个应用而随便扩展语言:相反Delphi完全由一个公司掌握,因此每支持一项新技术,变化最大的往往是语言本身. 学习MFC的代码,再对照VCL的实现,这真是一个很有意思的过程,其中可以看到两个框架在一些设

MFC中的DC CDC HDC由来由去理解

MFC中的DC CDC HDC由来由去理解 在此非常感谢博客主的究竟钻研,非常详细的参考资料:http://blog.csdn.net/yam_killer/article/details/7661449