11-12月份的学VC笔记
1.在一个MFC单视图画面上输出文字,图形.
在程序生成的View类中有一个OnDraw函数,在里面可以进行文字,图形的绘制操作.如
void CEx03aView::OnDraw(CDC* pDC)
{
pDC->TextOut(0, 0, "Hello, world!");
pDC->SelectStockObject(GRAY_BRUSH);
pDC->Ellipse(CRect(0, 20, 100, 120));
}
2.关于程序的窗口注册,窗口的样式是在程序的Frm类中完成的,典型的函数有:
CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
可以在以上函数中更改窗口的样式.
OnCreate是用来响应程序的WM_CREATE消息的.
3.在程序中::AfxGetApp()函数可以获得其本身的CWinApp对象,可以用它来做很多
事,如:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
AfxGetApp()->LoadStandardCursor(IDC_ARROW),
AfxGetApp()->LoadIcon(IDR_MAINFRAME));
}
4.在其它函数中(CView类的OnDraw函数除外)获得CDC对象的方法:
void CNewex03aView::OnLButtonDown(UINT nFlags, CPoint point)
{
CDC* dc=GetDC(); //初始化dc 或者用CDC dc(this);
dc->TextOut(100,100,"hello text");
CView::OnLButtonDown(nFlags, point);
}
5.改变显示的映射模式:
使用pDC->SetMapMode()函数可以改变其映射模式,如:
pDC->SetMapMode(MM_TEXT);
pDC->SetMapMode(MM_LOENGLISH); //0.01英寸
pDC->SetMapMode(MM_LOMETRIC); //0.1mm
6.对Edit控件进行限制某些字符输入的方法:
新增一个类,该类继承CEdit,对该类的OnChar函数写入如下代码:
void CNumEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if(::IsCharAlphaNumeric((TCHAR)nChar) && !IsCharAlpha((TCHAR)nChar))
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
7.设置对话框中静态文本(CStatic)控件的值:
SetDlgItemText(IDC_TEXT,"abcdefg");
注:不知道为什么前面加两个::就编译不能通过.
8.对话框中资源的赋初始值在C**Dialog::OnInitDialog函数中完成,而不是在
C**Dialog的构造函数中完成。
另,有时候,在OnCreate函数中也完成不了,比如,我有一个ListBox,想初始化
其值。在OnCreate函数中写入
CListBox* lb = (CListBox*)GetDlgItem(IDC_LIST1);
lb->AddString("abcdefg");
会报错,但放在OnInitDialog函数中,正确~
9.ListBox控件用法:
CDialog::OnInitDialog();
m_listbox.AddString("First String");
m_listbox.AddString("Second String");
m_listbox.SetCurSel(1);
int index = m_listbox.GetCurSel();
if (index != LB_ERR)
{
m_listbox.GetText(index, m_selected);
}
else
{
m_selected = "";
}
ListBox是一个不同于其它控件的东西,如果m_listbox是用Member Variables定义
的CListBox控件对象.它必须要等到dialog box在窗口显示出来后才有效,所以,
要用它的各个属性,必须在CDialog::OnInitDialog()函数调用完了之后.
当然,如果m_listbox这个对象是用GetDlgItem得来的,那么可以在OnInitDialog
函数之前.
还有,对于其它控件而言,取得它的设置数据,可以在DoModal()之后,比如:
dlg.DoModal();
MessageBox(dlg.m_Radio); //取radiobutton设置的值
MessageBox(dlg.m_edit); //取edit设置的值
但是,对于ListBox,则不可
MessageBox(dlg.m_listbox);是不行的.
如果要按OK后取得它的值,则必须另外设定一个全局CString变量.
CString c_listValue;
然后,在Dialog的OnOK函数中保存ListBox的值.
CDialog::OnOK()
{
c_listValue = m_listbox.GetText(index,m_selected);
}
然后在DoModal()之后可以取c_listValue的值.
10.整数转化成CString.
int i=1;
CString str;
str.Format("%d",i);
11.在其它函数中调用CView::OnDraw()函数重新Paint的函数
只要调用Invalidate()函数即可。
12.定义一种自定义的字体方法:
LOGFONT logFont;
logFont.lfHeight = 8;
logFont.lfWidth = 0;
logFont.lfEscapement = 2;
logFont.lfOrientation = 0;
logFont.lfWeight = FW_BOLD;
logFont.lfItalic = 0;
logFont.lfQuality = PROOF_QUALITY;
...... //还有其它许多logFont的参数可设置
strcpy(logFont.lfFaceName,"Times New Roman");
CFont font;
font.CreateFontIndirect(&logFont);
CFont* oldFont = pDC->SelectObject(&font); //定义字体
pDC->TextOut(20, 50, "A sample font.");
pDC->SelectObject(&oldFont);
13.更改窗口式样,包括窗口长度,宽度,最大化,最小化等
重写CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数,
其中CREATESTRUCT结构体定义了窗口的属性,样式。
例:
cs.cx = 440;
cs.cy = 480;
cs.style &= ~WS_SIZEBOX;
14.在View类中取得文档类的对象。
CMyAppDoc* pDoc->GetDocument();
15.获得当前时间,并把当前时间转化成字符串
CTime time = CTime::GetCurrentTime();
CString strTime = time.Format("Changed at %B %d %H:%M:%S")
16.当更改CDocument中数据的时候,需要调用:
pDoc->SetModifiedFlag()函数。以表示文档中的数据已更改。
17.自定义类的数据保存:
详见:http://www.vckbase.com/vckbase/seuvc6/ch07/ch07.htm
(1).首先,在VC中定义一个类CMessage,继承自CObject,在定义的时候,用VC的
ClassWizard功能,其中Class Type一项应该选择Generic Class,而不是MFC Class.
一共要有三个数据 CString strMsg1,strMsg2,strMsg3,用ClassWizard的Add
Memeber Variable功能定义。
(2).定义两个自定义函数用来获得和设置这三个数据
public CMessage::SetMessage(UINT msgNum,CString strMsg);
public CMessage::GetMessage(UINT msgNum);
(3).继承CObject的Serialize函数,用于对其数据的保存和取得。
virtual void Serialize(CArchive& ar);
其实现如下:
void CMessage::Serialize(CArchive &ar)
{
CObject::Serialize(ar);
if(ar.IsStoring())
{
ar<<msgStr1<<msgStr2<<msgStr3;
}
else
{
ar>>msgStr1>>msgStr2>>msgStr3;
}
}
(4)在message.h中添加DECLARE_SERIAL宏,在message.h中添加IMPLEMENT_SERIAL
宏,如下:
message.h>>>
public:
DECLARE_SERIAL(CMessage)
message.cpp>>>
IMPLEMENT_SERIAL(CMessage,CObject,0)
这样,这个类的定义就完了,
(5)下面的工作有:
在AppDoc.h中包含message.h头文件.
在AppDoc.h中添加类的对象 CMessage m_Message
在AppDoc类中的OnNewDocument函数中对m_Message对象中的数据初始化
在AppDoc类中的Serialize函数中保存和获得该对象的数据流
m_Message.Serialize(ar);
在AppView类中根据用户不同的操作来设置m_Message对象的值.并且值更改后需调
用pDoc->SetModifiedFlag (),以表示文档中的数据已经更改.
18.写文件和读文件的一些操作:
写文件:
CFile file("TESTFILE.TXT", CFile::modeCreate | CFile::modeWrite);
// Write data to the file.
CString message("Hello file!");
int length = message.GetLength();
file.Write((LPCTSTR)message, length);
// Obtain information about the file.
CString filePath = file.GetFilePath();
Int fileLength = file.GetLength();
读文件:
// Open the file.
CFile file("TESTFILE.TXT", CFile::modeRead);
// Read data from the file.
char s[81];
int bytesRead = file.Read(s, 80);
s[bytesRead] = 0;
CString message = s;
19.获得当前可绘区的rect
CRect rect;
GetClientRect(&rect);
pDC->DrawText(pDoc->GetString(), &rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
20.在Doc类中使视图类重新绘制.
UpdateAllViews(NULL);
例:
void CShowStringDoc::OnToolsOption()
{
COptionsDialog dlg;
dlg.m_string = m_String;
if(dlg.DoModal()==IDOK)
{
m_String = dlg.m_string;
SetModifiedFlag();
UpdateAllViews(NULL);
}
}
21.Dot Net中为什么没有VC6一样的ClassWizard and WizardBar
详见:http://msdn.microsoft.com/library/default.asp?url=/library/en-
us/vccore/html/vcgrfWhereIsClassWizardInVisualCNET.asp
22.在OnDraw中输出有颜色的文字:
COLORREF oldcolor;
oldcolor = pDC->SetTextColor(RGB(0xFF,0,0));
pDC->DrawText(pDoc->GetString(), &rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
pDC->SetTextColor(oldcolor);
23.一些FAQ的地址
http://blog.csdn.net/jiangsheng/archive/2005/12/24/561501.aspx
24.在主程序中(而不是对话框中)新建一个控件,比如CButton。
在主程序中,不像在对话框中,可以拖拽并程序自动分配ID。
首先,要申请一个ID号,在View->Resource Symbols->点击New Button.分配一个
需要的ID号,比如ID_BUTTON1
然后,在CMainFrame类的头文件中:
CButton m_button;
然后在CMainFrame类的OnCreate函数中
if(!m_button.Create(Click here",WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
CRect(0,0,20,20), this, ID_BUTTON1))
{
TRACE0("Failed to create checkbox\n");
return -1; // fail to create
}
其它控件类似,
25.可以容纳其它控件的CReBar.(类似于toolbar)
在CMainFrame类的头文件中:
CReBar m_rebar;
CButton m_button;
在CMainFrame的OnCreate函数中:
if(!m_rebar.Create(this))
{
TRACE0("Faild to create rebar");
return -1;
}
if(!m_button.Create("Click here",WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,
CRect(0,0,20,20), this, IDC_CHECK)) //IDC_CHECK的建立见24.
{
TRACE0("Failed to create checkbox\n");
return -1; // fail to create
}
m_rebar.AddBar(&m_button, "On The Bar", NULL, RBBS_BREAK |
RBBS_GRIPPERALWAYS);
26.获得CMainFrame的对象.
由于CMainFrame类中有控件的对象,所以,要在View类中操作控件的话.必须先得
到CMainFrame的对象.
使用函数(CMainFrame*)(AfxGetApp()->m_pMainWnd)就可以得到.
例如,在24.中,要操作CButton的对象.可以这样.
if(((CMainFrame*)(AfxGetApp()->m_pMainWnd))->m_button.GetCheck())
....
当然,要在CMainFrame中包含mainFrame.h头文件.
27.AfxGetApp()得到的只是CWinApp类的对象,你必须强制转换为你自己的类,才
能找到变量,如:
((CYourApp*)AfxGetApp())->YourMember = ...
28.更新文本框和获得文本框中的内容:
UpdateData(FALSE)用于更新文本框中的内容,即可以用来读取对话框类的数据成员
UpdateData(TRUE)用于获得文本框中的内容,即写入数据成员。
例如:
DDX_Text(pDX, IDC_EDIT1, m_sEdit1);//把变量m_sEdit1与IDC_EDIT1建立联系
//让文本框中显示“Hello”
m_sEdit1 = "Hello";
UpdateData(FALSE);
//获得文本框的用户输入内容
UpdateData(TRUE);//如果用户写入的是"Oh my god",那么m_sEdit1的值应该是
"Oh my god"
if (m_sEdit1 == "Oh my god")
{
AfxMessageBox("OK");
}
还有别的方法可以 获得文本框中的内容 或 向文本框中写入内容
例如:
CString str;
GetDlgItemText(IDC_EDIT1, str);//获得文本框中的内容,保存在str中
SetDlgItemText(IDC_EDIT1, str);//向文本框中写入str
29:有关File的操作
打开文件:
CFile file;
file.open(_T("File.txt"),CFile::modeReadWrite);
其中,第二个参数可以更据情况设定更多的参数信息。
新建文件:
CFile file(_T("File.txt"),CFile::modeReadWrite | CFile::modeCreate );
读文件:
BYTE buffer[0x1000];
CFile file(_T("File.txt"),CFile::modeRead);
DWORD dwBytesRemaining = file.GetLength();
while(dwBytesRemaining)
{
UINT nByteRead = file.Read(buffer,sizeof(buffer));
dwBytesRemaining -= nByteRead;
}
写文件:
file.Seek(dwPosition,CFile::begin); //从File的开始起第dwPosition个位置写
file.Write(buffer,nBytesRead);
定位文件:
CFile::SeekToBegin() 定位到文件开头
CFile::SeekToEnd() 定位到文件结束
更改文件名,删除文件:
CFile::Rename函数,和CFile::Remove()函数
30.枚举文件和文件夹:
有两个函数和一个结构体: ::FindFristFile和::FindNextFile,WIN32_FIND_DATA
其中WIN32_FIND_DATA结构体中的dwFileAttributes参数表示是文件还是文件夹,
CFileName[MAX_PATH]表示文件名
WIN32_FIND_DATA fd;
HANDLE hFile = ::FileFristFile(_T("C:\\*.*"),&fd);
if(hFile!=INVALID_HANDLE_VALUE)
{
do
{
if(!(fd.dwFileAttributes * FILE_ATTRIBUTE_DIRECTORY)) //如果不是文件夹
TRACE(_T("%s\n"),fd.cFileName)
}while(::FileNextFile(hFind,&fd));
}
设置当前文件夹: ::SetCurrentDirectory(LPCTSTR fileDirectory);
利用递归遍历文件夹:
void EnumerateFolders()
{
WIN32_FIND_DATA fd;
HADNLE hFile = ::FindFristFile(_T("*.*"),&fd);
if(hFile!=INVALID_HANDLE_VALUE)
{
do
{
if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
{
CString name = fd.cFileName;
if(name!=_T(".") || name!=_T(".."))
{
//TO DO: Get this Directory to do something...
::SetCurrentDirectory(name);
EnumerateFolders();
::SetCurrentDirectory(_T("..")) //don't forget call this function.
}
}
}while(::FindNextFile(hFile,&fd));
::FildClose(hFind);
}
}
31.有关HDC和CDC
HDC是DC的句柄,在API编程上面使用较多。
CDC针对的是MFC类库,是对HDC句柄的类包装。
两者的区别很像HBitmap和CBitmap这种区别。
在MFC的DOC/VIEW结构中,CAppView::GetDC()返回的是CDC类。
而全局的::GetDC(HWND)返回的是HDC。注意其区别。
32.怎样在主界面中做成记事本一样的样式,可以由用户输入字符。
要在主界面中做成记事本,首先要将CEdit控件添加进主界面中去。
首先,在CAppView类中添加一个CEdit m_wndEdit变量。在Resource.h中添加一个
IDC_EDIT 102的资源
然后,在CAppView::OnCreate函数中将CEdit控件Create出来。
CAppView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CWnd::OnCreate(..)==-1)
......
m_wndEdit.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE|ES_AUTOVSCROLL,CRect(0,0,0,0),this,IDC_EDIT);
}
由于在OnCreate函数中无法用GetClientRect得知客户端界面的大小。所以,暂时
让其大小为CRect(0,0,0,0), 撑大Edit控件的工作在OnPaint函数中实现
CAppView::OnPaint()
{
CRect rect;
GetClientRect(&rect);
m_wndEdit.MoveWindow(rect.left,rect.top,rect.right,rect.bottom); //让
Edit控件占据整个客户端界面。
}
这里主要用到了如何动态创建控件的知识。CEdit类还有许多动作,可以用来实现
更多的功能。如:
m_wndEdit.SetFocus()
m_wndEdit.Cut()
m_wndEdit.Copy()
m_wndEdit.Paste()
m_wndEdit.Clear()
m_wndEdit.Undo()
m_wndEdit.GetSel(nStart,nEnd)
-----------------------------------------------------------------------------------------
33.使用FileDialog通用对话框
CFileDialog dlg(TRUE,"bmp","*.bmp");
if(dlg.DoModal()==IDOK)
{
CDC* dc = GetDC();
dc->TextOut(30,30,dlg.GetPathName());
}
34.在子对话框中更改父对话框中的控件属性:
GetParent()->GetDlgItem(IDOK)->SetWindowText("Delete");
35.弹出是和否对话框
CString strMessage;
strMessage.Format("are you sure to delete %s files",filename);
if(AfxMessageBox(strMessage,MB_YESNO)==IDYES)
{
...
}
36.删除目录下所有类型的文件 和 删除目录下某个文件
删除所有类型文件:
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(_T("*.*"),&fd);
//::FindFirstFile(m_strFilePath + "\\*.*", &fd); //指定目录
if(hFind != INVALID_HANDLE_VALUE)
{
if(::DeleteFile(fData.cFileName)==FALSE)
{
strMessage.Format("Unable to delete file %s\n",fData.cFileName);
AfxMessageBox(strMessage);
break;
}
}
//读取文件与读取目录的区别在于if里面的!呵呵
if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
TRACE(_T("%s\n"),fd.cFileName);
//delete the file
}while(::FindNextFile(hFind,&fd));
::FindClose(hFind);
}
删除一个文件
CFile::Remove(strSingleFilename);
37. 怎么样获得类的动态信息:
在VC中,定义一个类,可以在类头文件中定义DECLARE_DYNAMIC()宏,在类的实现
中定义IMPLEMENT_DYNAMIC()宏,就可以实现RTTI的能。
这些宏的背后有一个类:CRuntimeClass. 比如,我们定义一个类:
class CMyClass:public CView
{
DECLARE_DYNAMIC(CMyClass)
public:
CMyClass();
};
实现该类:
IMPLEMENT_DYNAMIC(CMyClass,CView)
CMyClass::CMyClass()
{
//Constractor
}
就等于把CMyClass类加进了一个CRuntimeClass的链表,可以用这个链表查询很有
用的相关信息。
CRuntimeClass有几个属性,可以在程序中很方便的用到。
m_lpszClassName 属性:表示这个类的名称
m_pfnGetBaseClass()或 m_pBaseClass:表示这个类的父类。
IsDerivedFrom():表示这个类是不是继承自某个类。
m_pNextClass: 在CRuntimeClass链表中的下一个节点(注意,不是父类。)
举例:我在CMyView::OnLButtonDown事件中实现打印出当前的类及他的父类(一直
到根节点)的信息。
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
CRuntimeClass* runClass = GetRuntimeClass();
int site = 20;
while(runClass!=NULL)
{
dc->TextOut(site,site,runClass->m_lpszClassName);
runClass = runClass->m_pfnGetBaseClass();
site=site+20;
}
}
以上会在屏幕上打印出:CMyView CView CWnd CCmdTarget CObject信息
GetRunTimeClass是全局的函数,只要实现了RTTI,都可以用这个类获得帮助。
除了查询类的信息之外,还可以用IsKindOf()函数查询是不是属于某个类。
如: CView view = new CView;
view->IsKindOf(RUNTIME_CLASS(CView)); //返回1
view->IsKindOf(RUNTIME_CLASS(CCmd)); //返回1
view->IsKindOf(RUNTIME_CLASS(CWinApp)); //返回0
38.消息:
(1) AFX_MSGMAP_ENTRY 定义了消的资料结构
struct AFX_MSGMAP_ENTRY
{
UINT nMessage;
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn; //消息处理函数 typedef void (CCmdTarget::*AFX_PMSG)(void)
}
(2)AFX_MSGMAP 定义了消息网
struct AFX_MSGMAP
{
AFX_MSGMAP* pBaseMessageMap;
AFX_MSGMAP_ENTRY* lpEntries; //消息入口
}
(3)定义消息宏
#define DECLARE_MESSAGE_MAP() \
static AFX_MSGMAP_ENTRY _messageEntries[]; \
static AFX_MSGMAP messageMap; \
virtual AFX_MSGMAP* GetMessageMap() const;
(4)实现消息宏
#define BEGIN_MESSAGE_MAP(theclass,baseclass)
AFX_MSGMAP* theClass::GetMessageMap() const \
{return &theclass::messageMap;}
AFX_MSGMAP theClass::messageMap = \
{&(baseClass::messageMap),\
(AFX_MSGMAP_ENTRY*)&(theClass::_messageEntries)};\
AFX_MSGMAP_ENTRY theClass::_messageEntries[]=\
{
(5)消息宏
#define ON_COMMAND(id,memberFxn) \
{WM_COMMAND,0,(WORD)id,(WORD)id,AfxSig_vv,(AFX_PMSG)memberFxn},
(6)消息结束
#define END_MESSAGE_MAP()\
{0,0,0,0,AfxSig_end,(AFX_PMSG)0} \
}
所以,如果在CMyView类中有这样的消息形态
class CMyView:public CView
{
public:
afx_msg void OnEditUndo();
DECLARE_MESSAGE_MAP()
}
//in implementation file
#define ID_EDIT_UNDO 122
BEGINE_MESSAGE_MAP(CMyView,CView)
ON_COMMAND(ID_EDIT_UNDO,OnEditUndo)
END_MESSAGE_MAP
就相当于
class CMyView:public CView
{
public:
afx_msg void OnEditUndo();
tatic AFX_MSGMAP_ENTRY _messageEntries[];
static AFX_MSGMAP messageMap;
virtual AFX_MSGMAP* GetMessageMap() const;
}
//in implementation file
#define ID_EDIT_UNDO 122
AFX_MSGMAP* CMyView::GetMessageMap() const
{
return &CMyView::messageMap;
}
AFX_MSGMAP CMyView::messageMap =
{
&(CView::messageMap),
(AFX_MSGMAP_ENTRY*)&(CMyView::_messageEntries)
};
AFX_MSGMAP_ENTRY CMyView::_messageEntries[]=
{
{WM_COMMAND,0,(WORD)122,(WORD)122,1,(AFX_PMSG)OnEditUndo}, //AfxSig_vv为1
{0,0,0,0,0,(AFX_PMSG)0} //AfxSig_end为0
}
一般来说,可以用this->GetMessageMap()->pBaseMessageMap 获得基类的消息网
可以用this->GetMessageMap()->lpEntries[i]获得本类似第几个消息
39.消息的流向
对于一般的消息(WM_xxx),消息的流向是从派生类流向父类中。
对于WM_COMMAND消息,就要看在什么类中发出的WM_COMMAND的消息了。
一般来说,消息从CView流向CDoc流向CFrameWnd流向CWinApp.当然,具体的步聚如
CMyView->CView->
CWnd->CCmdTarget->CMyDoc->CDocument->CCmdTarget->CMyFrameWnd
->CFrameWnd->CWnd->CCmdTarget->CMyWinApp->CWinApp->CWinThread
->CCmdTarget,如果任何一个环节有处理该消息的函数,则会停止消息的流动。
40.消息映射宏的分类
1.用于Windows消息的宏,前缀如"ON_WM_***"
eg.ON_WM_LBUTTONDOWN()
宏不带参数,因为它对应的消息和消息处理函数名称,函数原型是确定的.MFC提供
了这类消息处理函数的定义和缺省实现。每个这样的宏处理不同的 Windows消息。
2.用于命令消息的宏ON_COMMAND
eg.ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
带有参数,需要通过参数指定命令ID和消息处理函数
但消息处理函数的原型是void(void),不带参数,不返回值.
3.用于控制通知消息的宏
如ON_CONTROL, ON_LBN_DBCLK 这类宏可能有三个参数(窗口ID,通知码,消息处理函
数),可能有二个参数(窗口ID,消息处理函数),
这个宏比较杂,见http://www.vczx.com/tutorial/mfc/mfc4.php
4.用户界面接口状态更新的ON_UPDATE_COMMAND_UP宏
eg.ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
带有两个参数,需要指定用户接口对象ID和消息处理函数。消息映射条目的第一个
成员nMessage被指定为WM_COMMAND,第二个成员 nCode被指定为-1,第三个成员
nID和第四个成员nLastID都指定为用户接口对象ID。消息处理函数的原型是 void
(CCmdUI*),参数指向一个CCmdUI对象,不返回值。
void CEx08AView::OnUpdateEditUndo(CCmdUI* pCmdUI)
39.在CMyView类中获得HWND
在CWnd类中,有一成员,名叫m_hWnd,它就是我们所需要的HWND窗口句柄,可以真
接拿来用.
40.在MFC各类中一个对象得到另一个对象的方法:
本对象 要得到的对象 使用的成员函数
CDocument对象 视列表 GetFirstViewPosition 、GetNextView
文档模板 GetDocTemplate
CView对象 文档对象 GetDocument
边框窗口 GetParentFrame
CMDIChildWnd或 活动视 GetActiveView
CFrameWnd对象 活动视的文档 GetActiveDocument
CMDIFrameWnd对象 活动文档边框窗口 MDIGetActive
还有就是在一个对象通知另一个对象的方法:
在CView对象中通知文档更新所有视 CDocument::UpdateAllViews();
在CDocument对象中更新一个视 CView::OnUpdate()
在CFrameWnd或CMDIFrameWnd对象中通知一个视为活动视CView::OnActivateView
设置为活动视 SetActivateView
40.定时处理
凡继承自CWnd的类都有一个SetTimer函数,可以用来设置定时处理。继承自CWnd的
类也有系统定义的接收WM_TIMER的OnTimer函数
比如,我要在一个对话框中每隔2秒刷新一次数据.可以这样做:
CMyDialog.cpp
//定义时间通知的ID,用来区别各个定时器。
#define ID_TIMER_NOTIFY 1
...
BOOL CMyDialog::OnInitDialog()
{
...
SetTimer(ID_TIMER_NOTIFY,2000,0);
//设置定时器,ID_TIMER_NOTIFY表时定时的ID,2000为2秒呼叫OnTimer函数,0表
示没有自定义的TimerProc,用系统定义的OnTimer函数。
}
//用ClassWizzard添加OnTimer函数
void CMyDialog::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
SetMemory(); //时间到了后处理的函数
CDialog::OnTimer(nIDEvent);
}
void CMyDialog::SetMemory()
{
UpdateData(FALSE); //刷新对话框。
}
去掉定时器,用KillTimer(UINT TIME_ID)函数来取消。
41.关于查询系统相关信息的函数GetSystemInfo
原型:VOID GetSystemInfo(LPSYSTEM_INFO psinf);
可以用它来获得
CPU页面的大小(dwPageSize)、
进程地址空间中用户模式最小地址、最大地址(lpMinimumApplicationAddress,
lpMaximumApplicationAddress)、
CPU数目(dwNumberOfProcessors)、
处理器的结构(wProcessorArchitecture,wProcessorLevel)、
处理器的级别(wProcessorRevision)
42.用来查询内存信息的函数:
VOID GlobalMemoryStatus(LPMEMORYSTATUS pmst);
包括:
内存管理系统的大致繁忙程度.dwMemoryLoad
存在的物理存储器的总字节数 ullTotalPhys
硬盘上的页文件中包含的最大字节数 ullTotalPageFile
每个进程的地址空间中私有的总字节数 ullTotalVirtual
地址空间中的所有空闲区域 ullAvailVirtual
等等...
43.关于stdafx.h头文件。
今天写一个Win32程序,用API实现,自己添加了一个.h文件,一个实现.h的cpp文件。
编译时,遇到unexpected end of file while looking for precompiled header
directive错误.
解决方案是在新加的cpp文件的第一行(记住是第一行)加入#include "stdafx.h"
文件,因为默认的编译选项是要预先编译头文件,预先编译需要用到添加的这些文件.
还有一种办法是,取消预先编译头文件选项:
1. [Project] - [Settings] - [C/C++] - [Category]
2. 选择 [Precomplied Headers]
3. 单选 [Not Using Precomplied Headers]
4. [OK]
头文件预编译: 把一个工程中使用的一些MFC标准头文件(Windows.h, Afxwin.h
等)预先编译,以后的工程编译时,不再编译这部分文件,只是用预编译的结果,
编译器认为所有在指令#i nclude " stdafx.h"之前的代码都是预编译的,跳过这
条指令,直接编译其后面的所有代码,这样可以加快编译速度,节省时间。
44.关于DebugBreak函数:
MSDN记载:You can call the DebugBreak Win32 function at any point in
your code. DebugBreak has the same effect as setting a breakpoint at
that location.
45.DialogBox函数
调用对话框的函数.
DialogBox(HANDLE hInstance,LPCTSTR lpTemplate,HWND hWndParent,DLGPROC
lpDialogFunc);
hInstance : 本程序的实例,注意,不是窗口的hwnd,不要搞混淆.
lpTemplate:对话框的title,可以用Text("Title")定义,也可以用
MAKEINTRESOURCE函数来定义
hWndParent:父窗口的句柄,注意,不是程序的实例.
lpDialogFunc:默认的对话框消息处理函数.
如果在主窗口的消息处理中调用对话框.那么:
CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static HINSTANCE hInstance;
switch(message)
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance; //首先要获得程序的实例
(hInstance)
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_APP_ABOUT:
DialogBox(hInstance,MAKEINTRESOURCE(IDD_ABOUT),hwnd,Dlg_Proc);
}
//下略...
}
如果应用程序就是一个对话框程序,那么DialogBox函数直接在WinMain中调用.应
用程序的实例句柄也不需要通过lParam来获得
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR
lpCmdLine,int)
{
DialogBox(hInstance,MAKEINTRESOURCE(IDD_ABOUT),NULL,Dlg_Proc);
//程序实例就是WinMain参数传过来的hInstance,父窗口句柄为空
}
##############################################################################################################################################
##############################################################################################################################################
Windows中的内存知识:
每个进程的内存从0x00000000-0xFFFFFFFF.其中的任何一个地址对于应用程序来说
都是有效的.
线程访问属于他进程的内存地址,不能访问其它进程空间中的地址.
操作系统本身的内存相对于应用程序来说也是隐藏的.与Win9x是不同的.
进程中的内存的分配方式:
0x00000000-0x0000FFFF --- NULL指针区
0x00010000-0x7FFEFFFF --- 用户方式
0x7FFF0000-0x7FFFFFFF --- 64KB禁止进入
0x80000000-0xFFFFFFFF --- 文件内核方式
NULL指针区保存了设为NULL的指针,帮助程序员了解NULL指针的情况.
用户方式为该进程私有,应用程序的exe和dll加载到这个分区.
内核方式存放操作系统代码,用于线程调度、内存管理、文件系统支持、网络支持
和所有设备驱动程序的代码全部在这个分区加载。驻留在这个分区中的一切均可被
所有进程共享.
注意,是进程共享,但应用程序不能访问,访问则违规.
内存分配函数:VirtualAlloc()
内存分配的粒度边界:64KB(FFFF),注意是边界,而不是分配的最小值.
内存是有页面的,对于x86结构的计算机来说,每个页面的大小是4KB(FFF)
页面的作用对于分配来说,必须是4KB的倍数.如果分配了10KB,那么系统将保留12KB.
内存分配完了之后,获得了地址空间中一块保留的内存区域.还有的工作就是将真
正的物理存储器映射到该内存区域中.
若要将物理存储器提交给一个已保留的地址空间区域,也要调用VirtualAlloc函数。
回收物理存储器VirtualFree函数.
所谓虚拟内存,我的理解就是将内存中不用的页面放到磁盘中去(形成页文件).
用的时候在放到内存中.
获得进程的保留空间的VirtualAlloc函数有可能就在磁盘中分配页文件.而不是一
定要在内存中.
硬盘上的一个程序的文件映像(这是个. exe文件或DLL文件)用作地址空间的区域
的物理存储器时,它称为内存映射文件。
已经分配了物理存储器的各个页面,可以具有不同的属性:
PAGE_NOACCESS / PAGE_READONLY / PAGE_READWRITE / PAGE_EXECUTE /
PAGE_EXECUTE_READ /PAGE_EXECUTE_READWRITE / PAGE_WRITECOPY /
PAGFE_EXECUTE_WRITECOPY
关于页面的Copy-On-Write属性
可参考:ms-help://MS.MSDNQTR.2003JAN.1033/memory/base/memory_protection.htm
摘入如下
Copy-on-write protection is an optimization that allows multiple
processes to map their virtual address spaces such that they share a
physical page until one of the processes modifies the page. This is part
of a technique called lazy evaluation, which allows the system to
conserve physical memory and time by not performing an operation until
absolutely necessary.
For example, suppose two processes load pages from the same DLL into
their virtual memory spaces. These virtual memory pages are mapped to
the same physical memory pages for both processes. As long as neither
process writes to these pages, they can map to and share, the same
physical pages, as shown in the following diagram.
If Process 1 writes to one of these pages, the contents of the physical
page are copied to another physical page and the virtual memory map is
updated for Process 1. Both processes now have their own instance of the
page in physical memory. Therefore, it is not possible for one process
to write to a shared physical page and for the other process to see the
changes.
在Process1中,PageB 被重写,此时,在物理内存中重新开辟一块新的页面,并把
PageB Copy到PageD,然后,PageD此不再属于Copy-On-Write属性,而是属于
Process1的私有页面,但PageB还是保留Copy -On-Write属性
自由的,保留的和已提交的内存:
在任意给定的时间,进程中的每个地址都可以被当作自由的,保留的,和已提交的.
进程开始时,所有的地址都是自由的,意味着它们都是自由空间并且可以被提交到
内存,或者为将来使用而保留起来.在任何自由的地址能够被使用前,必须首先被
分配为保留的,或已提交的,试图访问一个保留的或已交的地址将产生一个访问冲
突异常.(access violation exception)
一个进程中的所有2GB的地址要么为了使用而是自由的,要么为了将来使用而是保
留的,要么已经提交到特定的内存(在使用的).