FASM---Win32彙編學習4

FASM---Win32彙編學習4

第四課----繪製文本串

            本課中,我們將學習如何在窗口的客戶區“繪製”字符串。我們還將學習“設備環境”的概念。

 

    Windows中的文本是一個GUI(圖形用戶界面)對象。每一個字符實際上由許多像素點組成,這些點在有筆畫的地方顯示出來,這樣就會出現字符。這也是爲什麼我說“繪製”字符,而不是寫字符。通常您都是在您應用程序的“客戶區”繪製字符串(儘管在客戶區外也可以“繪製”)。Windows下的"繪製"字符串方法和Dos下的截然不同,在Dos下你可以把屏幕想成是85*25的平面,而在windows下由於屏幕上同時由幾個應用程序的畫面,所以您必須遵守相應的規範。Windows通過把每一個應用程序限制在它的客戶區來做到這一點。當然客戶區的大小是可變的,您可以隨時調整。

提示:客戶區是指我們窗體與用戶交互的部分。打個比方,比如我們windows 的notepad記事本程序的客戶區就是它的編輯框. 非客戶區則是指它的標題欄和菜單欄以及滾動條。

    在您在客戶區“繪製”字符串前,你必須從Windows那裏獲取客戶區的大小。確實你無法像在dos下那樣隨心所欲的在任何地方繪製,繪製前你必須得到Windows的允許,然後windows會告訴你客戶區的大小,字體,顏色以及其他的gui對象屬性。您可以用這些來“繪製”。

 

   什麼是"設備環境(DC)"呢,它其實是由windows內部維護的一個數據結構。一個設備環境與一個特定的設備相連。像打印機和顯示器。對於顯示器來說,"設備環境"和一個個特定的窗口相連。

 

  “設備環境”中有些屬性和繪圖有關,像顏色,字體等。您可以隨時的改動那些缺省值。之所有保存那些缺省值是因爲爲了方便。您可以把設備環境想成是Windows提供給你的繪圖環境。而你可以隨時根據需要改變那些缺省值。

在應用程序需要繪製時,您必須得到一個“設備環境”的句柄。通常有幾種方法。

    1.在WM_PAINT消息中使用call BeginPaint

    2.在其他消息中使用call GetDC

    3.Call createDC 建立你自己的DC

您必須牢記的是,在處理單個消息後,你必須釋放“設備環境”的句柄。不要在一個消息中獲得設備環境的句柄,而在另一個消息中釋放它。

 

  我們在Windows發送WM_PAINT消息時處理繪製客戶區,Windows不會保存客戶區的內容,它用的方法是“重繪”機制。(譬如當客戶區被另一個應用程序的客戶區覆蓋),Windows會把WM_PAINT消息放入該應用程序的消息隊列。重繪窗口的客戶區是各個窗口的責任,你要做的是在窗口過程處理WM_PAINT的部分知道繪製什麼和如何繪製。

  您必須瞭解另一個概念是“無效區域”。windows把一個最小的需要重繪的正方形叫做“無效區域”。當windows發現了一個無效區域後,它就會像該用戶程序發送一條WM_PAINT消息,在WM_PAINT的處理過程中首先要得到一個有關的繪圖結構體。裏面包括了“無效區”的座標位置等。您可以通過BeginPaint讓無效區有效。如果您不處理WM_PAINT消息,至少要調用缺省的DefWindowProc,或者調用ValidateRect讓"無效區"有效。否則您的應用將會受到無窮無盡的WM_PAINT。

 

下面是相應消息的步驟。

1.取得"設備環境"的句柄

2.繪製客戶區

3.釋放環境句柄。

心得:假如 我們程序的客戶區假設被另一個應用程序的客戶區所覆蓋,那麼此刻另一個應用程序退出,相應我們程序被覆蓋客戶區需要發生重繪,而被覆蓋的這部分此時叫做“無效區域”,這時候windows就會檢測到我們需要發生重繪的無效區域,然後發送給我們程序的消息隊列中一個WM_PAINT消息。此刻我們就需要在我們的窗口過程中處理WM_PAINT消息, 及時不處理也要調用缺省的DefWindowProc函數。BeginPaint函數默認使無效區有效。 那麼BeginPaint返回了一個設備環境的句柄。因爲我們windows下的硬件設備都是以相關的驅動形式來和windows通信了,那麼我們的設備環境也就關聯了相應的設備。 例如我們的窗體對象關聯了相應的顯示器設備,那麼此時我們要想在窗體上顯示文本,我們必須通過設備驅動,因爲windows提供給了我們相應的函數以及相應的數據類型,我們只需要取到設備環境的句柄,然後通過相應的函數來調用,此時我們不需要它是如何去實現的,我們只需要遵守windows的規則就可以了。。那麼我們下面來看看代碼更深入的來分析下。

 

   format PE GUI 4.0
    include 'win32ax.inc'
   
   
    ;************************數據********************************
    szClassName db 'first Windows',0
    szWndName    db '我的第一個程序',0
    szCommand dd ?
    hIcon    rd 1
    hInstanse rd 1
    hCursor    rd 1
    hWnd    rd 1


    entry $
        invoke GetModuleHandle,NULL
        mov    [hInstanse], eax
        invoke GetCommandLine,NULL
        mov [szCommand], eax
        stdcall _WinMain,[hInstanse], NULL, [szCommand], SW_SHOWDEFAULT
        invoke ExitProcess,NULL
       
    proc _WinMain    HhInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
        local @wc : WNDCLASSEX
        local @msg : MSG
       
       
        invoke RtlZeroMemory,addr @wc,sizeof.WNDCLASSEX
        invoke LoadIcon,NULL, IDI_WINLOGO
        mov [hIcon], eax
        invoke LoadCursor,NULL, IDC_ARROW
        mov [hCursor], eax
        mov [@wc.cbSize], sizeof.WNDCLASSEX
        mov [@wc.style], CS_HREDRAW or CS_VREDRAW
        mov [@wc.lpfnWndProc], _WndProc
        mov [@wc.cbClsExtra], NULL
        mov [@wc.cbWndExtra], NULL
        push hInstanse
        pop [@wc.hInstance]
        mov [@wc.hIcon], hIcon
        mov [@wc.hCursor], hCursor
        mov [@wc.hbrBackground], COLOR_WINDOW
        mov [@wc.lpszMenuName], NULL
        mov [@wc.lpszClassName], szClassName
        ;註冊窗口類
        invoke RegisterClassEx,addr @wc
        ;建立窗口
   
        invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
    100, 100, 600, 400,/
    NULL, NULL, [hInstanse], NULL
        mov [hWnd], eax
        invoke ShowWindow,[hWnd],SW_SHOWNORMAL
        invoke UpdateWindow,[hWnd]
   
GetMsg:
        invoke GetMessage,addr @msg, NULL, 0, 0
        or eax, eax
        jz EndMsg
        invoke TranslateMessage,addr @msg
        invoke DispatchMessage,addr @msg
        jmp GetMsg
       
       
EndMsg:
        mov eax, [@msg.wParam]
        ret
    endp
   
   
    proc _WndProc uses ebx esi edi,hWnd:DWORD, wMsg:DWORD, wParam:DWORD, lParam:DWORD
        local @hdc:DWORD
        local @ps : PAINTSTRUCT
        local @rect: RECT
       
        cmp [wMsg], WM_DESTROY
        jz Quit
        cmp [wMsg], WM_PAINT
        jz PAIT
        invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
        ret
   
    PAIT:
        invoke BeginPaint,[hWnd], addr @ps
        mov [@hdc], eax ;保存設備環境的句柄
        invoke GetClientRect,[hWnd], addr @rect
        invoke DrawText,[@hdc], szWndName, -1, addr @rect,/
        DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,[hWnd], addr @ps
        jmp endWnd
   
    Quit:
        invoke PostQuitMessage,NULL
        jmp endWnd
       
   
    endWnd:
        xor eax,eax
        ret
    endp

   
;///////////////////////////輸入表////////////////////////////////////////////////

   
section '.import' data import readable writeable
       
   
    library kernel32, 'kernel32.dll',/
            user32, 'user32.dll',/
            gdi32, 'gdi32.dll'    
    include 'api/kernel32.inc'
    include 'api/user32.inc'
include 'api/gdi32.inc'

 

今天我們和昨天不同的只有幾處代碼。。

PAIT:
        invoke BeginPaint,[hWnd], addr @ps
        mov [@hdc], eax ;保存設備環境的句柄
        invoke GetClientRect,[hWnd], addr @rect
        invoke DrawText,[@hdc], szWndName, -1, addr @rect,/
        DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,[hWnd], addr @ps

    我們看這裏剛剛在上面我們說了,windows只要檢測到我們由需要重繪的無效區域,就會發送WM_PAINT消息,那麼我們必須處理WM_PAINT或者通過缺省的DefWindowProc函數。 因爲BeginPaint函數會自動的將我們的無效區域設置爲有效,所以此時我們一般調用BeginPaint來設置我們的無效區域爲有效,因爲BeginPaint函數還返回我們相應的窗體對象的 設備環境的句柄,我們此時獲得相應設備環境句柄後,然後調用GetClientRect獲得我們應用程序窗體 客戶區的大小。大小放在@rect結構中。然後把它傳給DrawText。DrawTexe的語法是

DrawText是一個高層的調用函數。它能自動處理像換行、把文本放到客戶區中間等這些雜事。所以您只管集中精力“繪製”字符串就可以了。我們會在下一課中講解低一層的函數 TextOut,該函數在一個正方形區域中格式化一個文本串。它用當前選擇的字體、顏色和背景色。它處理換行以適應正方形區域。它會返回以設備邏輯單位度量的文本的高度,我們這裏的度量單位是像素點。讓我們來看一看該函數的參數:

hdc: “設備環境”的句柄。
lpString:要顯示的文本串,該文本串要麼以NULL結尾,要麼在nCount中指出它的長短。
nCount:要輸出的文本的長度。若以NULL結尾,該參數必須是-1。
lpRect: 指向要輸出文本串的正方形區域的指針,該方形必須是一個裁剪區,也就是說超過該區域的字符將不能顯示。
uFormat:指定如何顯示。我們可以用 or 把以下標誌或到一塊:
DT_SINGLELINE:是否單行顯示。
DT_CENTER:是否水平居中。
DT_VCENTER :是否垂直居中。


結束繪製後,必須調用 EndPaint 釋放“設備環境”的句柄。 好了,現在我們把“繪製”文本串的要點總結如下:

必須在開始和結束處分別調用 BeginPaint 和 EndPaint;
在 BeginPaint 和 EndPaint 之間調用所有的繪製函數;
如果在其它的消息處理中重新繪製客戶區,您可以有兩種選擇:
(1)用GetDC和ReleaseDC代替BeginPaint和EndPaint;
(2)調用InvalidateRect或UpdateWindow讓客戶區無效,這將迫使WINDOWS把WM_PAINT放入應用程序消息隊列,從而使得客戶區重繪。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章