MFC學習(五)

黨偉_90發表於2018-05-04

1:應用程式類

CTestOneApp::InitInstance

可以看做是MFC程式的入口函式,main函式隱藏在這個函式中。實際開發中一般不需要對這個類進行操作,但如果要在建立主對話方塊之前處理一些資料或者準備工作,就可以把程式碼新增到這個函式中,主對話方塊顯示之前。

這裡有兩個比較典型的應用:
1)啟動介面之前彈出個登入介面。
2)啟動介面之前,彈出一個專案配置介面。

2:對話方塊類

// CTestOneDlg 對話方塊類,繼承自CDialogEx類。對話方塊類負責與使用者互動,處理使用者訊息,接收使用者輸入。

class CTestOneDlg : public CDialogEx
{
public:
// 標準建構函式
CTestOneDlg(CWnd* pParent = NULL);
// 對話方塊資料
enum { IDD = IDD_TESTONE_DIALOG }; 
protected:
// 動態資料交換,負責控制元件與變數之間的關聯
virtual void DoDataExchange(CDataExchange* pDX); 
protected:
//應用程式控制程式碼
HICON m_hIcon;

//過載初始化對話方塊
virtual BOOL OnInitDialog();
//定義訊息WM_SYSCOMMAND處理函式
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
//定義訊息WM_PAINT處理函式
afx_msg void OnPaint();
//定義訊息ON_WM_QUERYDRAGICON處理函式
afx_msg HCURSOR OnQueryDragIcon();
//訊息對映
DECLARE_MESSAGE_MAP()
};

這個類看出以下幾點:
(1)控制元件與資料關聯,可以簡單的交給框架
(2)在MFC框架上開發主要是針對訊息處理機制

新增一個button,並且新增一個事件後:會有如下變化

PUSHBUTTON "Button1",IDC_BTN_TEST,151,57,50,14 // RC 檔案拿這個ID作為控制元件的標示

#define IDC_BTN_TEST 1001 /resource.h 定義一個ID號。

afx_msg void OnBnClickedBtnTest(); // 事件響應函式

BEGIN_MESSAGE_MAP(CTestOneDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_TEST, &CTestOneDlg::OnBnClickedBtnTest) // 把事件,響應函式, 行為三者繫結到一起
END_MESSAGE_MAP()

void CTestOneDlg::OnBnClickedBtnTest() //事件響應函式
{
// TODO: 在此新增控制元件通知處理程式程式碼
}

3 SendMessage和 postMessage 區別
SendMessage : 同步, 返回值表示處理訊息後的返回值。
postMessage: 非同步,只是把訊息放入佇列,返回值僅表示post是否正確。

同一個執行緒內:PostMessage只把訊息放入佇列,然後通過訊息迴圈Dispatch到達視窗。SendMessage傳送訊息時,系統直接呼叫目標視窗的訊息處理程式,並將結果返回。

不同執行緒:最好用PostThreadMessage代替PostMessage。 SendMessage傳送訊息到目標視窗所屬執行緒的訊息佇列,然後傳送的執行緒等待,直 到處理完。

 

4 onpaint() 和 ondraw()
視窗改變後,產生無效區域,需要重繪,windows會傳送WM_PAINT通知客戶區變化,客戶區的重繪需要自己完成。

CVIew派生自CWnd, 而OnPaint()是CWnd 的類成員,同時負責響應WM_PAINT訊息。OnDraw()是CVIEW的成員,沒有響應訊息功能。
要想在螢幕上繪圖,首先要建立裝置環境DC,DC是一個資料結構,包含輸出裝置的繪圖屬性的描述。MFC提供了CPaintDC 類和CWindowDC 類
實時響應,CPaintDC支援重畫。

當檢視無效時(大小,移動,被遮蓋)Windows將WM_PAINT訊息傳送給它。 該檢視的OnPaint處理函式通過建立CPaintDC類的DC物件來響應
該訊息並呼叫檢視的OnDraw成員函式,通常不用重寫ONpaint函式。
void CView::OnPaint()

CPaintDC dc(this);
OnPreparDC(&dc);
OnDraw(&dc); //呼叫了OnDraw
}
OnPaint最後也要呼叫OnDraw,因此我們一般會在OnDraw函式中進行繪製。

void CMyView::OnDraw( CDC* pDC )

CMyDoc* pDoc = GetDocument(); 
CString s = pDoc->GetData(); // Returns a CString
CRect rect;
GetClientRect( &rect ); 
pDC->SetTextAlign( TA_BASELINE | TA_CENTER ); 
pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );
}

5 強制重畫視窗

InvalidateRect(&Rect) :使得指定的區域無效。

Invalidate():使得整個視窗無效,形成無效矩形。

UpdateWindow(): 立即傳送WM_PAINT,不過在它傳送前,先呼叫GetUpdateRect(hWnd,NULL,TRUE)看有無可 繪製區域,如果沒有則不傳送訊息。

RedrawWindow()是具有Invalidate()和UpdateWindow()的雙特性。宣告視窗的狀態為無效,並立即更新視窗,立即呼叫WM_PAINT訊息處理。

6  CView 與 CDcoument關係

CVIew 有一個成員變數CDocument, 指向相關的Document.
CView 與 Document交談的過程:
A:使用者在View做動作,取得Document指標,更改資料內容。
B: View呼叫Document的UpdatedAllViews.
C: 其他的view的onUpdate() 被呼叫, 各種view的畫面就更新了。
D:CVIew:Onupdate()被呼叫,代表通知他:document的內容已經改變了,你更新畫面吧。 也可以用一種低效率的方式: invaalidate(TRUE),把視窗
整個設為重繪區,產生WM_paint,再讓Cview::OnDraw()。 

7 你熟悉預編譯指令麼?條件編譯是用來做什麼的?你會寫麼?
預編譯指令: 在編譯之前做一些事。
#include : 檔案包含
#define: 巨集替換
# if, #ifndef, #ifdef, #endif, #undef : 條件編譯。
#pragma :佈局控制。 主要是設定編譯器的狀態。 #pragma once                #pragma pack(n)

8  C++ 中的string/WString

string就是*char, wstring wchar_t, 用來處理中文,是寬字元。

9 MFC用的是Unicode

10 stdafx.h   標頭檔案預編譯

把一個工程中使用的一些MFC標準標頭檔案(windows.h Afxwin.h)預先編譯,以
後該工程編譯時,不再編譯這部分標頭檔案。

11 MFC包含幾種型別程式?其中MFC應用程式又包含哪幾類

單文件(畫圖),多文件(vs 2015), 對話方塊.

12 MFC的訊息機制
MFC使用一種訊息對映機制來處理訊息,一個訊息與訊息處理函式一一對應的訊息對映表,以及訊息處理
函式的宣告和實現程式碼。當視窗接收到訊息的時候,會到訊息對映表中查詢訊息的處理函式,然後訊息處理函式進行
處理

13 訊息對映

Windows程式都維護有自己的訊息佇列,保持佇列訊息(當然也有非佇列訊息,他們直接發給視窗),並用訊息迴圈
對訊息進行處理。

訊息迴圈首先通過GetMessage取得訊息並從佇列中移除,對於加速鍵,會呼叫TranslateAccelerator函式,對其進行
翻譯和處理,如果處理成功就不在呼叫translateMessge. 
否則進行訊息的轉換和派發。讓目的視窗的視窗過程來處理訊息。
while (GetMessage(&msg, NULL, 0, 0)) 
 { 
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 

TranslateMessage(&msg); 
DispatchMessage(&msg); 

真正處理訊息的是所謂的視窗過程(LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)),這個函式的引數記錄了過程對應的視窗、訊息的ID以及引數,在其內部開發者可以實現。

14 自定義訊息

A、自定義訊息號:#define WM_CCTRY_MSG (WM_USER+100)
B、在標頭檔案中新增訊息響應函式的宣告:afx_msg LRESULT OnCcTryMsg(WPARAM wParam, LPARAM lParam);
C、在CPP檔案中新增訊息響應函式的實現:
LRESULT CXXXDlg::OnCcTryMsg(WPARAM wParam, LPARAM lParam) {
//相關程式碼;
}
D、在 BEGIN_MESSAGE_MAP 與 END_MESSAGE_MAP 之間加入訊息的對映程式碼:ON_MESSAGE(WM_CCTRY_MSG, &CDlgTestDlg::OnCcTryMsg)

然後在想觸發訊息的地方:sendMessage(),就可以。

15. MFC的對話方塊的種類,各自怎麼使用?及相關函式。

模態對話方塊和非模態對話方塊。
模態對話方塊:工作時其父視窗無效。 DoModal();
非模態對話方塊: dlg.Create(;)

15 程式間通訊
管道(匿名): 半雙工。只適合父子程式之間通訊。傳輸的是流。所以程式間通訊必須約定好資料格式。建立管道時,
        分配一個頁面作為資料緩衝區,程式對緩衝區進行讀寫。
有名管道:非父子程式也可以通訊,有傳輸格式。
訊息佇列:放在核心中的訊息連結串列,
訊號量:
共享記憶體:分配一塊能被其他程式訪問的記憶體。共享的記憶體被對映到兩個程式的虛擬空間。但是需要自己提供同步機制。
訊號量,互斥鎖都可以。
這個效率最高,管道和訊息佇列需要在核心和使用者空間資料拷貝。

16 MFC程式的初始化:http://www.jizhuomi.com/software/267.html
建立MFC視窗很容易,只用兩步:
從CWinApp派生一個應用程式,然後建立應用程式物件。initInstance()裡面建立視窗就行。

WinMain()函式:
InitInstance 是程式入口點,是虛擬函式,應用程式初始化(其實也winmain函式呼叫它的)

注意: 全域性變數的的建構函式在main函式之前執行
基本流程:

註冊視窗:註冊後才能從系統中找到它,獲得控制程式碼,然後向視窗傳送訊息。
建立視窗:
顯示更新視窗:show, update()
訊息迴圈:
回撥函式:

17  訊息對映機制的原理及實現。

在每個能接收和處理訊息的類中,定義一個訊息和訊息函式靜態對照表,將訊息與訊息處理函式繫結。當處理訊息的時候到這個表中去查就行了。
BEGIN_MESSAGE_MAP() 巨集中定義。

18  ASSERT() ,是函式還是巨集 
預處理巨集, assert(expr), 先計算表示式expr, 如果為假,那麼它會輸出資訊並終止程式執行。
如果用if else實現同樣功能的話,就會從函式開始括到函式尾。
assert是巨集不是函式,定義在cassert標頭檔案中。
使用assert的缺點是,頻繁的呼叫會極大的影響程式的效能,增加額外的開銷;
完成除錯後,不必從原始碼中刪除assert()語句,因為巨集NDEBUG有定義時,巨集assert()的定義為空,
即可以通過在包含#include assert.h>或#include< csaaert >的語句之前插入 #define NDEBUG 來禁用assert呼叫:

assert只有在Debug版本中才有效,如果編譯為Release版本則被忽略(程式一般分為Debug 版本和Release 版本,
Debug 版本用於內部除錯,Release 版本發行給使用者使用);

assert用法注意:
A:不要一起判斷多個條件,否則不知道是哪個有問題。
B:不能改變變數的值、
C: assert語句後面空一行。

19 MFC訊息三種型別

      在MFC應用程式中傳輸的訊息有三種型別:視窗訊息、命令訊息和控制元件通知。  

(1)視窗訊息:WM_XXX,除WM_COMMAND之外,所有以WM_開頭的訊息

      視窗訊息(Window Message)一般與視窗的內部運作有關,如:建立視窗、繪製視窗和銷燬視窗等。通常,訊息是從系統傳送到視窗,或從視窗傳送到視窗。  

(2)命令訊息:WM_COMMAND

      命令訊息一般與處理使用者請求相關,當使用者單擊一個選單項或工具欄時,命令訊息產生,並被髮送到能處理該請求的類物件(如:裝載檔案、編輯文字和儲存選項等)。  

(3)控制元件通知:有多種格式
      通常,控制元件通知在某些重要事件發生時,由控制元件視窗傳送到父視窗,如開啟一個組合框。控制元件通知為父視窗進一步控制子視窗提供了機會。例如,開啟一個組合框時,父視窗可以用組合框初建時得不到的訊息填充它。  

      BN_XXXX是CButton產生的訊息,EN_XXXX是CEdit產生的訊息,等等。

 

現在就可以知道為什麼有ON_MESSAGE ,ON_COMMAND, , ON_NOTIFY了。
ON_MESSAGE是處理所有的Windows的訊息的,因為所有的訊息都以相同的格式傳送,也就是ID, WPARAM, LPARAM.
ON_COMMAND是專門處理WM_COMMAND訊息的,這樣我們就不用自己解開WM_COMMAND中wParam和lParam中傳送的控制元件ID, 事件種類…(所有的都在MFC內部解決了:),當然方便了。
ON_NOTIFY更是不用說了,看看他的處理函式,是不是把NMHDR解出來了。

20 MFC繪圖有哪幾類DC?各自的類名,及區別

   裝置描述表(DC)是Windows中的一種資料結構,它包含GDI需要的所有關於顯示介面情況的描述欄位,包括相連的物理裝置和各種各樣的狀態資訊。從而提供了應用程式設計的平臺無關性。

 

HDC:裝置上下文控制程式碼(可以理解為指向DC結構的指標),它指向一塊描述裝置的相關的內容的記憶體塊。

 

CDC:是MFC裡面的一個類,且這類封裝了幾乎所有關於HDC的操作,由於類的內部包含一個m_hWnd的控制程式碼,

 

所以,CDC封裝的操作(函式)與SDK平臺中與關於HDC的操作都缺少一個指向裝置上的控制程式碼(不是沒有,而是這個控制程式碼在被封裝起來)。

(1)、HDC到CDC的轉換:
方法一: 此方法在裝置結束時不會銷燬原來的資源(即:hDC,hBitmap)
CDC *pDC = CDC::FromHandle(hDC);

方法二:此方法在裝置結束時會銷燬原來的資源(即:hDC,hBitmap)
CDC dc;
dc.Attach(hDC);

(2)、CDC到HDC的轉換:

   CDC  dc;

   HDC  hDC;

   hDC = dc.GetSafeHdc();

 

CPaintDC:

CClientDC:

CWindowDC:

21:  MFC的執行緒有哪幾類?相互有什麼區別?各自的建立方法是什麼

 兩種:介面執行緒:有訊息迴圈,能響應使用者的介面操作,必須繼承自CWinThread.

            工作執行緒:

  AfxBeginThread(RUNTIME_CLASS(MyThread));   二者的引數有區別。

22 MFC常用控制元件?通用對話方塊?

23 MFC的檔案類?檔案查詢類?

相關文章