青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

如需改變標(biāo)題(Caption)時(shí),使之換行可以用c++轉(zhuǎn)義符\n
vc++中不能改變單個(gè)控件的字體,可以使用位圖改變

 

應(yīng)用程序的一般組成:應(yīng)用程序?qū)ο蠛痛翱趯?duì)象。其中沒(méi)個(gè)MFC程序必須有一個(gè)應(yīng)用程序?qū)ο螅?fù)責(zé)啟動(dòng)和終止應(yīng)用程序。
文檔類(lèi):用于管理應(yīng)用程序的數(shù)據(jù)

視圖類(lèi):用于顯示應(yīng)用程序的數(shù)據(jù)

組件:專(zhuān)用的,自包含的窗口對(duì)象,它們通常是構(gòu)成用戶界面的元素。也稱(chēng)為控件。
組件必須包含在其他的窗口中,把該窗口成為斧窗口。所以控件也叫做子窗口控件。
MFC的六種嵌入窗口家族:
CStatic--用于顯示文本或圖標(biāo)控件
CButton--按鈕、復(fù)選框、單選按鈕、組框
CListBox--顯示項(xiàng)目滾動(dòng)列表的控件
CComboBox--顯示可縮回的項(xiàng)目列表的控件
CScrollBar--水平或垂直滾動(dòng)條
CEdit--單行或多行文本編輯控件

資源:特殊形式的只讀數(shù)據(jù),由一個(gè)叫做資源編譯器的程序?qū)⒅?lián)編到可執(zhí)行程序中。
資源的兩種基本形式:
二進(jìn)制--圖形資源,包括圖標(biāo)、光標(biāo)和位圖
文本--結(jié)構(gòu)化的資源,包括對(duì)話框、菜單、字符串表和加速鍵表。
二進(jìn)制資源存儲(chǔ)在一個(gè)獨(dú)立的文件中,文本資源存儲(chǔ)在普通的ASCII文本文件中,該文件描述了每個(gè)元素的結(jié)構(gòu)。該ASSCII文件稱(chēng)為資源描述文件--通常與你的項(xiàng)目同名,其擴(kuò)展名為.rc


DECLARE_MESSAGE_MAP()該宏告訴VisualC++這個(gè)類(lèi)將響應(yīng)Windows消息,該入口以及在實(shí)現(xiàn)文件中的相應(yīng)映射入口,是用于建立消息映射的高級(jí)宏系統(tǒng)的部分。消息映射保證Windows消息被交給正確的成員函數(shù)。

#include "stdafx.h"是所有MFC程序都要用到的標(biāo)準(zhǔn)應(yīng)用程序框架頭文件。它引入用語(yǔ)標(biāo)準(zhǔn)MFC組件、大多數(shù)通用擴(kuò)展以及Internet Explorer4常用控件的定義。

正式版和調(diào)試版的轉(zhuǎn)換:使用Build|Set Active Configuration.

構(gòu)造函數(shù)和InitInstance()函數(shù):
構(gòu)造函數(shù)是在對(duì)象創(chuàng)建是調(diào)用的。InitInstance()是在WinMain()調(diào)用時(shí)重載的。所以夠找函數(shù)是在WinMain()被調(diào)用前調(diào)用的。此時(shí),很多MFC系統(tǒng)還沒(méi)有完成自身的初始化,只有在主應(yīng)用程序?qū)ο蟊粯?gòu)造完畢,WinMain()才會(huì)被調(diào)用。

在一個(gè)API應(yīng)用程序中,WinMain()函數(shù)有三個(gè)任務(wù),它必須完成:
1、注冊(cè)一個(gè)新的主窗口類(lèi)
2、創(chuàng)建一個(gè)窗口的實(shí)例并顯示它
3、運(yùn)行消息循環(huán)
InitInstance()函數(shù)為MFC的WinMain()函數(shù)執(zhí)行以上內(nèi)容的第二項(xiàng)內(nèi)容。

關(guān)于CWinApp
MFC中CWinApp類(lèi)定義的關(guān)鍵幾個(gè)虛函數(shù)或可重載函數(shù)
1、InitInstance()肯定是要被重載的
2、Run()掃描消息并處理消息。
3、OnIdlc(),當(dāng)Run()在沒(méi)有消息要處理事就要調(diào)用該函數(shù)。OnIdle()可利用該機(jī)會(huì)執(zhí)行后臺(tái)任務(wù),否則該后臺(tái)任務(wù)會(huì)降低系統(tǒng)反應(yīng)速度。
4、ExitInstance(),當(dāng)一個(gè)應(yīng)用程序結(jié)束時(shí)由Run()進(jìn)行調(diào)用。

基于CDialog的窗口是以局部變量的形式在棧中創(chuàng)建的,而基于CWnd的窗口是以動(dòng)態(tài)變量的形式在自由存儲(chǔ)器(freestore)中創(chuàng)建的 。
故有對(duì)于基于CDialog類(lèi):
CFourUpdlg dlg;
m_pMainWnd=&dlg;
對(duì)于基于CWnd的類(lèi):
m_pMainWnd=new CMainWindow;

CFourUpDlg::CFourUpDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CFourUpDlg::IDD, pParent)
構(gòu)造函數(shù)中CFourUpDlg::IDD的 IDD是個(gè)枚舉量(定義在頭文件中),包含對(duì)話框模板的資源ID(包含在資源描述中)。表示CDialog構(gòu)造函數(shù)從對(duì)話框模板中讀出信息,然后構(gòu)造一個(gè)窗口,在構(gòu)造過(guò)程使用的是由模板給出的規(guī)范。為了創(chuàng)建對(duì)話框窗口(并且所有的控件都包含在對(duì)話框模板中),CDialog構(gòu)造函數(shù)使用CWnd::Create()或CWnd::CreateEx()函數(shù),對(duì)主對(duì)話框調(diào)用該函數(shù)一次并對(duì)它碰到的每個(gè)控件都調(diào)用一次。

CWnd:Create()函數(shù)
原型:
virtual BOOL Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT & rect,UINT nID,CCreateContext * pContext=NULL);


在Windows中,每個(gè)窗口都分成兩大部分:非客戶區(qū)(包括標(biāo)題條,窗口邊界)和客戶區(qū)。
其中Windows負(fù)責(zé)非客戶區(qū)的繪制

每當(dāng)Windows想繪制一個(gè)窗口的客戶區(qū)時(shí),Windows便向該窗口發(fā)送WM_PAINT消息,通常由OnPaint()成員函數(shù)。
OnPaint()大致分為:
1.獲取畫(huà)布或繪制平面,在Windows中,使用的繪制平面稱(chēng)為設(shè)備環(huán)境(device context,DC).
2.建立環(huán)境,包括收集所有需要的畫(huà)筆和畫(huà)刷,并測(cè)量工作平面的大小,以便能夠正確地在改平面上對(duì)其圖案.
3.用Windows圖形庫(kù)GDI中地函數(shù)繪制窗口

只可以在OnPaint()中創(chuàng)建CPaintDC對(duì)象.
需要在其他地方繪制時(shí)用CClientDC
調(diào)用CWnd::Invalidate()可以重畫(huà)整個(gè)窗口.

畫(huà)筆:用來(lái)畫(huà)線或圖形邊框的顏色
刷子:用于填充圖形或繪制窗口背景的顏色
庫(kù)存對(duì)象(stock object):Windows提供的幾種嵌入式的畫(huà)筆和刷子.可以通過(guò)SelectStockObject()選擇.

定時(shí)器創(chuàng)建:SetTimer(),它有三個(gè)函數(shù):
1.定時(shí)器ID--區(qū)別不同的定時(shí)器實(shí)例.
2.定時(shí)器間隔--最大分辨率55毫秒
3.定時(shí)器回調(diào)函數(shù)--特殊的回調(diào)函數(shù)的地址,該函數(shù)用于處理定時(shí)器消息.如果值為NULL,Windows將向你通知WM_TIMER消息.
在基于對(duì)話框的應(yīng)用程序中,OnInitDialog()函數(shù)是最佳的創(chuàng)建定時(shí)器的地方.如果創(chuàng)建成功,SetTimer()函數(shù)返回的定時(shí)器ID于所使用的函數(shù)相同.
刪除定時(shí)器:
KillTimer()向之傳遞構(gòu)造定時(shí)器所使用的ID.定時(shí)器是個(gè)受限的全局資源.當(dāng)用完定時(shí)器后,調(diào)KillTimer()是個(gè)必須的過(guò)程.通常可以相應(yīng)WM_DESTORY消息時(shí)處理KillTimer()函數(shù)
定時(shí)器的相應(yīng):
當(dāng)計(jì)數(shù)器溢出時(shí),產(chǎn)生一個(gè)WM_TIMER消息,通常在OnTimer()處理WM_TIMER消息


創(chuàng)建畫(huà)筆:
第一種:使用構(gòu)造函數(shù),如:CPen greePen(PS_SOLID,10,RGB(0,255,0));
第二種:兩步法
CPen greenPen;
greePen.CreatePen(PS_SOLID,10,RGB(0,255,0));
第三中:創(chuàng)建LOGPEN結(jié)構(gòu)的實(shí)例來(lái)構(gòu)造
LOGPEN lp;
lp.lopnstyle=PS_SDLID;
lp.lopnWidth=10;
lp.lopnColor=RGB(0,255,0);
Cpen greenPen;
GreenPen.CreatePenIndirect(&lp);

posted @ 2008-12-15 21:20 wrh 閱讀(270) | 評(píng)論 (0)編輯 收藏

函數(shù):fopen()


fopen

打開(kāi)文件或者 URL。

語(yǔ)法: int fopen(string filename, string mode);

返回值: 整數(shù)

函數(shù)種類(lèi): 文件存取

內(nèi)容說(shuō)明

說(shuō)明: 本函數(shù)可用來(lái)打開(kāi)本地或者遠(yuǎn)端的文件。若參數(shù) filename 為 "http://......" 則本函數(shù)利用 HTTP 1.0 協(xié)議與服務(wù)器連接,文件指針則指到服務(wù)器返回文件的起始處。若參數(shù) filename 為 "ftp://......." 則本函數(shù)會(huì)與服務(wù)器連接,文件指針指到指定的文件處。若 FTP 服務(wù)器沒(méi)有支持被動(dòng)模式 (passive mode ftp) 則返回失敗值。打開(kāi)的 FTP 文件可以是讀取或?qū)懭肫渲兄唬荒茏x或?qū)懚N同時(shí)使用。其它的情形,本函數(shù)打開(kāi)本地的文件,文件的指針則指向打開(kāi)的文件。若開(kāi)文件失敗,則返回 false 值。

字符串參數(shù) mode 可以是下列的情形:

  • 'r' 開(kāi)文件方式為只讀,文件指針指到開(kāi)始處。
  • 'r+' 開(kāi)文件方式為可讀寫(xiě),文件指針指到開(kāi)始處。
  • 'w' 開(kāi)文件方式為寫(xiě)入,文件指針指到開(kāi)始處,并將原文件的長(zhǎng)度設(shè)為 0。若文件不存在,則建立新文件。
  • 'w+' 開(kāi)文件方式為可讀寫(xiě),文件指針指到開(kāi)始處,并將原文件的長(zhǎng)度設(shè)為 0。若文件不存在,則建立新文件。
  • 'a' 開(kāi)文件方式為寫(xiě)入,文件指針指到文件最后。若文件不存在,則建立新文件。
  • 'a+' 開(kāi)文件方式為可讀寫(xiě),文件指針指到文件最后。若文件不存在,則建立新文件。
  • 'b' 若操作系統(tǒng)的文字及二進(jìn)位文件不同,則可以用此參數(shù),UNIX 系統(tǒng)不需要使用本參數(shù)。

使用范例

第一行為 UNIX 系統(tǒng)使用;第二行是 Windows 系列系統(tǒng)的用法;第三、四行則為 URL 的使用范例。

<?       
$fp 
fopen("/home/rasmus/file.txt""r");
$fp fopen("c:\\mydata\\info.txt""r");
$fp fopen("http://www.php.net/""r");
$fp fopen("ftp://user:password@my.com/""w");
?>

參考

fclose()  popen()  fsockopen()

posted @ 2008-12-13 19:21 wrh 閱讀(522) | 評(píng)論 (0)編輯 收藏
---- 方法一:調(diào)用CWinApp類(lèi)的成員函數(shù)SetDialogBkColor來(lái)實(shí)現(xiàn)。
---- 其中函數(shù)的第一個(gè)參數(shù)指定了背景顏色,第二個(gè)參數(shù)指定了文本顏色。下面的例子是將應(yīng)用程序?qū)υ捒蛟O(shè)置為藍(lán)色背景和紅色文本,步驟如下:
---- ① 新建一個(gè)基于Dialog的MFC AppWizard應(yīng)用程序ExampleDlg。
---- ② 在CExampleDlgApp ::InitInstance()中添加如下代碼:
BOOL CExampleDlgApp: : InitInstance ( )
{
...
    CExampleDlgDlg dlg;
    m_pMainWnd = &dlg;
//先于DoModal()調(diào)用,將對(duì)話框設(shè)置為藍(lán)色背景、紅色文本
    SetDialogBkColor(RGB(0,0,255),RGB(255,0,0));
    int nResponse = dlg.DoModal();
...
}
---- 編譯并運(yùn)行,此時(shí)對(duì)話框的背景色和文本色已發(fā)生了改變。值得注意的是:在調(diào)用DoModal()之前必須先調(diào)用SetDialogBkColor,且此方法是將改變應(yīng)用程序中所有的對(duì)話框顏色,并不能針對(duì)某一個(gè)指定的對(duì)話框。
---- 方法二:重載OnPaint(),即WM_PAINT消息。有關(guān)代碼如下(以上例工程為準(zhǔn)):
void CExampleDlgDlg::OnPaint()
{
    if (IsIconic())
...
  else
  {
        CRect rect;
        CPaintDC dc(this);
        GetClientRect(rect);
        dc.FillSolidRect(rect,RGB(0,255,0));  //設(shè)置為綠色背景
        CDialog::OnPaint();
  }
---- 方法三:重載OnCtlColor (CDC* pDC, CWnd* pWnd, UINT nCtlColor),即WM_CTLCOLOR消息。具體步驟如下(以上例工程為準(zhǔn)):
---- ①在CExampleDlgDlg的頭文件中,添加一CBrush的成員變量:
class CExampleDlgDlg : public CDialog
{
...
protected:
CBrush m_brush;
...
};
---- ②在OnInitDialog()函數(shù)中添加如下代碼:
BOOL CExampleDlgDlg::OnInitDialog()
{
...
// TODO: Add extra initialization here
m_brush.CreateSolidBrush(RGB(0, 255, 0)); // 生成一綠色刷子
...
}
---- ③利用ClassWizard重載OnCtlColor(...),即WM_CTLCOLOR消息:
HBRUSH CExampleDlgDlg::OnCtlColor
(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
/*
** 這里不必編寫(xiě)任何代碼!
**下行代碼要注釋掉
** HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
*/
return m_brush;  //返加綠色刷子
}
---- 方法四:還是重載OnCtlColor (CDC* pDC, CWnd* pWnd, UINT nCtlColor),即WM_CTLCOLOR消息。具體步驟如下(以上例工程為準(zhǔn)):
---- 步驟①、②同上方法三中的步驟①、②。
---- 步驟③利用ClassWizard重載OnCtlColor(...)(即WM_CTLCOLOR消息)時(shí)則有些不同:
HBRUSH CExampleDlgDlg::OnCtlColor
(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
//在這加一條是否為對(duì)話框的判斷語(yǔ)句
if(nCtlColor ==CTLCOLOR_DLG)
return m_brush;  //返加綠色刷子
return hbr;
}
---- 編譯并運(yùn)行即可。
posted @ 2008-11-05 16:01 wrh 閱讀(1701) | 評(píng)論 (0)編輯 收藏

  1. MFC和Win32

     

    1. MFC Object和Windows Object的關(guān)系

       

MFC中最重要的封裝是對(duì)Win32 API的封裝,因此,理解Windows Object和MFC Object (C++對(duì)象,一個(gè)C++類(lèi)的實(shí)例)之間的關(guān)系是理解MFC的關(guān)鍵之一。所謂Windows Object(Windows對(duì)象)是Win32下用句柄表示的Windows操作系統(tǒng)對(duì)象;所謂MFC Object (MFC對(duì)象)是C++對(duì)象,是一個(gè)C++類(lèi)的實(shí)例,這里(本書(shū)范圍內(nèi))MFC Object是有特定含義的,指封裝Windows Object的C++ Object,并非指任意的C++ Object。

MFC Object 和Windows Object是不一樣的,但兩者緊密聯(lián)系。以窗口對(duì)象為例:

一個(gè)MFC窗口對(duì)象是一個(gè)C++ CWnd類(lèi)(或派生類(lèi))的實(shí)例,是程序直接創(chuàng)建的。在程序執(zhí)行中它隨著窗口類(lèi)構(gòu)造函數(shù)的調(diào)用而生成,隨著析構(gòu)函數(shù)的調(diào)用而消失。而Windows窗口則是Windows系統(tǒng)的一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)的實(shí)例,由一個(gè)“窗口句柄”標(biāo)識(shí),Windows系統(tǒng)創(chuàng)建它并給它分配系統(tǒng)資源。Windows窗口在MFC窗口對(duì)象創(chuàng)建之后,由CWnd類(lèi)的Create成員函數(shù)創(chuàng)建,“窗口句柄”保存在窗口對(duì)象的m_hWnd成員變量中。Windows窗口可以被一個(gè)程序銷(xiāo)毀,也可以被用戶的動(dòng)作銷(xiāo)毀。MFC窗口對(duì)象和Windows窗口對(duì)象的關(guān)系如圖2-1所示。其他的Windows Object和對(duì)應(yīng)的MFC Object也有類(lèi)似的關(guān)系。

下面,對(duì)MFC Object和Windows Object作一個(gè)比較。有些論斷對(duì)設(shè)備描述表(MFC類(lèi)是CDC,句柄是HDC)可能不適用,但具體涉及到時(shí)會(huì)指出。

  1. 從數(shù)據(jù)結(jié)構(gòu)上比較

     

    MFC Object是相應(yīng)C++類(lèi)的實(shí)例,這些類(lèi)是MFC或者程序員定義的;

    Windows Object是Windows系統(tǒng)的內(nèi)部結(jié)構(gòu),通過(guò)一個(gè)句柄來(lái)引用;

    MFC給這些類(lèi)定義了一個(gè)成員變量來(lái)保存MFC Object對(duì)應(yīng)的Windows Object的句柄。對(duì)于設(shè)備描述表CDC類(lèi),將保存兩個(gè)HDC句柄。

  2. 從層次上講比較

     

    MFC Object是高層的,Windows Object是低層的;

    MFC Object封裝了Windows Object的大部分或全部功能,MFC Object的使用者不需要直接應(yīng)用Windows Object的HANDLE(句柄)使用Win32 API,代替它的是引用相應(yīng)的MFC Object的成員函數(shù)。

  3. 從創(chuàng)建上比較

     

    MFC Object通過(guò)構(gòu)造函數(shù)由程序直接創(chuàng)建;Windows Object由相應(yīng)的SDK函數(shù)創(chuàng)建。

    MFC中,使用這些MFC Object,一般分兩步:

    首先,創(chuàng)建一個(gè)MFC Object,或者在STACK中創(chuàng)建,或者在HEAP中創(chuàng)建,這時(shí),MFC Object的句柄實(shí)例變量為空,或者說(shuō)不是一個(gè)有效的句柄。

    然后,調(diào)用MFC Object的成員函數(shù)創(chuàng)建相應(yīng)的Windows Object,MFC的句柄變量存儲(chǔ)一個(gè)有效句柄。

    CDC(設(shè)備描述表類(lèi))的創(chuàng)建有所不同,在后面的2.3節(jié)會(huì)具體說(shuō)明CDC及其派生類(lèi)的創(chuàng)建和使用。

    當(dāng)然,可以在MFC Object的構(gòu)造函數(shù)中創(chuàng)建相應(yīng)的Windows對(duì)象,MFC的GDI類(lèi)就是如此實(shí)現(xiàn)的,但從實(shí)質(zhì)上講,MFC Object的創(chuàng)建和Windows Object的創(chuàng)建是兩回事。

  4. 從轉(zhuǎn)換上比較

     

    可以從一個(gè)MFC Object得到對(duì)應(yīng)的Windows Object的句柄;一般使用MFC Object的成員函數(shù)GetSafeHandle得到對(duì)應(yīng)的句柄。

    可以從一個(gè)已存在的Windows Object創(chuàng)建一個(gè)對(duì)應(yīng)的MFC Object; 一般使用MFC Object的成員函數(shù)Attach或者FromHandle來(lái)創(chuàng)建,前者得到一個(gè)永久性對(duì)象,后者得到的可能是一個(gè)臨時(shí)對(duì)象。

  5. 從使用范圍上比較

     

    MFC Object對(duì)系統(tǒng)的其他進(jìn)程來(lái)說(shuō)是不可見(jiàn)、不可用的;而Windows Object一旦創(chuàng)建,其句柄是整個(gè)Windows系統(tǒng)全局的。一些句柄可以被其他進(jìn)程使用。典型地,一個(gè)進(jìn)程可以獲得另一進(jìn)程的窗口句柄,并給該窗口發(fā)送消息。

    對(duì)同一個(gè)進(jìn)程的線程來(lái)說(shuō),只可以使用本線程創(chuàng)建的MFC Object,不能使用其他線程的MFC Object。

  6. 從銷(xiāo)毀上比較

     

MFC Object隨著析構(gòu)函數(shù)的調(diào)用而消失;但Windows Object必須由相應(yīng)的Windows系統(tǒng)函數(shù)銷(xiāo)毀。

設(shè)備描述表CDC類(lèi)的對(duì)象有所不同,它對(duì)應(yīng)的HDC句柄對(duì)象可能不是被銷(xiāo)毀,而是被釋放。

當(dāng)然,可以在MFC Object的析構(gòu)函數(shù)中完成Windows Object的銷(xiāo)毀,MFC Object的GDI類(lèi)等就是如此實(shí)現(xiàn)的,但是,應(yīng)該看到:兩者的銷(xiāo)毀是不同的。

每類(lèi)Windows Object都有對(duì)應(yīng)的MFC Object,下面用表格的形式列出它們之間的對(duì)應(yīng)關(guān)系,如表2-1所示:

表2-1 MFC Object和Windows Object的對(duì)應(yīng)關(guān)系

描述

Windows句柄

MFC Object

窗口

HWND

CWnd and CWnd-derived classes

設(shè)備上下文

HDC

CDC and CDC-derived classes

菜單

HMENU

CMenu

HPEN

CGdiObject類(lèi),CPen和CPen-derived classes

刷子

HBRUSH

CGdiObject類(lèi),CBrush和CBrush-derived classes

字體

HFONT

CGdiObject類(lèi),CFont和CFont-derived classes

位圖

HBITMAP

CGdiObject類(lèi),CBitmap和CBitmap-derived classes

調(diào)色板

HPALETTE

CGdiObject類(lèi),CPalette和CPalette-derived classes

區(qū)域

HRGN

CGdiObject類(lèi),CRgn和CRgn-derived classes

圖像列表

HimageLIST

CimageList和CimageList-derived classes

套接字

SOCKET

CSocket,CAsynSocket及其派生類(lèi)

 

 


表2-1中的OBJECT分以下幾類(lèi):

 

Windows對(duì)象,

設(shè)備上下文對(duì)象,

GDI對(duì)象(BITMAP,BRUSH,F(xiàn)ONT,PALETTE,PEN,RGN),

菜單,

圖像列表,

網(wǎng)絡(luò)套接字接口。

從廣義上來(lái)看,文檔對(duì)象和文件可以看作一對(duì)MFC Object和Windows Object,分別用CDocument類(lèi)和文件句柄描述。

后續(xù)幾節(jié)分別對(duì)前四類(lèi)作一個(gè)簡(jiǎn)明扼要的論述。

    1. Windows Object

       

      用SDK的Win32 API編寫(xiě)各種Windows應(yīng)用程序,有其共同的規(guī)律:首先是編寫(xiě)WinMain函數(shù),編寫(xiě)處理消息和事件的窗口過(guò)程WndProc,在WinMain里頭注冊(cè)窗口(Register Window),創(chuàng)建窗口,然后開(kāi)始應(yīng)用程序的消息循環(huán)。

      MFC應(yīng)用程序也不例外,因?yàn)镸FC是一個(gè)建立在SDK API基礎(chǔ)上的編程框架。對(duì)程序員來(lái)說(shuō)所不同的是:一般情況下,MFC框架自動(dòng)完成了Windows登記、創(chuàng)建等工作。

      下面,簡(jiǎn)要介紹MFC Window對(duì)Windows Window的封裝。

      1. Windows的注冊(cè)

         

一個(gè)應(yīng)用程序在創(chuàng)建某個(gè)類(lèi)型的窗口前,必須首先注冊(cè)該“窗口類(lèi)”(Windows Class)。注意,這里不是C++類(lèi)的類(lèi)。Register Window把窗口過(guò)程、窗口類(lèi)型以及其他類(lèi)型信息和要登記的窗口類(lèi)關(guān)聯(lián)起來(lái)。

  1. “窗口類(lèi)”的數(shù)據(jù)結(jié)構(gòu)

     

    “窗口類(lèi)”是Windows系統(tǒng)的數(shù)據(jù)結(jié)構(gòu),可以把它理解為Windows系統(tǒng)的類(lèi)型定義,而Windows窗口則是相應(yīng)“窗口類(lèi)”的實(shí)例。Windows使用一個(gè)結(jié)構(gòu)來(lái)描述“窗口類(lèi)”,其定義如下:

    typedef struct _WNDCLASSEX {

    UINT cbSize; //該結(jié)構(gòu)的字節(jié)數(shù)

    UINT style; //窗口類(lèi)的風(fēng)格

    WNDPROC lpfnWndProc; //窗口過(guò)程

    int cbClsExtra;

    int cbWndExtra;

    HANDLE hInstance; //該窗口類(lèi)的窗口過(guò)程所屬的應(yīng)用實(shí)例

    HICON hIcon; //該窗口類(lèi)所用的像標(biāo)

    HCURSOR hCursor; //該窗口類(lèi)所用的光標(biāo)

    HBRUSH hbrBackground; //該窗口類(lèi)所用的背景刷

    LPCTSTR lpszMenuName; //該窗口類(lèi)所用的菜單資源

    LPCTSTR lpszClassName; //該窗口類(lèi)的名稱(chēng)

    HICON hIconSm; //該窗口類(lèi)所用的小像標(biāo)

    } WNDCLASSEX;

    從“窗口類(lèi)”的定義可以看出,它包含了一個(gè)窗口的重要信息,如窗口風(fēng)格、窗口過(guò)程、顯示和繪制窗口所需要的信息,等等。關(guān)于窗口過(guò)程,將在后面消息映射等有關(guān)章節(jié)作詳細(xì)論述。

    Windows系統(tǒng)在初始化時(shí),會(huì)注冊(cè)(Register)一些全局的“窗口類(lèi)”,例如通用控制窗口類(lèi)。應(yīng)用程序在創(chuàng)建自己的窗口時(shí),首先必須注冊(cè)自己的窗口類(lèi)。在MFC環(huán)境下,有幾種方法可以用來(lái)注冊(cè)“窗口類(lèi)”,下面分別予以討論。

  2. 調(diào)用AfxRegisterClass注冊(cè)

     

    AfxRegisterClass函數(shù)是MFC全局函數(shù)。AfxRegisterClass的函數(shù)原型:

    BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);

    參數(shù)lpWndClass是指向WNDCLASS結(jié)構(gòu)的指針,表示一個(gè)“窗口類(lèi)”。

    首先,AfxRegisterClass檢查希望注冊(cè)的“窗口類(lèi)”是否已經(jīng)注冊(cè),如果是則表示已注冊(cè),返回TRUE,否則,繼續(xù)處理。

    接著,調(diào)用::RegisterClass(lpWndClass)注冊(cè)窗口類(lèi);

    然后,如果當(dāng)前模塊是DLL模塊,則把注冊(cè)“窗口類(lèi)”的名字加入到模塊狀態(tài)的域m_szUnregisterList中。該域是一個(gè)固定長(zhǎng)度的緩沖區(qū),依次存放模塊注冊(cè)的“窗口類(lèi)”的名字(每個(gè)名字是以“\n\0”結(jié)尾的字符串)。之所以這樣做,是為了DLL退出時(shí)能自動(dòng)取消(Unregister)它注冊(cè)的窗口類(lèi)。至于模塊狀態(tài)將在后面第9章詳細(xì)的討論。

    最后,返回TRUE表示成功注冊(cè)。

  3. 調(diào)用AfxRegisterWndClass注冊(cè)

     

    AfxRegisterWndClass函數(shù)也是MFC全局函數(shù)。AfxRegisterWndClass的函數(shù)原型:

    LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,

    HCURSOR hCursor, HBRUSH hbrBackground, HICON hIcon)

    參數(shù)1指定窗口類(lèi)風(fēng)格;

    參數(shù)2、3、4分別指定該窗口類(lèi)使用的光標(biāo)、背景刷、像標(biāo)的句柄,缺省值是0。

    此函數(shù)根據(jù)窗口類(lèi)屬性動(dòng)態(tài)地產(chǎn)生窗口類(lèi)的名字,然后,判斷是否該類(lèi)已經(jīng)注冊(cè),是則返回窗口類(lèi)名;否則用指定窗口類(lèi)的屬性(窗口過(guò)程指定為缺省窗口過(guò)程),調(diào)用AfxRegisterCalss注冊(cè)窗口類(lèi),返回類(lèi)名。

    動(dòng)態(tài)產(chǎn)生的窗口類(lèi)名字由以下幾部分組成(包括冒號(hào)分隔符):

    如果參數(shù)2、3、4全部為NULL,則由三部分組成。

    “Afx”+“:”+模塊實(shí)例句柄”+“:”+“窗口類(lèi)風(fēng)格”

    否則,由六部分組成:

    “Afx”+“:”+模塊實(shí)例句柄+“:”+“窗口類(lèi)風(fēng)格”+“:”+光標(biāo)句柄+“:”+背景刷句柄+“:”+像標(biāo)句柄。比如:“Afx:400000:b:13de:6:32cf”。

    該函數(shù)在MFC注冊(cè)主邊框或者文檔邊框“窗口類(lèi)”時(shí)被調(diào)用。具體怎樣用在5.3.3.3節(jié)會(huì)指出。

  4. 隱含的使用MFC預(yù)定義的的窗口類(lèi)

     

    MFC4.0以前的版本提供了一些預(yù)定義的窗口類(lèi),4.0以后不再預(yù)定義這些窗口類(lèi)。但是,MFC仍然沿用了這些窗口類(lèi),例如:

    用于子窗口的“AfxWnd”;

    用于邊框窗口(SDI主窗口或MDI子窗口)或視的“AfxFrameOrView”;

    用于MDI主窗口的“AfxMDIFrame”;

    用于標(biāo)準(zhǔn)控制條的“AfxControlBar”。

    這些類(lèi)的名字就 是“AfxWnd”、“AfxFrameOrView”、“AfxMdiFrame”、 “AfxControlBar”加上前綴和后綴(用來(lái)標(biāo)識(shí)版本號(hào)或是否調(diào)試版等)。它們使用標(biāo)準(zhǔn)應(yīng)用程序像標(biāo)、標(biāo)準(zhǔn)文檔像標(biāo)、標(biāo)準(zhǔn)光標(biāo)等標(biāo)準(zhǔn)資源。為了使用這些“窗口類(lèi)”,MFC會(huì)在適當(dāng)?shù)臅r(shí)候注冊(cè)這些類(lèi):或者要?jiǎng)?chuàng)建該類(lèi)的窗口時(shí),或者創(chuàng)建應(yīng)用程序的主窗口時(shí),等等。

    MFC內(nèi)部使用了函數(shù)

    BOOL AFXAPI AfxEndDeferRegisterClass(short fClass)

    來(lái)幫助注冊(cè)上述原MFC版本的預(yù)定義“窗口類(lèi)”。參數(shù)fClass區(qū)分了那些預(yù)定義窗口的類(lèi)型。根據(jù)不同的類(lèi)型,使用不同的窗口類(lèi)風(fēng)格、窗口類(lèi)名字等填充WndClass的域,然后調(diào)用AfxRegisterClass注冊(cè)窗口類(lèi)。并且注冊(cè)成功之后,通過(guò)模塊狀態(tài)的m_fRegisteredClasses記錄該窗口類(lèi)已經(jīng)注冊(cè),這樣該模塊在再次需要注冊(cè)這些窗口類(lèi)之前可以查一下m_fRegisteredClasses,如果已經(jīng)注冊(cè)就不必浪費(fèi)時(shí)間了。為此,MFC內(nèi)部使用宏

    AfxDeferRegisterClass(short fClass)

    來(lái)注冊(cè)“窗口類(lèi)”,如果m_fRegisteredClasses記錄了注冊(cè)的窗口類(lèi),返回TRUE,否則,調(diào)用AfxEndDeferRegisterClass注冊(cè)。

    注冊(cè)這些窗口類(lèi)的例子:

    MFC在加載邊框窗口時(shí),會(huì)自動(dòng)地注冊(cè)“AfxFrameOrView”窗口類(lèi)。在創(chuàng)建視時(shí),就會(huì)使用該“窗口類(lèi)”創(chuàng)建視窗口。當(dāng)然,如果創(chuàng)建視窗口時(shí),該“窗口類(lèi)”還沒(méi)有注冊(cè),MFC將先注冊(cè)它然后使用它創(chuàng)建視窗口。

    不過(guò),MFC并不使用”AfxMDIFrame”來(lái)創(chuàng)建MDI主窗口,因?yàn)樵诩虞d主窗口時(shí)一般都指定了主窗口的資源,MFC使用指定的像標(biāo)注冊(cè)新的MDI主窗口類(lèi)(通過(guò)函數(shù)AfxRegisterWndClass完成,因此“窗口類(lèi)”的名字是動(dòng)態(tài)產(chǎn)生的)。

    MDI子窗口類(lèi)似于上述MDI主窗口的處理。

    在MFC創(chuàng)建控制窗口時(shí),如工具欄窗口,如果“AfxControlBar”類(lèi)還沒(méi)有注冊(cè),則注冊(cè)它。注冊(cè)過(guò)程很簡(jiǎn)單,就是調(diào)用::InitCommonControl加載通用控制動(dòng)態(tài)連接庫(kù)。

  5. 調(diào)用::RegisterWndClass。

     

    直接調(diào)用Win32的窗口注冊(cè)函數(shù)::RegisterWndClass注冊(cè)“窗口類(lèi)”,這樣做有一個(gè)缺點(diǎn):如果是DLL模塊,這樣注冊(cè)的“窗口類(lèi)”在程序退出時(shí)不會(huì)自動(dòng)的被取消注冊(cè)(Unregister)。所以必須記得在DLL模塊退出時(shí)取消它所注冊(cè)的窗口類(lèi)。

  6. 子類(lèi)化

     

子類(lèi)化(Subclass)一個(gè)“窗口類(lèi)”,可自動(dòng)地得到它的“窗口類(lèi)”屬性。

      1. MFC窗口類(lèi)CWnd

         

在Windows系統(tǒng)里,一個(gè)窗口的屬性分兩個(gè)地方存放:一部分放在“窗口類(lèi)”里頭,如上所述的在注冊(cè)窗口時(shí)指定;另一部分放在Windows Object本身,如:窗口的尺寸,窗口的位置(X,Y軸),窗口的Z軸順序,窗口的狀態(tài)(ACTIVE,MINIMIZED,MAXMIZED,RESTORED…),和其他窗口的關(guān)系(父窗口,子窗口…),窗口是否可以接收鍵盤(pán)或鼠標(biāo)消息,等等。

為了表達(dá)所有這些窗口的共性,MFC設(shè)計(jì)了一個(gè)窗口基類(lèi)CWnd。有一點(diǎn)非常重要,那就是CWnd提供了一個(gè)標(biāo)準(zhǔn)而通用的MFC窗口過(guò)程,MFC下所有的窗口都使用這個(gè)窗口過(guò)程。至于通用的窗口過(guò)程卻能為各個(gè)窗口實(shí)現(xiàn)不同的操作,那就是MFC消息映射機(jī)制的奧秘和作用了。這些,將在后面有關(guān)章節(jié)詳細(xì)論述。

CWnd提供了一系列成員函數(shù),或者是對(duì)Win32相關(guān)函數(shù)的封裝,或者是CWnd新設(shè)計(jì)的一些函數(shù)。這些函數(shù)大致如下。

(1)窗口創(chuàng)建函數(shù)

這里主要討論函數(shù)Create和CreateEx。它們封裝了Win32窗口創(chuàng)建函數(shù)::CreateWindowEx。Create的原型如下:

BOOL CWnd::Create(LPCTSTR lpszClassName,

LPCTSTR lpszWindowName, DWORD dwStyle,

const RECT& rect,

CWnd* pParentWnd, UINT nID,

CCreateContext* pContext)

Create是一個(gè)虛擬函數(shù),用來(lái)創(chuàng)建子窗口(不能創(chuàng)建桌面窗口和POP UP窗口)。CWnd的基類(lèi)可以覆蓋該函數(shù),例如邊框窗口類(lèi)等覆蓋了該函數(shù)以實(shí)現(xiàn)邊框窗口的創(chuàng)建,視類(lèi)則使用它來(lái)創(chuàng)建視窗口。

Create調(diào)用了成員函數(shù)CreateEx。CWnd::CreateEx的原型如下:

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)

CreateEx有11個(gè)參數(shù),它將調(diào)用::CreateWindowEx完成窗口的創(chuàng)建,這11個(gè)參數(shù)對(duì)應(yīng)地傳遞給::CreateWindowEx。參數(shù)指定了窗口擴(kuò)展風(fēng)格、“窗口類(lèi)”、窗口名、窗口大小和位置、父窗口句柄、窗口菜單和窗口創(chuàng)建參數(shù)。

CreateEx的處理流程將在后面4.4.1節(jié)討論窗口過(guò)程時(shí)分析。

窗口創(chuàng)建時(shí)發(fā)送WM_CREATE消息,消息參數(shù)lParam指向一個(gè)CreateStruct結(jié)構(gòu)的變量,該結(jié)構(gòu)有11個(gè)域,其描述見(jiàn)后面4.4.1節(jié)對(duì)窗口過(guò)程的分析,Windows使用和CreateEx參數(shù)一樣的內(nèi)容填充該變量。

(2)窗口銷(xiāo)毀函數(shù)

例如:

DestroyWindow函數(shù) 銷(xiāo)毀窗口

PostNcDestroy( ),銷(xiāo)毀窗口后調(diào)用,虛擬函數(shù)

(3)用于設(shè)定、獲取、改變窗口屬性的函數(shù),例如:

SetWindowText(CString tiltle) 設(shè)置窗口標(biāo)題

GetWindowText() 得到窗口標(biāo)題

SetIcon(HICON hIcon, BOOL bBigIcon);設(shè)置窗口像標(biāo)

GetIcon( BOOL bBigIcon ) ;得到窗口像標(biāo)

GetDlgItem( int nID);得到窗口類(lèi)指定ID的控制子窗口

GetDC(); 得到窗口的設(shè)備上下文

SetMenu(CMenu *pMenu); 設(shè)置窗口菜單

GetMenu();得到窗口菜單

(4)用于完成窗口動(dòng)作的函數(shù)

用于更新窗口,滾動(dòng)窗口,等等。一部分成員函數(shù)設(shè)計(jì)成或可重載(Overloaded)函數(shù),或虛擬(Overridden)函數(shù),或MFC消息處理函數(shù)。這些函數(shù)或者實(shí)現(xiàn)了一部分功能,或者僅僅是一個(gè)空函數(shù)。如:

  • 有關(guān)消息發(fā)送的函數(shù):

     

SendMessage( UINT message,WPARAM wParam = 0, LPARAM lParam = 0 );

給窗口發(fā)送發(fā)送消息,立即調(diào)用方式

PostMessage(( UINT message,WPARAM wParam = 0, LPARAM lParam = 0 );

給窗口發(fā)送消息,放進(jìn)消息隊(duì)列

  • 有關(guān)改變窗口狀態(tài)的函數(shù)

     

MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );

移動(dòng)窗口到指定位置

ShowWindow(BOOL );顯示窗口,使之可見(jiàn)或不可見(jiàn)

….

  • 實(shí)現(xiàn)MFC消息處理機(jī)制的函數(shù):

     

virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam ); 窗口過(guò)程,虛擬函數(shù)

virtual BOOL OnCommand( WPARAM wParam, LPARAM lParam );處理命令消息

  • 消息處理函數(shù):

     

OnCreate( LPCREATESTRUCT lpCreateStruct );MFC窗口消息處理函數(shù),窗口創(chuàng)建時(shí)由MFC框架調(diào)用

OnClose();MFC窗口消息處理函數(shù),窗口創(chuàng)建時(shí)由MFC框架調(diào)用

  • 其他功能的函數(shù)

     

CWnd的導(dǎo)出類(lèi)是類(lèi)型更具體、功能更完善的窗口類(lèi),它們繼承了CWnd的屬性和方法,并提供了新的成員函數(shù)(消息處理函數(shù)、虛擬函數(shù)、等等)。

常用的窗口類(lèi)及其層次關(guān)系見(jiàn)圖1-1。

      1. 在MFC下創(chuàng)建一個(gè)窗口對(duì)象

         

MFC下創(chuàng)建一個(gè)窗口對(duì)象分兩步,首先創(chuàng)建MFC窗口對(duì)象,然后創(chuàng)建對(duì)應(yīng)的Windows窗口。在內(nèi)存使用上,MFC窗口對(duì)象可以在棧或者堆(使用new創(chuàng)建)中創(chuàng)建。具體表述如下:

  • 創(chuàng)建MFC窗口對(duì)象。通過(guò)定義一個(gè)CWnd或其派生類(lèi)的實(shí)例變量或者動(dòng)態(tài)創(chuàng)建一個(gè)MFC窗口的實(shí)例,前者在棧空間創(chuàng)建一個(gè)MFC窗口對(duì)象,后者在堆空間創(chuàng)建一個(gè)MFC窗口對(duì)象。

     

  • 調(diào)用相應(yīng)的窗口創(chuàng)建函數(shù),創(chuàng)建Windows窗口對(duì)象。

     

例如:在前面提到的AppWizard產(chǎn)生的源碼中,有CMainFrame(派生于CMDIFrame(SDI)或者CMDIFrameWnd(MDI))類(lèi)。它有兩個(gè)成員變量定義如下:

CToolBar m_wndToolBar;

CStatusBar m_wndStatusBar;

當(dāng)創(chuàng)建CMainFrame類(lèi)對(duì)象時(shí),上面兩個(gè)MFC Object也被構(gòu)造。

CMainFrame還有一個(gè)成員函數(shù)

OnCreate(LPCREATESTRUCT lpCreateStruct),

它的實(shí)現(xiàn)包含如下一段代碼,調(diào)用CToolBar和CStatusBar的成員函數(shù)Create來(lái)創(chuàng)建上述兩個(gè)MFC對(duì)象對(duì)應(yīng)的工具欄HWND窗口和狀態(tài)欄HWND窗口:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (!m_wndToolBar.Create(this) ||

!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

{

TRACE0("Failed to create toolbar\n");

return -1; // fail to create

}

if (!m_wndStatusBar.Create(this) ||

!m_wndStatusBar.SetIndicators(indicators,

sizeof(indicators)/sizeof(UINT)))

{

TRACE0("Failed to create status bar\n");

return -1; // fail to create

}

}

關(guān)于工具欄、狀態(tài)欄將在后續(xù)有關(guān)章節(jié)作詳細(xì)討論。

在MFC中,還提供了一種動(dòng)態(tài)創(chuàng)建技術(shù)。動(dòng)態(tài)創(chuàng)建的過(guò)程實(shí)際上也如上所述分兩步,只不過(guò)MFC使用這個(gè)技術(shù)是由框架自動(dòng)地完成整個(gè)過(guò)程的。通常框架窗口、文檔框架窗口、視使用了動(dòng)態(tài)創(chuàng)建。介于MFC的結(jié)構(gòu),CFrameWnd和CView及其派生類(lèi)的實(shí)例即使不使用動(dòng)態(tài)創(chuàng)建,也要用new在堆中分配。理由見(jiàn)窗口的銷(xiāo)毀(2.2.5節(jié))。

至于動(dòng)態(tài)創(chuàng)建技術(shù),將在下一章具體討論。

在Windows窗口的創(chuàng)建過(guò)程中,將發(fā)送一些消息,如:

在創(chuàng)建了窗口的非客戶區(qū)(Nonclient area)之后,發(fā)送消息WM_NCCREATE;

在創(chuàng)建了窗口的客戶區(qū)(client area)之后,發(fā)送消息WM_CREATE;

窗口的窗口過(guò)程在窗口顯示之前收到這兩個(gè)消息。

如果是子窗口,在發(fā)送了上述兩個(gè)消息之后,還給父窗口發(fā)送WM_PARENATNOTIFY消息。其他類(lèi)或風(fēng)格的窗口可能發(fā)送更多的消息,具體參見(jiàn)SDK開(kāi)發(fā)文檔。

      1. MFC窗口的使用

         

        MFC提供了大量的窗口類(lèi),其功能和用途各異。程序員應(yīng)該選擇哪些類(lèi)來(lái)使用,以及怎么使用他們呢?

        直接使用MFC提供的窗口類(lèi)或者先從MFC窗口類(lèi)派生一個(gè)新的C++類(lèi)然后使用它,這些在通常情況下都不需要程序員提供窗口注冊(cè)的代碼。是否需要派生新的C++類(lèi),視MFC已有的窗口類(lèi)是否能滿足使用要求而定。派生的C++類(lèi)繼承了基類(lèi)的特性并改變或擴(kuò)展了它的功能,例如增加或者改變對(duì)消息、事件的特殊處理等。

        主要使用或繼承以下一些MFC窗口類(lèi)(其層次關(guān)系圖見(jiàn)圖1-1):

        框架類(lèi)CFrameWnd,CMdiFrameWnd;

        文檔框架CMdiChildWnd;

        視圖CView和CView派生的有特殊功能的視圖如:列表CListView,編輯CEditView,樹(shù)形列表CTreeView,支持RTF的CRichEditView,基于對(duì)話框的視CFormView等等。

        對(duì)話框CDialog。

        通常,都要從這些類(lèi)派生應(yīng)用程序的框架窗口和視窗口或者對(duì)話框。

        工具條CToolBar

        狀態(tài)條CStatusBar

        其他各類(lèi)控制窗口,如列表框CList,編輯框CEdit,組合框CComboBox,按鈕Cbutton等。

        通常,直接使用這些類(lèi)。

      2. 在MFC下窗口的銷(xiāo)毀

         

窗口對(duì)象使用完畢,應(yīng)該銷(xiāo)毀。在MFC下,一個(gè)窗口對(duì)象的銷(xiāo)毀包括HWND窗口對(duì)象的銷(xiāo)毀和MFC窗口對(duì)象的銷(xiāo)毀。一般情況下,MFC編程框架自動(dòng)地處理了這些。

(1)對(duì)CFrameWnd和CView的派生類(lèi)

這些窗口的關(guān)閉導(dǎo)致銷(xiāo)毀窗口的函數(shù)DestroyWindow被調(diào)用。銷(xiāo)毀Windows窗口時(shí),MFC框架調(diào)用的最后一個(gè)成員函數(shù)是OnNcDestroy函數(shù),該函數(shù)負(fù)責(zé)Windows清理工作,并在最后調(diào)用虛擬成員函數(shù)PostNcDestroy。CFrameWnd和CView的PostNcDestroy調(diào)用delete this刪除自身這個(gè)MFC窗口對(duì)象。

所以,對(duì)這些窗口,如前所述,應(yīng)在堆(Heap)中分配,而且,不要對(duì)這些對(duì)象使用delete操作。

(2)對(duì)Windows Control窗口

在它們的析構(gòu)函數(shù)中,將調(diào)用DestroyWidnow來(lái)銷(xiāo)毀窗口。如果在棧中分配這樣的窗口對(duì)象,則在超出作用范圍的時(shí)候,隨著析構(gòu)函數(shù)的調(diào)用,MFC窗口對(duì)象和它的Windows window對(duì)象都被銷(xiāo)毀。如果在堆(Heap)中分配,則顯式調(diào)用delete操作符,導(dǎo)致析構(gòu)函數(shù)的調(diào)用和窗口的銷(xiāo)毀。

所以,這種類(lèi)型的窗口應(yīng)盡可能在棧中分配,避免用額外的代碼來(lái)銷(xiāo)毀窗口。如前所述的CMainFrame的成員變量m_wndStatusBar和m_wndToolBar就是這樣的例子。

(3)對(duì)于程序員直接從CWnd派生的窗口

程序員可以在派生類(lèi)中實(shí)現(xiàn)上述兩種機(jī)制之一,然后,在相應(yīng)的規(guī)范下使用。

后面章節(jié)將詳細(xì)的討論應(yīng)用程序退出時(shí)關(guān)閉、清理窗口的過(guò)程。

    1. 設(shè)備描述表

       

      1. 設(shè)備描述表概述

         

當(dāng)一個(gè)應(yīng)用程序使用GDI函數(shù)時(shí),必須先裝入特定的設(shè)備驅(qū)動(dòng)程序,然后為繪制窗口準(zhǔn)備設(shè)備描述表,比如指定線的寬度和顏色、刷子的樣式和顏色、字體、剪裁區(qū)域等等。不像其他Win32結(jié)構(gòu),設(shè)備描述表不能被直接訪問(wèn),只能通過(guò)系列Win32函數(shù)來(lái)間接地操作。

如同Windows“窗口類(lèi)”一樣,設(shè)備描述表也是一種Windows數(shù)據(jù)結(jié)構(gòu),用來(lái)描述繪制窗口所需要的信息。它定義了一個(gè)坐標(biāo)映射模式、一組GDI圖形對(duì)象及其屬性。這些GDI對(duì)象包括用于畫(huà)線的筆,繪圖、填圖的刷子,位圖,調(diào)色板,剪裁區(qū)域,及路徑(Path)。

表2-2列出了設(shè)備描述表的結(jié)構(gòu)和各項(xiàng)缺省值,表2-3列出了設(shè)備描述表的類(lèi)型,表2-4顯示設(shè)備描述表的類(lèi)型。

表2-2 設(shè)備描述表的結(jié)構(gòu)

屬性

缺省值

Background color

Background color setting from Windows Control Panel (typically, white)

Background mode

OPAQUE

Bitmap

None

Brush

WHITE_BRUSH

Brush origin

(0,0)

Clipping region

Entire window or client area with the update region clipped, as appropriate. Child and pop-up windows in the client area may also be clipped

Palette

DEFAULT_PALETTE

Current pen position

(0,0)

Device origin

Upper left corner of the window or the client area

Drawing mode

R2_COPYPEN

Font

SYSTEM_FONT (SYSTEM_FIXED_FONT for applications written to run with Windows versions 3.0 and earlier)

Intercharacter spacing

0

Mapping mode

MM_TEXT

Pen

BLACK_PEN

Polygon-fill mode

ALTERNATE

Stretch mode

BLACKONWHITE

Text color

Text color setting from Control Panel (typically, black)

Viewport extent

(1,1)

Viewport origin

(0,0)

Window extent

(1,1)

Window origin

(0,0)

 

表2-3 設(shè)備描述表的分類(lèi)

Display

顯示設(shè)備描述表,提供對(duì)視頻顯示設(shè)備上的繪制操作的支持

Printer

打印設(shè)備描述表,提供對(duì)打印機(jī)、繪圖儀設(shè)備上的繪制操作的支持

Memory

內(nèi)存設(shè)備描述表,提供對(duì)位圖操作的支持

Information

信息設(shè)備描述表,提供對(duì)操作設(shè)備信息獲取的支持

表2-3中的顯示設(shè)備描述表又分三種類(lèi)型,如表2-4所示。

表2-4 顯示設(shè)備描述表的分類(lèi)

名稱(chēng)

特點(diǎn)

功能

Class Device

Contexts

提供對(duì)Win16的向后兼容

 

Common

Device

Contexts

在Windows系統(tǒng)的高速緩沖區(qū),數(shù)量有限

Applicaion獲取設(shè)備描述表時(shí),Windows用缺省值初始化該設(shè)備描述表,Application使用它完成繪制操作,然后釋放

Private

Device

Contexts

沒(méi)有數(shù)量限制,用完不需釋放一次獲取,多次使用

多次使用過(guò)程中,每次設(shè)備描述表屬性的任何修改或變化都會(huì)被保存,以支持快速繪制

 

(1)使用設(shè)備描述表的步驟

要使用設(shè)備描述表,一般有如下步驟:

  • 獲取或者創(chuàng)建設(shè)備描述表;

     

  • 必要的話,改變?cè)O(shè)備描述表的屬性;

     

  • 使用設(shè)備描述表完成繪制操作;

     

  • 釋放或刪除設(shè)備描述表。

     

Common設(shè)備描述表通過(guò)::GetDC,::GetDCEx,::BeginPaint來(lái)獲得一個(gè)設(shè)備描述表,用畢,用::ReleaseDC或::EndPaint釋放設(shè)備描述表;

Printer設(shè)備描述表通過(guò)::CreateDC創(chuàng)建設(shè)備描述表,用::DeleteDC刪除設(shè)備描述表。

Memory設(shè)備描述表通過(guò)::CreateCompatibleDC創(chuàng)建設(shè)備描述表,用::DeleteDC刪除。

Information設(shè)備描述表通過(guò)::CreateIC創(chuàng)建設(shè)備描述表,用::DeleteDC刪除。

(2)改變?cè)O(shè)備描述表屬性的途徑

要改變?cè)O(shè)備描述表的屬性,可通過(guò)以下途徑:

用::SelectObject選入新的除調(diào)色板以外的GDI Object到設(shè)備描述表中;

對(duì)于調(diào)色板,使用::SelectPalette函數(shù)選入邏輯調(diào)色板,并使用::RealizePalette把邏輯調(diào)色板的入口映射到物理調(diào)色板中。

用其他API函數(shù)改變其他屬性,如::SetMapMode改變映射模式。

      1. 設(shè)備描述表在MFC中的實(shí)現(xiàn)

         

MFC提供了CDC類(lèi)作為設(shè)備描述表類(lèi)的基類(lèi),它封裝了Windows的HDC設(shè)備描述表對(duì)象和相關(guān)函數(shù)。

  1. CDC類(lèi)

     

    CDC類(lèi)包含了各種類(lèi)型的Windows設(shè)備描述表的全部功能,封裝了所有的Win32 GDI 函數(shù)和設(shè)備描述表相關(guān)的SDK函數(shù)。在MFC下,使用CDC的成員函數(shù)來(lái)完成所有的窗口繪制工作。

    CDC 類(lèi)的結(jié)構(gòu)示意圖2-2所示。

    CDC類(lèi)有兩個(gè)成員變量:m_hDC,m_hAttribDC,它們都是Windows設(shè)備描述表句柄。CDC的成員函數(shù)作輸出操作時(shí),使用m_Hdc;要獲取設(shè)備描述表的屬性時(shí),使用m_hAttribDC。

    在創(chuàng)建一個(gè)CDC類(lèi)實(shí)例時(shí),缺省的m_hDC等于m_hAttribDC。如果需要的話,程序員可以分別指定它們。例如,MFC框架實(shí)現(xiàn)CMetaFileDC類(lèi)時(shí),就是如此:CMetaFileDC從物理設(shè)備上讀取設(shè)備信息,輸出則送到元文件(metafile)上,所以m_hDC和m_hAttribDC是不同的,各司其責(zé)。還有一個(gè)類(lèi)似的例子:打印預(yù)覽的實(shí)現(xiàn),一個(gè)代表打印機(jī)模擬輸出,一個(gè)代表屏幕顯示。

    CDC封裝::SelectObject(HDC hdc,HGDIOBJECT hgdiobject)函數(shù)時(shí),采用了重載技術(shù),即它針對(duì)不同的GDI對(duì)象,提供了名同而參數(shù)不同的成員函數(shù):

    SelectObject(CPen *pen)用于選入筆;

    SelectObject(CBitmap* pBitmap)用于選入位圖;

    SelectObject(CRgn *pRgn)用于選入剪裁區(qū)域;

    SelectObject(CBrush *pBrush)用于選入刷子;

    SelectObject(CFont *pFont)用于選入字體;

    至于調(diào)色板,使用SelectPalette(CPalette *pPalette,BOOL bForceBackground )選入調(diào)色板到設(shè)備描述表,使用RealizePalletter()實(shí)現(xiàn)邏輯調(diào)色板到物理調(diào)色板的映射。

  2. 從CDC派生出功能更具體的設(shè)備描述表

     

從CDC 派生出四個(gè)功能更具體的設(shè)備描述表類(lèi)。層次如圖2-3所示。

下面,分別討論派生出的四種設(shè)備描述表。

  • CCientDC

     

代表窗口客戶區(qū)的設(shè)備描述表。其構(gòu)造函數(shù)CClientDC(CWnd *pWin)通過(guò)::GetDC獲取指定窗口的客戶區(qū)的設(shè)備描述表HDC,并且使用成員函數(shù)Attach把它和CClientDC對(duì)象捆綁在一起;其析構(gòu)函數(shù)使用成員函數(shù)Detach把設(shè)備描述表句柄HDC分離出來(lái),并調(diào)用::ReleaseDC釋放設(shè)備描述表HDC。

  • CPaintDC

     

僅僅用于響應(yīng)WM_PAINT消息時(shí)繪制窗口,因?yàn)樗臉?gòu)造函數(shù)調(diào)用了::BeginPaint獲取設(shè)備描述表HDC,并且使用成員函數(shù)Attach把它和CPaintDC對(duì)象捆綁在一起;析構(gòu)函數(shù)使用成員函數(shù)Detach把設(shè)備描述表句柄HDC分離出來(lái),并調(diào)用::EndPaint釋放設(shè)備描述表HDC,而::BeginPaint和::EndPaint僅僅在響應(yīng)WM_PAINT時(shí)使用。

  • CMetaFileDC

     

用于生成元文件。

  • CWindowDC

     

代表整個(gè)窗口區(qū)(包括非客戶區(qū))的設(shè)備描述表。其構(gòu)造函數(shù)CWindowDC(CWnd *pWin)通過(guò)::GetWindowDC獲取指定窗口的客戶區(qū)的設(shè)備描述表HDC,并使用Attach把它和CWindowDC對(duì)象捆綁在一起;其析構(gòu)函數(shù)使用Detach把設(shè)備描述表HDC分離出來(lái),調(diào)用::ReleaseDC釋放設(shè)備描述表HDC。

      1. MFC設(shè)備描述表類(lèi)的使用

         

  1. 使用CPaintDC、CClientDC、CWindowDC的方法

     

    首先,定義一個(gè)這些類(lèi)的實(shí)例變量,通常在棧中定義。然后,使用它。

    例如,MFC中CView對(duì)WM_PAINT消息的實(shí)現(xiàn)方法如下:

    void CView::OnPaint()

    {

    // standard paint routine

    CPaintDC dc(this);

    OnPrepareDC(&dc);

    OnDraw(&dc);

    }

    在棧中定義了CPaintDC類(lèi)型的變量dc,隨著構(gòu)造函數(shù)的調(diào)用獲取了設(shè)備描述表;設(shè)備描述表使用完畢,超出其有效范圍就被自動(dòng)地清除,隨著析構(gòu)函數(shù)的調(diào)用,其獲取的設(shè)備描述表被釋放。

    如果希望在堆中創(chuàng)建,例如

    CPaintDC *pDC;

    pDC = new CPaintDC(this)

    則在使用完畢時(shí),用delete刪除pDC:

    delete pDC;

  2. 直接使用CDC

     

需要注意的是:在生成CDC對(duì)象的時(shí)候,并不像它的派生類(lèi)那樣,在構(gòu)造函數(shù)里獲取相應(yīng)的Windows設(shè)備描述表。最好不要使用::GetDC等函數(shù)來(lái)獲取一個(gè)設(shè)備描述表,而是創(chuàng)建一個(gè)設(shè)備描述表。其構(gòu)造函數(shù)如下:

CDC::CDC()

{

m_hDC = NULL;

m_hAttribDC = NULL;

m_bPrinting = FALSE;

}

其析構(gòu)函數(shù)如下:

CDC::~CDC()

{

if (m_hDC != NULL)

::DeleteDC(Detach());

}

在CDC析構(gòu)函數(shù)中,如果設(shè)備描述表句柄不空,則調(diào)用DeleteDC刪除它。這是直接使用CDC時(shí)最好創(chuàng)建Windows設(shè)備描述表的理由。如果設(shè)備描述表不是創(chuàng)建的,則應(yīng)該在析構(gòu)函數(shù)被調(diào)用前分離出設(shè)備描述表句柄并用::RealeaseDC釋放它,釋放后m_hDC為空,則在析構(gòu)函數(shù)調(diào)用時(shí)不會(huì)執(zhí)行::DeleteDC。當(dāng)然,不用擔(dān)心CDC的派生類(lèi)的析構(gòu)函數(shù)調(diào)用CDC的析構(gòu)函數(shù),因?yàn)镃DC::~CDC()不是虛擬析構(gòu)函數(shù)。

直接使用CDC的例子是內(nèi)存設(shè)備上下文,例如:

CDC dcMem; //聲明一個(gè)CDC對(duì)象

dcMem.CreateCompatibleDC(&dc); //創(chuàng)建設(shè)備描述表

pbmOld = dcMem.SelectObject(&m_bmBall);//更改設(shè)備描述表屬性

…//作一些繪制操作

dcMem.SelectObject(pbmOld);//恢復(fù)設(shè)備描述表的屬性

dcMem.DeleteDC(); //可以不調(diào)用,而讓析構(gòu)函數(shù)去刪除設(shè)備描述表

    1. GDI對(duì)象

       

在討論設(shè)備描述表時(shí),已經(jīng)多次涉及到GDI對(duì)象。這里,需強(qiáng)調(diào)一下:GDI對(duì)象要選入Windows 設(shè)備描述表后才能使用;用畢,要恢復(fù)設(shè)備描述表的原GDI對(duì)象,并刪除該GDI對(duì)象。

一般按如下步驟使用GDI對(duì)象:

Create or get a GDI OBJECT hNewGdi;

hOldGdi = ::SelectObject(hdc, hNewGdi)

……

::SelectObject(hdc, hOldGdi)

::DeleteObject(hNewGdi)

先創(chuàng)建或得到一個(gè)GDI對(duì)象,然后把它選入設(shè)備描述表并保存它原來(lái)的GDI對(duì)象;用畢恢復(fù)設(shè)備描述表原來(lái)的GDI對(duì)象并刪除新創(chuàng)建的GDI對(duì)象。

需要指出的是,如果hNewGdi是一個(gè)Stock GDI對(duì)象,可以不刪除(刪除也可以)。通過(guò)

HGDIOBJ GetStockObject(

int fnObject // type of stock object

);

來(lái)獲取Stock GDI對(duì)象。

  1. MFC GDI對(duì)象

     

    MFC用一些類(lèi)封裝了Windows GDI對(duì)象和相關(guān)函數(shù),層次結(jié)構(gòu)如圖2-4所示:

    CGdiObject封裝了Windows GDI Object共有的特性。其派生類(lèi)在繼承的基礎(chǔ)上,主要封裝了各類(lèi)GDI的創(chuàng)建函數(shù)以及和具體GDI對(duì)象相關(guān)的操作。

    CGdiObject的構(gòu)造函數(shù)僅僅讓m_hObject為空。如果m_hObject不空,其析構(gòu)函數(shù)將刪除對(duì)應(yīng)的Windows GDI對(duì)象。MFC GDI對(duì)象和Windows GDI對(duì)象的關(guān)系如圖2-5所示。

  2. 使用MFC GDI類(lèi)的使用

     

首先創(chuàng)建GDI對(duì)象,可分一步或兩步創(chuàng)建。一步創(chuàng)建就是構(gòu)造MFC對(duì)象和Windows GDI對(duì)象一步完成;兩步創(chuàng)建則先構(gòu)造MFC對(duì)象,接著創(chuàng)建Windows GDI對(duì)象。然后,把新創(chuàng)建的GDI對(duì)象選進(jìn)設(shè)備描述表,取代原GDI對(duì)象并保存。最后,恢復(fù)原GDI對(duì)象。例如:

void CMyView::OnDraw(CDC *pDC)

{

CPen penBlack; //構(gòu)造MFC CPen對(duì)象

if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))

{

CPen *pOldPen = pDC->SelectObject(&penBlack)); //選進(jìn)設(shè)備表,保存原筆

pDC->SelectObject(pOldPen); //恢復(fù)原筆

}else

{

}

}

和在SDK下有一點(diǎn)不同的是:這里沒(méi)有DeleteObject。因?yàn)閳?zhí)行完OnDraw后,棧中的penBlack被銷(xiāo)毀,它的析構(gòu)函數(shù)被調(diào)用,導(dǎo)致DeleteObject的調(diào)用。

還有一點(diǎn)要說(shuō)明:

pDC->SelectObject(&penBlack)返回了一個(gè)CPen *指針,也就是說(shuō),它根據(jù)原來(lái)PEN的句柄創(chuàng)建了一個(gè)MFC CPen對(duì)象。這個(gè)對(duì)象是否需要?jiǎng)h除呢?不必要,因?yàn)樗且粋€(gè)臨時(shí)對(duì)象,MFC框架會(huì)自動(dòng)地刪除它。當(dāng)然,在本函數(shù)執(zhí)行完畢把控制權(quán)返回給主消息循環(huán)之前,該對(duì)象是有效的。

關(guān)于臨時(shí)對(duì)象及MFC處理它們的內(nèi)部機(jī)制,將在后續(xù)章節(jié)詳細(xì)討論。

至此,Windows編程的核心概念:窗口、GDI界面(設(shè)備描述表、GDI對(duì)象等)已經(jīng)陳述清楚,特別揭示了MFC對(duì)這些概念的封裝機(jī)制,并簡(jiǎn)明講述了與這些Windows Object對(duì)應(yīng)的MFC類(lèi)的使用方法。還有其他Windows概念,可以參見(jiàn)SDK開(kāi)發(fā)文檔。在MFC的實(shí)現(xiàn)上,基本上僅僅是對(duì)和這些概念相關(guān)的Win32函數(shù)的封裝。如果明白了MFC的窗口、GDI界面的封裝機(jī)制,其他就不難了。

posted @ 2008-11-01 15:42 wrh 閱讀(1346) | 評(píng)論 (0)編輯 收藏

系統(tǒng)理解Win32 API和MFC(下)
作者: 溫昱
作者主頁(yè): lcspace.diy.163.com

系統(tǒng)理解Win32 API和MFC(上)

二、MFC的概念模型

前面我們研究了WIN32 API的“領(lǐng)域模型”,對(duì)它有較全面的認(rèn)識(shí)。下面,對(duì)MFC概念模型的研究,我們把重點(diǎn)放在對(duì)app framework的研究上。
app framework中的message響應(yīng)/傳遞機(jī)制是最重要的。而Hook機(jī)制和Message響應(yīng)/傳遞機(jī)制是密切相關(guān)的,后者以前者為基礎(chǔ)。

1. Hook機(jī)制

也許有些程序員只知道hook機(jī)制可以編寫(xiě)很“牛”的應(yīng)用,孰不知MFC本身也是依靠hook機(jī)制的。

從圖中看到,每個(gè)hook擁有一個(gè)指針隊(duì)列,每個(gè)指針指向一個(gè)稱(chēng)為的HookProc函數(shù),HookProc將在合適的時(shí)機(jī)被OS調(diào)用執(zhí)行。hook是分不同種類(lèi)的,其實(shí)正是hook的種類(lèi)決定了它什么時(shí)機(jī)被OS調(diào)用執(zhí)行。提示,可以看一下“訂閱-發(fā)布”設(shè)計(jì)模式以助理解。

2 MFC中Message響應(yīng)函數(shù)的安裝

2.1 回憶API中Message響應(yīng)函數(shù)的安裝

API中Message響應(yīng)函數(shù)的安裝,是由CreateWindow()實(shí)現(xiàn)的,它將window與一個(gè)windowClass聯(lián)系起來(lái),而后者中記錄了Message響應(yīng)函數(shù)的指針。
至于細(xì)節(jié),看一下如何用Win32 SDK或Win16 SDK寫(xiě)程序就清楚了,其中 DefWindowProc()是API函數(shù),負(fù)責(zé)提供缺省的消息處理,所以,程序員只需要handle需要特殊處理的消息。

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
WNDCLASS wndclass;
...
wndclass.lpfnWndProc =WndProc;
wndclass.lpszClassName = szWindowClass;
...
RegisterClass(&wndclass);
hWnd = CreateWindow( szWindowClass, ...);
...
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
...
return;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
2.2 MFC中Message響應(yīng)函數(shù)的安裝

MFC中Message響應(yīng)函數(shù)的安裝顯然更復(fù)雜,是在CWnd::CreateEx()被調(diào)用時(shí)完成的,其中還用到了Hook機(jī)制。

我們可以先猜一下MFC是怎么做的。MFC支持massage map,使得對(duì)消息的響應(yīng)份散到多個(gè)message handler函數(shù)中,而不是API開(kāi)發(fā)是那種集中式的消息處理函數(shù);所以,想必會(huì)有專(zhuān)門(mén)的代碼來(lái)負(fù)責(zé)“檢索message map table然后調(diào)用message handle”。message map是為了支持程序員處理他關(guān)心的特殊message的,那么缺省的message處理邏輯在哪里呢?答案是MFC創(chuàng)建window obj時(shí)是用的“預(yù)定義的窗口類(lèi)”,自然已經(jīng)有了缺省的message處理函數(shù)。

從圖中看到,CWnd有成員變量m_pfnSuper、成員變量m_hWnd、成員函數(shù)OnWndMsg()和成員函數(shù)DefWindowProc()。Wnd::OnWndMsg()負(fù)責(zé)“在message map中定義的message handle”能否處理到來(lái)的message,如果處理了要返回true;CWnd::DefWindowProc()負(fù)責(zé)對(duì)message缺省處理。
執(zhí)行過(guò)程是,首先CWnd::CreateEx()被調(diào)用,window obj和window class被相應(yīng)建立,此時(shí)window class的WindowProc字段存儲(chǔ)了預(yù)定義的缺省處理函數(shù)的地址;由于有hook在監(jiān)聽(tīng)窗口創(chuàng)建消息,所以注冊(cè)的hookProc()會(huì)被調(diào)用執(zhí)行,它將classWindow數(shù)據(jù)結(jié)構(gòu)的WindowProc字段備份到CWnd::m_pfnSuper,再用SetWindowLong()改寫(xiě)classWindow數(shù)據(jù)結(jié)構(gòu)的WindowProc字段為::AfxWndProc()的地址。當(dāng)任何一個(gè)message到達(dá)時(shí),::AfxWndProc()被調(diào)用,至于它的邏輯,聰明的你一定猜到了,先調(diào)用Wnd::OnWndMsg(),如果返回值為false,還要調(diào)用CWnd::DefWindowProc(),CWnd::m_pfnSuper指向的缺省處理邏輯,也會(huì)在CWnd::DefWindowProc()中被調(diào)用。
提示,上面其實(shí)有多態(tài)情況發(fā)生。比如你可以在搜一下pWnd->WindowProc(nMsg, wParam, lParam); 另外,OnWndMsg和DefWindowProc都是CWnd類(lèi)的虛擬函數(shù)。

要是覺(jué)得不太好理解,最好在VC++里創(chuàng)建一個(gè)project實(shí)際跟蹤一下,下面是我跟蹤時(shí)調(diào)用棧映象的截圖。


3. SubClass機(jī)制


從圖中看到,SubClass機(jī)制以CWnd自身的m_pfnSuper為基礎(chǔ),和“MFC中Message響應(yīng)函數(shù)的安裝”很象。

4.frame work中的主要相關(guān)類(lèi)

frame work中的主要相關(guān)類(lèi) 就是 message route的候選人,正是它們的OnCmdMsg()共同完成了message route,形成了chain of responsability模式。

5. frame work中的chain of responsability模式

下圖是一個(gè)對(duì)象樹(shù),注意消息會(huì)在縱向和橫向兩個(gè)方向傳播。


消息在縱向方向上的傳遞,是在“上溯父類(lèi)的massge map表”,MFC的message map完全是為了代替虛函數(shù)而采取的手段,而和message route無(wú)關(guān)。

消息在橫向方向上的傳遞,才是message route,才是chain of responsability模式,由多個(gè)相關(guān)類(lèi)的OnCmdMsg()共同完成。

三、 總結(jié)
從上面的討論不難發(fā)現(xiàn),MFC中用到了不少設(shè)計(jì)模式,如上面提到的chain of responsability模式、composite模式和“訂閱-發(fā)布”模式。上面的討論不僅有助于程序員全面掌握Win32 API和MFC,對(duì)architect設(shè)計(jì)architecture也有很大幫助。

posted @ 2008-11-01 15:38 wrh 閱讀(356) | 評(píng)論 (0)編輯 收藏

系統(tǒng)理解Win32 API和MFC(上)
作者: 溫昱
作者主頁(yè): lcspace.diy.163.com

Win32 API是微軟的操作系統(tǒng)Windows提供給開(kāi)發(fā)人員的編程接口,它決定了我們開(kāi)發(fā)的Windows應(yīng)用程序的能力。MFC是微軟為開(kāi)發(fā)人員提供的類(lèi)庫(kù),在某種意義上是對(duì)Win32 API的封裝。本文試圖從全局角度對(duì)Win32 API和MFC進(jìn)行理解──給出二者的概念模型。
本文使用UML描述概念模型。Win32 API本不是面向?qū)ο蟮模矣妹嫦驅(qū)ο蟮挠^點(diǎn)去理解它,無(wú)非是想表達(dá)其全局。
本文參考了MSDN、相關(guān)書(shū)籍和網(wǎng)上的一些資料,在此一并感謝。

一、Win32 API的概念模型

Win32 API的object有3種:user obj,gdi obj,kernel obj。但是,如果一點(diǎn)不考慮OS本身的支持,就會(huì)在有些問(wèn)題上疑惑,因此,我這里把“operation system負(fù)責(zé)將中斷封裝成message”加上。

1、user obj、gdi obj、kernel obj、system 4者的關(guān)系

由于是kernel obj部分負(fù)責(zé)將另外3者聯(lián)系起來(lái),因此我們?cè)谙聢D中直接深入到kernel obj部分內(nèi)部。

從圖中看到,在內(nèi)存中運(yùn)行的,除了“負(fù)責(zé)將中斷封裝成message”的system支持部分,還有另外3類(lèi)object:kernel obj、user obj和gdi obj,每個(gè)obj都有一個(gè)句柄handle與之對(duì)應(yīng)。其中,gdi obj建立了待開(kāi)發(fā)的Windows 應(yīng)用和外部輸出設(shè)備的聯(lián)系,kernel obj中的file建立了內(nèi)存和永久存儲(chǔ)設(shè)備的聯(lián)系。具體說(shuō),內(nèi)存中的file從可以從硬盤(pán)上來(lái),如果這個(gè)file是可執(zhí)行文件,它將生成module,module運(yùn)行起來(lái)就是process,process可以包含多條thread,而thread的運(yùn)行映象最終還是來(lái)自于file。thread是kernel obj中最重要的一個(gè),因?yàn)橄㈥?duì)列就是thread擁有的,只有thread才能夠接受message。對(duì)gdi obj、urser obj和file的操作,也是發(fā)生在thread中的。所以書(shū)都講,process至少擁有一個(gè)thread。

2、展開(kāi)“system負(fù)責(zé)將中斷封裝成message”部分

下面展開(kāi)“system負(fù)責(zé)將中斷封裝成message”部分,盡早解除對(duì)“message到底是怎么形成的”的困惑。


3、展開(kāi)“gdi obj”部分

開(kāi)發(fā)人員可以通過(guò)gdi obj將app的信息反饋給User。

從圖中看到,gdi obj有8種,其中7種為:bmp,brush,pen,region,font,palette,path。另一種比較特殊的是DC,它可以被理解為一種容器,程序員通過(guò)調(diào)用SelectPallette()將pallte放入容器,通過(guò)調(diào)用BeginPath()和EndPath()將path放入容器,其它5種gdi obj,是通過(guò)調(diào)用SelectObject()放入容器的。DC又具體分為4種,其中DisplayDC就是最常用的用來(lái)支持我們“畫(huà)Window”的DC。 另外,如果覺(jué)得不好理解,請(qǐng)參考composite設(shè)計(jì)模式。

4、展開(kāi)user obj部分

4.1 第1次迭代

window在Windows應(yīng)用開(kāi)發(fā)中占有重要地位。

從圖中看到,window可分為3種:desktop,top-level window,child window。所有window被OS組織成tree,有專(zhuān)門(mén)的數(shù)據(jù)結(jié)構(gòu)來(lái)管理。desktop就是樹(shù)根,desktop的子節(jié)點(diǎn)是top-level window,top-level window的子節(jié)點(diǎn)是child window,child window仍然可以有子節(jié)點(diǎn),同樣歸屬于child window。tree數(shù)據(jù)結(jié)構(gòu)中還記錄了4種重要信息,是4種指針:parent指針、child指針、brother指針、owner指針。這樣,從任何一個(gè)window就能很容易地找到其它window了。

好了,暫且得到 window = desktop + topLevel + child 的結(jié)論,看看全局先。畢竟,一步到位有時(shí)候并不好。
從圖中看到,window確實(shí)占有重要地位。從邏輯是講,thread是window的擁有者;但是,所有window一起決定了屏幕看起來(lái)是上面樣子,何況點(diǎn)擊任何一個(gè)window都會(huì)使window得相互覆蓋關(guān)系發(fā)生變化,對(duì)所用window進(jìn)行統(tǒng)一管理是必須的,所以O(shè)S又不得不統(tǒng)一用window tree來(lái)管理window,反映復(fù)雜的window關(guān)系。每個(gè)window都必須有一個(gè)且只能有一個(gè)客戶區(qū),還可能有一個(gè)title bar。

 

再來(lái)看看CreateWindow()函數(shù)的interface spec透露了哪些信息。

 


從圖中看到,CreateWindow()負(fù)責(zé)為window建立與窗口類(lèi)的聯(lián)系。每個(gè)window都有一個(gè)窗口類(lèi)與之對(duì)應(yīng),而一個(gè)窗口類(lèi)可以對(duì)應(yīng)多個(gè)window。窗口類(lèi)中記錄了窗口函數(shù)和菜單等資源信息,而由file生成的module正是窗口函數(shù)和資源的老家。

 

4.2 第2次迭代

考察消息種類(lèi)。

從圖中看到,每個(gè)message都是發(fā)送給某個(gè)window的。注意,msg可由SYS代碼產(chǎn)生,也可以由API函數(shù)產(chǎn)生。

進(jìn)一步考察window,深入topLevel和child。


從圖中看到,OVERLAPPED風(fēng)格的window是top-level window的一種,而另一種POPUP風(fēng)格的window從本質(zhì)上(行為上)是特殊的一種OVERLAPPED風(fēng)格的window,雖然我們從coding的角度常常不這么認(rèn)為。

還是不好,因?yàn)楫?dāng)我們調(diào)用CreateWindow() API函數(shù)時(shí),明明感覺(jué)CHILD、OVERLAPPED、POPUP是“window style”。我再畫(huà)一張圖。

從圖中看到,control必須是CHILD風(fēng)格的,dialog必須是POPUP風(fēng)格的,而一般性的window卻可以是任意風(fēng)格的。

4.3 第3次迭代

總結(jié)user obj:


CreateDialog()函數(shù)示意:

從圖中看到,CreateDialog()和CreateWindow()最大的區(qū)別就是,它有對(duì)話框模板支持方便地定制dialog界面。注意,Dialog是特殊的window,窗口類(lèi)它一定也是有的。

posted @ 2008-11-01 15:37 wrh 閱讀(216) | 評(píng)論 (0)編輯 收藏
1.檢測(cè)程序中的括號(hào)是否匹配

  把光標(biāo)移動(dòng)到需要檢測(cè)的括號(hào)(如大括號(hào){}、方括號(hào)[]、圓括號(hào)()和尖括號(hào)<>)前面,鍵入快捷鍵“Ctrl+]”。如果括號(hào)匹配正確,光標(biāo)就跳到匹配的括號(hào)處,否則光標(biāo)不移動(dòng),并且機(jī)箱喇叭還會(huì)發(fā)出一聲警告聲。

  2.查看一個(gè)宏(或變量、函數(shù))的宏定義

  把光標(biāo)移動(dòng)到你想知道的一個(gè)宏上,就比如說(shuō)最常見(jiàn)的DECLARE_MAP_MESSAGE上按一下F12(或右鍵菜單中的Go To Defition Of …),如果沒(méi)有建立Browse files,會(huì)出現(xiàn)提示對(duì)話框,確定,然后就會(huì)跳到定義那些東西的地方。

  相當(dāng)可喜的是,它也可以看到Microsoft定義的系統(tǒng)宏,非常good.

  3.格式化一段亂七八糟的源代碼

  選中那段源代碼,按ATL+F8。

  4.在編輯狀態(tài)下發(fā)現(xiàn)成員變量或函數(shù)不能顯示

  刪除該項(xiàng)目擴(kuò)展名為.ncb文件,重新打開(kāi)該項(xiàng)目。

  5.如何整理ClassView視圖中大量的類(lèi)

  可以在classview 視圖中右鍵新建文件夾(new folder),再把具有相近性質(zhì)的類(lèi)拖到對(duì)應(yīng)的文件夾中,使整個(gè)視圖看上去清晰明了.

  6.定位預(yù)處理指定

  在源文件中定位光標(biāo)到對(duì)稱(chēng)的#if, #endif,使用Ctrl+K.

  7.如何添加系統(tǒng)中Lib到當(dāng)前項(xiàng)目

  在Project | Settings | Link | Object/library modules:輸入Lib名稱(chēng),不同的Lib之間用空格格開(kāi).

  8.如何添加系統(tǒng)中的頭文件(.h)到當(dāng)前項(xiàng)目.

  #include ,告訴編譯到VC系統(tǒng)目錄去找;使用#include "FileName.h",告訴編譯在當(dāng)前目錄找.

  9.如何在Studio使用匯編調(diào)試

  在WorkBench的Debugger狀態(tài)下按CTRL+F7.

  10.怎樣處理ClassZiard找不到的系統(tǒng)消息

  如果要在ClassWizard中處理WM_NCHITTEST等系統(tǒng)消息,請(qǐng)?jiān)贑lassWizard中Class Info頁(yè)中將Message filter改為Window就有了.

  11.如何干凈的刪除一個(gè)類(lèi)

  先從Workspace中的FileView中刪除對(duì)應(yīng)的.h和.cpp文件,再關(guān)閉項(xiàng)目,從實(shí)際的文件夾中刪除對(duì)應(yīng)的.h和.cpp文件與.clw文件。

  12.在Studio中快速切換兩個(gè)文件

  有時(shí),我們需要在最近使用的兩個(gè)文件中快速切換,換Ctrl+F6。這在兩個(gè)文件不相今的時(shí)候就有用的.

  13.取得源程序預(yù)處理后的結(jié)果:

  在Studio里,可以在->PROJECT-> SETTINGS->C/C++->Project Options中,在最后加上 /P /EP這兩個(gè)編譯開(kāi)關(guān)即可做到"只進(jìn)行預(yù)處理".就可以了。編譯以后就可以在源程序目錄中發(fā)現(xiàn)“文件名.I ”的文本文件。這就是預(yù)處理后的結(jié)果。

  (注意注:區(qū)分大小定,請(qǐng)用大定/P)

  14.在Debug模式中查看WINAPI調(diào)用后的返回值:

  很簡(jiǎn)單,且實(shí)用:在watch中加入@hr,err。在CSDN的文檔中心有一篇講得更細(xì),請(qǐng)參考。

  15.產(chǎn)生指定源程序文件的匯編代碼:

  從IDE菜單的Project->Setting打開(kāi)項(xiàng)目設(shè)置,按如下文件做:

  1.先在左邊選擇指定文件,可以多選。

  2. 在右邊的C++屬性頁(yè)中,在category中選擇List Files,接著在下面的List Files Type中選擇Assembly and source code(或選擇其它),最后在List File Name中輸入在個(gè)C/C++源文件產(chǎn)生的相應(yīng)的匯編代碼的文件。

  3.編譯整個(gè)工程。

  16.手工編譯純資源成dll:

  Rc.exe /v data.rc
  Cvtres.exe /machine:ix86 data.res
  Link /SUBSYSTEM:WINDOWS /DLL /NOENTRY data.res ;編譯成DLL文件

  這種方式創(chuàng)建的DLL是最小的,比起你用Win 32 Dynamic Libray等產(chǎn)生的更小。

  17:怎樣快速生成一個(gè)與現(xiàn)有項(xiàng)目除了項(xiàng)目名外完全相同的新項(xiàng)目?

  利用File菜單下生成新項(xiàng)目中的Custom AppWizard ,選擇 An existing Project ,然后選擇現(xiàn)有項(xiàng)目的項(xiàng)目文件名(*.dsp)Finish,編譯后就生成一個(gè)可以生成與現(xiàn)有項(xiàng)目相同但可以重新取名的項(xiàng)目的AppWizard。你可以象用MFC AppWizard一樣用它。如果不想用了,可以在VC 安裝目錄下Common\MSDev98\Template目錄中刪除該Wizard中.awx和 .pdb文件。

  18:如果想把整個(gè)項(xiàng)目拷貝到軟盤(pán),那些文件可以刪掉?

  除了項(xiàng)目文件夾中debug文件夾可以刪除外,.ncb,.clw,.opt 等文件也可以刪除,這些文件Rebuilt all后可以重新生成。

  附:VC項(xiàng)目文件說(shuō)明

  .dsp 項(xiàng)目參數(shù)配置文件,這個(gè)文件太重要,重點(diǎn)保護(hù)對(duì)象。.

  .dsw 工作區(qū)文件,重要性一般,因?yàn)樗畔⒉晃遥菀谆謴?fù)。

  以下文件在項(xiàng)目中是可丟棄的,有些文件刪除后,VC會(huì)自動(dòng)生成的。

  .clw ClassWizard信息文件,實(shí)際上是INI文件的格式,有興趣可以研究一下.有時(shí)候ClassWizard出問(wèn)題,手工修改CLW文件可以解決.如果此文件不存在的話,每次用ClassWizard的時(shí)候繪提示你是否重建.

  .ncb 無(wú)編譯瀏覽文件(no compile browser)。當(dāng)自動(dòng)完成功能出問(wèn)題時(shí)可以刪除此文件。build后會(huì)自動(dòng)生成。

  .opt 工程關(guān)于開(kāi)發(fā)環(huán)境的參數(shù)文件。如工具條位置等信息;(可丟棄)

  .aps (AppStudio File),資源輔助文件,二進(jìn)制格式,一般不用去管他.

  .plg 是編譯信息文件,編譯時(shí)的error和warning信息文件(實(shí)際上是一個(gè)html文件),一般用處不大.在Tools->Options里面有個(gè)選項(xiàng)可以控制這個(gè)文件的生成.

  .hpj (Help Project)是生成幫助文件的工程,用microsfot Help Compiler可以處理.

  .mdp (Microsoft DevStudio Project)是舊版本的項(xiàng)目文件,如果要打開(kāi)此文件的話,會(huì)提示你是否轉(zhuǎn)換成新的DSP格式.

  .bsc 是用于瀏覽項(xiàng)目信息的,如果用Source Brower的話就必須有這個(gè)文件.如果不用這個(gè)功能的話,可以在Project Options里面去掉Generate Browse Info File,可以加快編譯速度.

  .map 是執(zhí)行文件的映像信息紀(jì)錄文件,除非對(duì)系統(tǒng)底層非常熟悉,這個(gè)文件一般用不著.

  .pch (Pre-Compiled File)是預(yù)編譯文件,可以加快編譯速度,但是文件非常大.

  .pdb (Program Database)記錄了程序有關(guān)的一些數(shù)據(jù)和調(diào)試信息,在調(diào)試的時(shí)候可能有用.

  .exp 只有在編譯DLL的時(shí)候才會(huì)生成,記錄了DLL文件中的一些信息.一般也沒(méi)什么用.

posted @ 2008-10-26 09:25 wrh 閱讀(200) | 評(píng)論 (0)編輯 收藏

VC 中的定時(shí)

VC中提供了很多關(guān)于時(shí)間操作的函數(shù),編寫(xiě)程序時(shí)我們可以跟據(jù)定時(shí)的不同精度要求選擇不同的時(shí)間函數(shù)來(lái)完成定時(shí)和計(jì)時(shí)操作。
  
     方式一:VC中的WM_TIMER消息映射能進(jìn)行簡(jiǎn)單的時(shí)間控制。首先調(diào)用函數(shù)SetTimer()設(shè)置定時(shí)間隔,如SetTimer(0,200,NULL)即為設(shè)置200ms的時(shí)間間隔。然后在應(yīng)用程序中增加定時(shí)響應(yīng)函數(shù) OnTimer(),并在該函數(shù)中添加響應(yīng)的處理語(yǔ)句,用來(lái)完成到達(dá)定時(shí)時(shí)間的操作。這種定時(shí)方法非常簡(jiǎn)單,可以實(shí)現(xiàn)一定的定時(shí)功能,但其定時(shí)功能如同Sleep()函數(shù)的延時(shí)功能一樣,精度非常低,最小計(jì)時(shí)精度僅為18ms。CPU占用低,且定時(shí)器消息在多任務(wù)操作系統(tǒng)中的優(yōu)先級(jí)很低,不能得到及時(shí)響應(yīng),往往不能滿足實(shí)時(shí)控制環(huán)境下的應(yīng)用。只可以用來(lái)實(shí)現(xiàn)諸如位圖的動(dòng)態(tài)顯示等對(duì)定時(shí)精度要求不高的情況。

  方式二:VC中使用sleep()函數(shù)實(shí)現(xiàn)延時(shí),它的單位是ms,如延時(shí)2秒,用sleep(2000)。精度非常低,最小計(jì)時(shí)精度僅為30ms,用sleep函數(shù)的不利處在于延時(shí)期間不能處理其他的消息,如果時(shí)間太長(zhǎng),就好象死機(jī)一樣,CPU占用率非常高,只能用于要求不高的延時(shí)程序中。

  方式三:利用COleDateTime類(lèi)和COleDateTimeSpan類(lèi)結(jié)合WINDOWS的消息處理過(guò)程來(lái)實(shí)現(xiàn)秒級(jí)延時(shí)。以下是實(shí)現(xiàn)2秒的延時(shí)代碼:

     COleDateTime      start_time = COleDateTime::GetCurrentTime();
     COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;
     while(end_time.GetTotalSeconds()< 2) //實(shí)現(xiàn)延時(shí)2秒
    {
             MSG   msg;
             GetMessage(&msg,NULL,0,0);
             TranslateMessage(&msg);
             DispatchMessage(&msg);
             
            //以上四行是實(shí)現(xiàn)在延時(shí)或定時(shí)期間能處理其他的消息,
     //雖然這樣可以降低CPU的占有率,
            //但降低了延時(shí)或定時(shí)精度,實(shí)際應(yīng)用中可以去掉。
            end_time = COleDateTime::GetCurrentTime()-start_time;
     }//這樣在延時(shí)的時(shí)候我們也能夠處理其他的消息。      

  方式四:在精度要求較高的情況下,VC中可以利用GetTickCount()函數(shù),該函數(shù)的返回值是 DWORD型,表示以ms為單位的計(jì)算機(jī)啟動(dòng)后經(jīng)歷的時(shí)間間隔。精度比WM_TIMER消息映射高,在較短的定時(shí)中其計(jì)時(shí)誤差為15ms,在較長(zhǎng)的定時(shí)中其計(jì)時(shí)誤差較低,如果定時(shí)時(shí)間太長(zhǎng),就好象死機(jī)一樣,CPU占用率非常高,只能用于要求不高的延時(shí)程序中。下列代碼可以實(shí)現(xiàn)50ms的精確定時(shí):
      DWORD dwStart = GetTickCount();
      DWORD dwEnd   = dwStart;
      do
      {
         dwEnd = GetTickCount() - dwStart;
      }while(dwEnd <50);
為使GetTickCount()函數(shù)在延時(shí)或定時(shí)期間能處理其他的消息,可以把代碼改為:
      DWORD dwStart = GetTickCount();
      DWORD dwEnd   = dwStart;
      do
      {
             MSG   msg;
             GetMessage(&msg,NULL,0,0);
             TranslateMessage(&msg);
             DispatchMessage(&msg);
             dwEnd = GetTickCount()-dwStart;
      }while(dwEnd <50);
雖然這樣可以降低CPU的占有率,并在延時(shí)或定時(shí)期間也能處理其他的消息,但降低了延時(shí)或定時(shí)精度。

  方式五:與GetTickCount()函數(shù)類(lèi)似的多媒體定時(shí)器函數(shù)DWORD timeGetTime(void),該函數(shù)定時(shí)精度為ms級(jí),返回從Windows啟動(dòng)開(kāi)始經(jīng)過(guò)的毫秒數(shù)。微軟公司在其多媒體Windows中提供了精確定時(shí)器的底層API持,利用多媒體定時(shí)器可以很精確地讀出系統(tǒng)的當(dāng)前時(shí)間,并且能在非常精確的時(shí)間間隔內(nèi)完成一個(gè)事件、函數(shù)或過(guò)程的調(diào)用。不同之處在于調(diào)用DWORD timeGetTime(void) 函數(shù)之前必須將 Winmm.lib 和 Mmsystem.h 添加到工程中,否則在編譯時(shí)提示DWORD timeGetTime(void)函數(shù)未定義。由于使用該函數(shù)是通過(guò)查詢的方式進(jìn)行定時(shí)控制的,所以,應(yīng)該建立定時(shí)循環(huán)來(lái)進(jìn)行定時(shí)事件的控制。

  方式六:使用多媒體定時(shí)器timeSetEvent()函數(shù),該函數(shù)定時(shí)精度為ms級(jí)。利用該函數(shù)可以實(shí)現(xiàn)周期性的函數(shù)調(diào)用。函數(shù)的原型如下:
      MMRESULT timeSetEvent( UINT uDelay,
                              UINT uResolution,
                              LPTIMECALLBACK lpTimeProc,
                              WORD dwUser,
                              UINT fuEvent )
  該函數(shù)設(shè)置一個(gè)定時(shí)回調(diào)事件,此事件可以是一個(gè)一次性事件或周期性事件。事件一旦被激活,便調(diào)用指定的回調(diào)函數(shù),成功后返回事件的標(biāo)識(shí)符代碼,否則返回NULL。函數(shù)的參數(shù)說(shuō)明如下:
      uDelay:以毫秒指定事件的周期。
      Uresolution:以毫秒指定延時(shí)的精度,數(shù)值越小定時(shí)器事件分辨率越高。缺省值為1ms。
      LpTimeProc:指向一個(gè)回調(diào)函數(shù)。
      DwUser:存放用戶提供的回調(diào)數(shù)據(jù)。
      FuEvent:指定定時(shí)器事件類(lèi)型:
      TIME_ONESHOT:uDelay毫秒后只產(chǎn)生一次事件
      TIME_PERIODIC :每隔uDelay毫秒周期性地產(chǎn)生事件。      

  具體應(yīng)用時(shí),可以通過(guò)調(diào)用timeSetEvent()函數(shù),將需要周期性執(zhí)行的任務(wù)定義在LpTimeProc回調(diào)函數(shù)中(如:定時(shí)采樣、控制等),從而完成所需處理的事件。需要注意的是,任務(wù)處理的時(shí)間不能大于周期間隔時(shí)間。另外,在定時(shí)器使用完畢后,應(yīng)及時(shí)調(diào)用timeKillEvent()將之釋放。

  方式七:對(duì)于精確度要求更高的定時(shí)操作,則應(yīng)該使用QueryPerformanceFrequency()和 QueryPerformanceCounter()函數(shù)。這兩個(gè)函數(shù)是VC提供的僅供Windows 95及其后續(xù)版本使用的精確時(shí)間函數(shù),并要求計(jì)算機(jī)從硬件上支持精確定時(shí)器。

     QueryPerformanceFrequency()函數(shù)和QueryPerformanceCounter()函數(shù)的原型如下:
      BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
      BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);
  數(shù)據(jù)類(lèi)型ARGE_INTEGER既可以是一個(gè)8字節(jié)長(zhǎng)的整型數(shù),也可以是兩個(gè)4字節(jié)長(zhǎng)的整型數(shù)的聯(lián)合結(jié)構(gòu),其具體用法根據(jù)編譯器是否支持64位而定。該類(lèi)型的定義如下:
      typedef union _LARGE_INTEGER
      {
          struct
          {
             DWORD LowPart ;// 4字節(jié)整型數(shù)
             LONG HighPart;// 4字節(jié)整型數(shù)
          };
          LONGLONG QuadPart ;// 8字節(jié)整型數(shù)
          
       }LARGE_INTEGER ;
  
     在進(jìn)行定時(shí)之前,先調(diào)用QueryPerformanceFrequency()函數(shù)獲得機(jī)器內(nèi)部定時(shí)器的時(shí)鐘頻率,然后在需要嚴(yán)格定時(shí)的事件發(fā)生之前和發(fā)生之后分別調(diào)用QueryPerformanceCounter()函數(shù),利用兩次獲得的計(jì)數(shù)之差及時(shí)鐘頻率,計(jì)算出事件經(jīng)歷的精確時(shí)間。下列代碼實(shí)現(xiàn)1ms的精確定時(shí):
      LARGE_INTEGER litmp;
      LONGLONG QPart1,QPart2;
      double dfMinus, dfFreq, dfTim;
      QueryPerformanceFrequency(&litmp);
      dfFreq = (double)litmp.QuadPart;// 獲得計(jì)數(shù)器的時(shí)鐘頻率
      QueryPerformanceCounter(&litmp);
      QPart1 = litmp.QuadPart;// 獲得初始值
      do
      {
         QueryPerformanceCounter(&litmp);
         QPart2 = litmp.QuadPart;//獲得中止值
         dfMinus = (double)(QPart2-QPart1);
         dfTim = dfMinus / dfFreq;// 獲得對(duì)應(yīng)的時(shí)間值,單位為秒
      }while(dfTim<0.001);

posted @ 2008-10-25 09:15 wrh 閱讀(1606) | 評(píng)論 (0)編輯 收藏


VC定時(shí)器 SetTimer 怎么用阿
[此問(wèn)題的推薦答案]
SetTimer函數(shù)的用法
1 )用WM_TIMER來(lái)設(shè)置定時(shí)器

先請(qǐng)看SetTimer這個(gè)API函數(shù)的原型

UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定時(shí)器ID,多個(gè)定時(shí)器時(shí),可以通過(guò)該ID判斷是哪個(gè)定時(shí)器
UINT uElapse, // 時(shí)間間隔,單位為毫秒
TIMERPROC lpTimerFunc // 回調(diào)函數(shù)
);

例如
SetTimer(m_hWnd,1,1000,NULL); //一個(gè)1秒觸發(fā)一次的定時(shí)器
在MFC程序中SetTimer被封裝在CWnd類(lèi)中,調(diào)用就不用指定窗口句柄了

于是SetTimer函數(shù)的原型變?yōu)椋?

UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,YINT ,DWORD))

當(dāng)使用SetTimer函數(shù)的時(shí)候,就會(huì)生成一個(gè)計(jì)時(shí)器。函數(shù)中nIDEvent指的是計(jì)時(shí)器的標(biāo)識(shí),也就是名字。nElapse指的是時(shí)間間隔,
也就是每隔多長(zhǎng)時(shí)間觸發(fā)一次事件。第三個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),在這個(gè)函數(shù)里,放入你想要做的事情的代碼,你可以將它設(shè)定為NULL,
也就是使用系統(tǒng)默認(rèn)的回調(diào)函數(shù),系統(tǒng)默認(rèn)認(rèn)的是onTime函數(shù)。這個(gè)函數(shù)怎么生成的呢?你需要在需要計(jì)時(shí)器的類(lèi)的生成onTime函數(shù):
在ClassWizard里,選擇需要計(jì)時(shí)器的類(lèi),添加WM_TIME消息映射,就自動(dòng)生成onTime函數(shù)了。然后在函數(shù)里添加代碼,讓代碼實(shí)現(xiàn)功能。
每隔一段時(shí)間就會(huì)自動(dòng)執(zhí)行一次。

例:

SetTimer(1,1000,NULL);

1:計(jì)時(shí)器的名稱(chēng);

1000:時(shí)間間隔,單位是毫秒;

NULL:使用onTime函數(shù)。

當(dāng)不需要計(jì)時(shí)器的時(shí)候調(diào)用KillTimer(nIDEvent);

例如:KillTimer(1);

2) 調(diào)用回調(diào)函數(shù)

此方法首先寫(xiě)一個(gè)如下格式的回調(diào)函數(shù)

void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);
然后再用SetTimer(1,100,TimerProc)函數(shù)來(lái)建一個(gè)定時(shí)器,第三個(gè)參數(shù)就是回調(diào)函數(shù)地址。

二. 或許你會(huì)問(wèn),如果我要加入兩個(gè)或者兩個(gè)以上的 timer怎么辦?

繼續(xù)用SetTimer函數(shù)吧,上次的timer的ID是1,這次可以是2,3,4。。。。

SetTimer(2,1000,NULL);

SetTimer(3,500,NULL);

嗯,WINDOWS會(huì)協(xié)調(diào)他們的。當(dāng)然onTimer函數(shù)體也要發(fā)生變化,要在函數(shù)體內(nèi)添加每一個(gè)timer的處理代碼:

onTimer(nIDEvent)

{
switch(nIDEvent)

{
case 1:........;
break;
case 2:.......;
break;
case 3:......;
break;
}
}
本貼來(lái)自ZDNetChina中文社區(qū) http://bbs.zdnet.com.cn ,本貼地址:http://bbs.zdnet.com.cn/viewthread.php?tid=313294


VC定時(shí)器 SetTimer 怎么用阿
Timer事件,即定時(shí)器事件,是在游戲編程中,經(jīng)常使用的一個(gè)事件。借助它可以產(chǎn)生定時(shí)執(zhí)行動(dòng)作的效果。這篇文章,就和大家一起探討一下如何使用SetTimer()函數(shù)。
1、SetTimer定義在那里?

SetTimer表示的是定義個(gè)定時(shí)器。根據(jù)定義指定的窗口,在指定的窗口(CWnd)中實(shí)現(xiàn)OnTimer事件,這樣,就可以相應(yīng)事件了。

SetTimer有兩個(gè)函數(shù)。一個(gè)是全局的函數(shù)::SetTimer()

UINT SetTimer(
HWND hWnd, // handle of window for timer messages
UINT nIDEvent, // timer identifier
UINT uElapse, // time-out value
TIMERPROC lpTimerFunc // address of timer procedure
);

其中hWnd 是指向CWnd的指針,即處理Timer事件的窗口類(lèi)。說(shuō)道窗口類(lèi)(CWnd),我們有必要來(lái)看一下CWnd的繼承情況:CWnd有以下子類(lèi):CFrameWnd,CDialog,CView,CControlBar等類(lèi)。這也意味這些類(lèi)中都可以定義SetTimer事件。

同時(shí),SetTimer()在CWnd中也有定義,即SetTimer()是CWnd的一個(gè)成員函數(shù)。CWnd的子類(lèi)可以調(diào)用該函數(shù),來(lái)設(shè)置觸發(fā)器。

UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );

參數(shù)含義:

nIDEvent:是指設(shè)置這個(gè)定時(shí)器的iD,即身份標(biāo)志,這樣在OnTimer()事件中,才能根據(jù)不同的定時(shí)器,來(lái)做不同的事件響應(yīng)。這個(gè)ID是一個(gè)無(wú)符號(hào)的整型。

nElapse

是指時(shí)間延遲。單位是毫秒。這意味著,每隔nElapse毫秒系統(tǒng)調(diào)用一次Ontimer()。

void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD)

Specifies the address of the application-supplied TimerProc callback function that processes the WM_TIMER messages. If this parameter is NULL, the WM_TIMER messages are placed in the application’s message queue and handled by the CWnd object。

意思是,指定應(yīng)用程序提供的TimerProc回調(diào)函數(shù)的地址,來(lái)處里這個(gè)Timer事件。如果是NULL,處理這個(gè)Timer事件的定義這個(gè)Timer的CWnd對(duì)象。他將WM_TIMER消息傳遞給這個(gè)對(duì)象,通過(guò)實(shí)現(xiàn)這個(gè)對(duì)象的OnTimer()事件來(lái)處理這個(gè)Timer事件。

所以,一般情況下,我們將這個(gè)值設(shè)為NULL,有設(shè)置該定時(shí)器的對(duì)象中的OnTimer()函數(shù)來(lái)處理這個(gè)事件。

同樣的,我們?cè)倏纯碖illTimer()和OnTimer()的定義:

KillTimer同SetTimer()一樣,他也有兩個(gè),一個(gè)是全局的::KillTimer(),另一個(gè)是CWnd的一個(gè)函數(shù)。他的聲明如下:


//全局函數(shù)

BOOL KillTimer(
HWND hWnd, // handle of window that installed timer
UINT uIDEvent // timer identifier
);

//CWnd函數(shù)

BOOL KillTimer( int nIDEvent );

這兩個(gè)函數(shù)表示的意思是將iD為nIDEVENT的定時(shí)器移走。使其不再作用。其用法如同SetTimer()一樣。

再看看OnTimer()

CWnd::OnTimer
afx_msg void OnTimer( UINT nIDEvent );

ontimer()是響應(yīng)CWnd對(duì)象產(chǎn)生的WM_Timer消息。nIDEvent表示要響應(yīng)TIMER事件的ID。

二、Timer事件的使用:

由以上的分析,我們應(yīng)該很清楚,如何來(lái)使用Timer事件。假定我們?cè)谝晥D上畫(huà)一個(gè)漸變的動(dòng)畫(huà)。我們首先在菜單欄上添加一個(gè)菜單項(xiàng),給這個(gè)菜單添加命令響應(yīng):

pView->SetTimer(1,1000,NULL);//pView是視圖類(lèi)的指針,這里是在視圖類(lèi)當(dāng)中設(shè)置一個(gè)定時(shí)器。

添加完畢,再給視圖類(lèi)添加一個(gè)WM_Timer事件的相應(yīng)。在OnTimer()函數(shù)中編寫(xiě)漢書(shū),進(jìn)行相應(yīng)。

如此,就能做出動(dòng)畫(huà)。
本貼來(lái)自ZDNetChina中文社區(qū) http://bbs.zdnet.com.cn ,本貼地址:http://bbs.zdnet.com.cn/viewthread.php?tid=313294

 

 

 

posted @ 2008-10-25 09:02 wrh 閱讀(4091) | 評(píng)論 (0)編輯 收藏

用GetModuleFileName獲取程序當(dāng)前執(zhí)行文件名

在開(kāi)發(fā)過(guò)程中經(jīng)常需要獲得程序當(dāng)前的運(yùn)行目錄,這時(shí)就可以使用GetModuleFileName函數(shù)
DWORD WINAPI GetModuleFileName(
  HMODULE hModule,
  LPTSTR lpFileName,
  DWORD nSize
);

hModule:要獲取文件名的模塊名柄,null表示當(dāng)前模塊
lpFileName:輸出參數(shù),存放取得的文件名
nSize:lpFileName參數(shù)的長(zhǎng)度


void FileName()
{
    TCHAR lpFileName[MAX_PATH];
    ::GetModuleFileName(null, lpFileName, MAX_PATH);
    SetDlgItemText(IDC_TEXTBOX, lpFileName);
}


//==============================================================================

//==============================================================================
在開(kāi)發(fā)軟件的過(guò)程里,經(jīng)常需要把數(shù)據(jù)保存到當(dāng)前執(zhí)行文件路徑下面,或者讀取當(dāng)前執(zhí)行文件路徑下的一些配置信息。這時(shí)就需要從當(dāng)前模塊里獲取所在的目錄路徑,以便進(jìn)行固定的位置操作文件。要解決這個(gè)需求,就需要調(diào)用API函數(shù)GetModuleFileName來(lái)獲取模塊所在的路徑。
 
函數(shù)GetModuleFileName聲明如下:
WINBASEAPI
DWORD
WINAPI
GetModuleFileNameA(
    __in_opt HMODULE hModule,
    __out_ecount_part(nSize, return + 1) LPCH lpFilename,
    __in     DWORD nSize
    );
WINBASEAPI
DWORD
WINAPI
GetModuleFileNameW(
    __in_opt HMODULE hModule,
    __out_ecount_part(nSize, return + 1) LPWCH lpFilename,
    __in     DWORD nSize
    );
#ifdef UNICODE
#define GetModuleFileName GetModuleFileNameW
#else
#define GetModuleFileName GetModuleFileNameA
#endif // !UNICODE
hModule是模塊的句柄,或者設(shè)置為NULL表示當(dāng)前模塊。
lpFilename是保存路徑的緩沖區(qū)。
nSize是緩沖區(qū)的大小。
 
調(diào)用函數(shù)的例子如下:
#001 //獲取當(dāng)前程序所在路徑。
#002  //蔡軍生 2007/12/05 QQ:9073204 深圳
#003  void TestGetExePath(void)
#004  {
#005         //
#006         const int nBufSize = 512;
#007         TCHAR chBuf[nBufSize];
#008         ZeroMemory(chBuf,nBufSize);
#009 
#010         //獲取當(dāng)前執(zhí)行文件的路徑。
#011        if (GetModuleFileName(NULL,chBuf,nBufSize))
#012         {
#013               //輸出帶文件名稱(chēng)路徑。
#014               OutputDebugString(chBuf);
#015               OutputDebugString(_T("\r\n"));
#016 
#017               //獲取文件路徑。
#018               TCHAR* lpStrPath = chBuf;
#019               PathRemoveFileSpec(lpStrPath);
#020               OutputDebugString(lpStrPath);
#021               OutputDebugString(_T("\r\n"));
#022         }
#023 
#024  }
 
輸出的結(jié)果如下:
g:\work\windows_api\wincpp2\debug\WinCpp.exe
g:\work\windows_api\wincpp2\debug
posted @ 2008-10-21 09:45 wrh 閱讀(4835) | 評(píng)論 (0)編輯 收藏
僅列出標(biāo)題
共25頁(yè): First 15 16 17 18 19 20 21 22 23 Last 

導(dǎo)航

<2008年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

統(tǒng)計(jì)

常用鏈接

留言簿(19)

隨筆檔案

文章檔案

收藏夾

搜索

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品国产精品亚洲精品| 久久偷窥视频| 美女免费视频一区| 毛片一区二区三区| 亚洲剧情一区二区| 在线一区亚洲| 国产精品热久久久久夜色精品三区| 亚洲一本视频| 欧美一区三区二区在线观看| 国产精品久久久久aaaa九色| 亚洲第一精品夜夜躁人人躁| 亚洲国产高清在线| 欧美中文字幕第一页| 亚洲国产免费看| 国产日韩亚洲欧美| 国产精品久久看| 国产精品私房写真福利视频| 欧美伦理91i| 亚洲神马久久| 久久精品国产第一区二区三区最新章节| 欧美国产日韩一二三区| 久久这里只有精品视频首页| 国产欧美精品在线| 免费不卡在线视频| 亚洲免费影视| 免费成人你懂的| 欧美伊人精品成人久久综合97| 久久午夜电影| 久久久精品动漫| 国产精品久久久久久久久免费桃花 | 亚洲一区二区毛片| 亚洲一区图片| 欧美日韩国产在线播放网站| 久久一二三国产| 欧美激情二区三区| 国产精品美女久久久久久2018| 欧美特黄一级| 美国三级日本三级久久99| 亚洲精品五月天| 嫩草影视亚洲| 欧美日韩精品在线观看| 亚洲品质自拍| 久久国产精品久久国产精品| 在线亚洲观看| 欧美精品1区2区| 欧美成人福利视频| 影音先锋一区| 久久精品午夜| 久久综合给合| 在线观看一区| 快射av在线播放一区| 久久亚洲综合网| 国产一区二区三区久久| 性色av一区二区三区在线观看| 亚洲欧美日韩另类精品一区二区三区| 欧美伦理影院| 日韩一区二区免费看| 久久久久成人精品| 亚洲国产精品成人综合| 久久三级视频| 欧美韩日精品| 亚洲精品小视频在线观看| 欧美福利视频一区| aa成人免费视频| 欧美一级视频一区二区| 国外成人在线| 国产欧美精品久久| 亚洲免费av观看| 一区二区欧美精品| 欧美三日本三级三级在线播放| 亚洲九九爱视频| 亚洲香蕉成视频在线观看 | 亚洲老司机av| 欧美日韩精品中文字幕| 亚洲最新在线视频| 欧美在线看片a免费观看| 激情久久久久久久| 狼人天天伊人久久| 亚洲精品一区中文| 午夜精品短视频| 在线观看视频免费一区二区三区| 欧美成人精品一区| 亚洲性人人天天夜夜摸| 久久综合中文| 在线一区二区日韩| 国产欧美一区二区精品秋霞影院| 久久精品亚洲热| 亚洲精品久久久久久久久| 亚洲图片激情小说| 韩国自拍一区| 欧美日韩免费在线视频| 亚洲欧美日本精品| 欧美激情一区二区在线| 亚洲欧美日韩视频二区| 亚洲高清资源| 国产精品日韩精品欧美在线| 久久综合九色九九| 亚洲无人区一区| 欧美福利在线| 久久精品导航| 一本久道久久综合狠狠爱| 国产亚洲欧洲一区高清在线观看 | 西西人体一区二区| 尤物yw午夜国产精品视频明星| 欧美人与禽猛交乱配| 欧美在线观看一二区| 亚洲精品中文字幕有码专区| 久久九九精品99国产精品| 亚洲视频999| 1024成人| 国产亚洲精品高潮| 欧美午夜精品理论片a级按摩| 久久免费视频在线观看| 亚洲男人的天堂在线aⅴ视频| 亚洲黄色性网站| 久久综合一区二区| 欧美一区二区三区在线看| 一本色道久久综合亚洲精品不卡| 在线精品观看| 在线播放精品| 国色天香一区二区| 国产色婷婷国产综合在线理论片a| 欧美人成在线| 欧美高清视频在线| 亚洲欧洲一区二区三区在线观看 | 久久综合五月| 亚洲欧美综合一区| 欧美一区二区免费观在线| 亚洲国产mv| 蜜臀av性久久久久蜜臀aⅴ四虎| 午夜精品亚洲一区二区三区嫩草| 亚洲精品乱码久久久久久久久| 黄色精品一二区| 国产亚洲亚洲| 国产日产亚洲精品| 国产目拍亚洲精品99久久精品| 欧美日韩一区二区高清| 欧美激情精品久久久久久| 蜜臀99久久精品久久久久久软件| 久久久国产一区二区| 久久久成人精品| 久久久久久亚洲综合影院红桃 | 欧美一级视频免费在线观看| 亚洲一区二区在线免费观看视频| 亚洲美女诱惑| 夜夜爽99久久国产综合精品女不卡| 亚洲国产精品尤物yw在线观看| 亚洲成人资源网| 亚洲国产欧美精品| 亚洲精品偷拍| 亚洲天堂视频在线观看| 午夜精品在线视频| 久久久福利视频| 久热精品视频在线观看一区| 久久综合久久综合九色| 免费在线观看成人av| 亚洲国产精品va在看黑人| 亚洲人午夜精品| 中文亚洲免费| 午夜精品一区二区三区四区| 欧美综合二区| 免费日韩av片| 欧美视频在线观看视频极品| 国产精品一区二区久久| 好吊妞这里只有精品| 亚洲国产精品久久精品怡红院| 最新中文字幕亚洲| 一区二区成人精品| 午夜精品久久久久久久蜜桃app | 国产精品电影在线观看| 国产日韩欧美a| 亚洲国产精品久久久久婷婷884| 99re成人精品视频| 午夜精品三级视频福利| 国产日韩欧美一区在线| 在线观看一区二区精品视频| avtt综合网| 久久高清一区| 亚洲国产精品精华液网站| 中文欧美日韩| 免费观看成人网| 国产精品一区二区三区久久| 一色屋精品亚洲香蕉网站| 亚洲色诱最新| 久久综合久久综合这里只有精品| 亚洲青色在线| 欧美一区不卡| 欧美日本中文字幕| 国内精品一区二区三区| 99riav久久精品riav| 久久久久久久久久久久久9999| 亚洲三级视频| 久久久精品动漫| 欧美午夜免费影院| 亚洲第一级黄色片| 欧美在线播放一区二区| 亚洲人www| 欧美成人在线免费视频| 亚洲一区二区三区中文字幕在线| 久久在线免费观看|