| ||
Examples are essential for studying programming. I also learned on the examples.
In this chapter, I start with a detailed description of the WM_PAINT message. In Chapter 3, you considered this message but didn't use it. This was because the program window contained only controlsno text information or graphics. Now, I am going to correct this situation. Besides the WM_PAINT message, you will also consider various problems related to Windows programming.
Listing 7.1 contains an example of a simple Windows program. If you only start to develop Windows applications, you'll find lots of new ideas in this listing. Therefore, start with a detailed description of this program.
In this program, the colors of the window background and text are defined on the basis of combinations of three colors: red, green, and blue. The color is set by a 32-bit number. The first byte specifies the intensity of red, the second byte specifies the intensity of green, and the third byte specifies the intensity of blue. The last byte is set to zero. The mechanism of generating this number is illustrated by the definition of the RGBW constant.
Window background color is set by defining a brush through the CreateSolidBrush function. A brush is a bitmap used by the system for filling solid areas.
Because the WM_PAINT message is sent by the system when redrawing window, the contents of the program window must be redrawn when receiving this message.
This program outputs only one text string. To output this information into the window, it is first necessary to get the window context (also known as device context). From a programmer's point of view, this device context is simply a number used for ensuring communication between a program and its window As a rule, the device context is defined by calling the GetDC function. When receiving the WM_PAINT message, the program acquires the device context by calling the BeginPaint function. The argument of this function is the pointer to the special structure PAINTSTR; however, the fields of this structure are not used in this program.
I hope that you have already guessed on the basis of the program listingtext output is carried out using the OutText function. Before calling this function, you define the background and foreground colors by calling the SetBkColor and setTextColor functions. Background color, naturally, is the same as window color.
Now, it is necessary to pay attention to the coordinate system. Its center is in the top left corner of the window, the Y-axis is directed downward, and the X-axis is directed rightward. This variant is commonly adopted for graphical screens.
Another important aspect relates to text output into the window. The length of the string that must be displayed in the window is one of parameters of the OutText function. At this point, there are several important aspects that should be noted There are several methods of determining the output string length (minus the trailing 0). For example, you can use the SIZEOF or LENGTHOF operators of a macroassembler. However, Turbo Assembler lacks these operators. It is possible to resolve this discrepancy by placing a label in the end of the string or by using obsolete LENGTH and SIZE directives. However, I hope that you have already grasped the idea that to ensure compatibility and portability between MASM32 and TASM32, it is wise to avoid using macros. Moreover, because you are using string definitions according to C language conventions, it would be natural to define string functions {see the note at the end of this chapter). In this example, I have defined the function that returns the string length. Don't be confused because this function returns the result into the EBX register. It is simply more convenient . Besides, functions have another important advantage over macro tools, namely, they get the string length at run time instead of compile time.
Now, to ensure that this program can be compiled using Turbo Assembler, it is necessary to carry out the same manipulations as before, namely, to delete all @N suffixes and to include the IMPORT32.LIB library.
; The TEXT1.INC file ; Constants ; The message arrives when the window is closed WM_DESTROY equ 2 ; The message arrives when the window is created WM_CREATE equ 1 ; The message arrives when the window must be redrawn WM_PAINT equ 0Fh ; Window properties CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H Stylcl equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS DX0 equ 300 DY0 equ 200 ; Color components RED equ 50 GREEN equ 50 BLUE equ 255 RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16) RGBT equ 255 ; Red ; Standard icon identifier IDI_APPLICATION equ 32512 ; Cursor identifier IDC_CROSS equ 32515 ; Normal display mode SW_SHOWNORMAL equ 1 ; Prototypes of external procedures EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN BeginPaint@8:NEAR EXTERN EndPaint@8:NEAR EXTERN TextOutA@20:NEAR EXTERN GetStockObject@4:NEAR EXTERN CreateSolidBrush@4:NEAR EXTERN SetBkColor@8:NEAR EXTERN SetTextColor@8:NEAR ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? ; Identifier of the window ; that receives the message MSMESSAGE DD ? ; Message identifier MSWPARAM DD ? ; Additional information about the message MSLPARAM DD ? ; Additional information about the message MSTIME DD ? ; Time the message was sent MSPT DD ? ; Cursor position when this message was sent MSGSTRUCT ENDS ;-------- WNDCLASS STRUC CLSSTYLE DD ? ; Window style CLSLPFNWNDPROC DD ? ; Pointer to the window procedure CLSCBCLSEXTRA DD ? ; Information about auxiliary bytes ; for this structure CLSCBWNDEXTRA DD ? ; Information about auxiliary bytes ; for the window CLSHINSTANCE DD ? ; Application descriptor CLSHICON DD ? ; Window icon identifier CLSHCURSOR DD ? ; Window cursor identifier CLSHBRBACKGROUND DD ? ; Window brush identifier MENNAME DD ? ; Identifier of the menu name CLSNAME DD ? ; Specifies the name for the window class WNDCLASS ENDS ;-------- PAINTSTR STRUC hdc DWORD 0 fErase DWORD 0 left DWORD 0 top DWORD 0 right DWORD 0 bottom DWORD 0 fRes DWORD 0 fIncUp DWORD 0 Reserv DB 32 dup(0) PAINTSTR ENDS ; The TEXT1.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall ;--------------------------------------- include textl.inc ; Connecting libraries includelib c:\masm32 lib\user32.lib includelib c:\masm32 lib\Kernel32.lib includelib c:\masm32 lib'\gdi32.lib ;--------------------------------------- ; Data segment _DATA SEGMENT NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> PNT PAINTSTR <?> HINST DD 0 TITLENAME DB 'Window text', 0 NAM DB 'CLASS32', 0 XT DWORD 30 YT DWORD 30 TEXT DB 'Text in the window is red', 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; Fill the window structure ; Style MOV [WC.CLSSTYLE], stylcl ; Message-handling procedure MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEXTRA], 0 MOV [WC.CLSCBWNDEXTRA], 0 MOV EAX,[HINST] MOV [WC.CLSHINSTANCE], EAX ; Window icon PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;--------- Window cursor PUSH IDC_CROSS PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;--------- PUSH RGBW ; Brush color CALL CreateSolidBrush@4 ; Create a brush MOV [WC.CLSHBRBACKGROUND], EAX MOV DWORD PTR [WC.MENNAME], 0 MOV DWORD PTR [WC.CLSNAME], OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ; Create a window of the registered class PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DY0 - Window height PUSH DX0 ; DX0 - Window width PUSH 100 ; The Y coordinate PUSH 100 ; The X coordinate PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; Window name PUSH OFFSET NAM ; Class name PUSH 0 CALL CreateWindowExA@48 ; Error check CMP EAX, 0 JZ _ERR MOV [NEWHWND], EAX ; Window descriptor ;------------------------ PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; Show the window that you have just created ;------------------------ PUSH [NEWHWND] CALL UpdateWindow@4 ; Redraw the visible part of the window ; Message-processing loop MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; Exit the program (terminate the process) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ; Window procedure ; Parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH], WM_PAINT JE WMPAINT JMP DEFWNDPROC WMPAINT: ;--------------- PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 PUSH EAX ; Save the context (descriptor) ;----------------Background color = window color PUSH RGBW PUSH EAX CALL SetBkColor@8 ;----------------Context POP EAX PUSH EAX ;----------------Text color (red) PUSH RGBT PUSH EAX CALL SetTextColor@8 ;----------------Context POP, EAX ;----------------Display text PUSH OFFSET TEXT CALL LENSTR PUSH EBX ; string length PUSH OFFSET TEXT ; String address PUSH YT ; Y PUSH XT ; X PUSH EAX ; Window context CALL TextOutA@20 ;----------------Close PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 MOV EAX, 0 JMP FINISH WMCREATE: MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: PUSH 0 CALL PostQuitMessage@4 ; WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP ;----------- Function -------------------------- ; String length [EBP+08H] Pointer to the string LENSTR PROC PUSH EBP MOV EBP, ESP PUSH ESI MOV ESI, DWORD PTR [EBP+8] XOR EBX, EBX LBL1: CMP BYTE PTR [ESI], 0 JZ LBL2 INC EBX INC ESI JMP LBL1 LBL2: POP ESI POP EBP RET 4 LENSTR ENDP _TEXT ENDS END START
Now consider another example illustrating text output into the window. Now your task is more complicated. Aim at ensuring that the text string always remains in the middle of the window no matter what happens to it. To achieve it, you'll need to know the window size in pixels and the string length. The string length in pixels is defined using the GetTextExtentPoint32 function; to determine the window size, use the GetwindowRect function. When doing so, you'll need structures such as SIZET and RECT . I hope you understand how to determine the string position if its length and window size are given. I'll only mention again that it's necessary to take into account the height of the window header.
; The TEXT2.INC file ; Constants ; The message arrives when the window is closed WM_DESTROY equ 2 ; The message arrives when the window is created WM_CREATE equ 1 ; The message arrives when the window is redrawn WM_PAINT equ 0Fh ; Window properties CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H stylcl equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS DX0 equ 300 DY0 equ 200 ; Color components RED equ 80 GREEN equ 80 BLUE equ 255 RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16) RGBT equ 00FF00H ; Green ; Standard icon identifier IDI_APPLICATION equ 32512 ; Cursor identifier IDC_CROSS equ 32515 ; Window display mode is normal SW_SHOWNORMAL equ 1 ; Prototypes of external procedures EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess04:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@ 8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN BeginPaint@8:NEAR EXTERN EndPaint@8:NEAR EXTERN TextOutA@20:NEAR EXTERN GetStock0bject@4:NEAR EXTERN CreateSolidBrush@4:NEAR EXTERN SetBkColor@8:NEAR EXTERN SetTextColor@8:NEAR EXTERN GetTextExtentPoint32A@16:NEAR EXTERN GetWindowRect@8:NEAR ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? ; Identifier of the window ; that receives the message MSMESSAGE DD ? ; Message identifier MSWPARAM DD ? ; Additional information about the message MSLPARAM DD ? ; Additional information about the message MSTIME DD ? ; Message sending time MSPT DD ? ; Cursor position when the message was sent MSGSTRUCT ENDS ;--------- WNDCLASS STRUC CLSSTYLE DD ? ; Window style CLSLPFNWNDPROC DD ? ; Pointer to the window procedure CLSCBCLSEXTRA DD ? ; Information about additional bytes for ; this structure CLSCBWNDEXTRA DD ? ; Information about additional bytes ; for the window CLSHINSTANCE DD ? ; Application descriptor CLSHICON DD ? ; Window icon identifier CLSHCURSOR DD ? ; Window cursor identifier CLSHBRBACKGROUND DD ? ; Window brush identifier MENNAME DD ? ; Menu identifier name CLSNAME DD ? ; Specifies the window class name WNDCLASS ENDS ;--------- PAINTSTR STRUC hdc DWORD 0 fErase DWORD 0 left DWORD 0 top DWORD 0 right DWORD 0 bottom DWORD 0 fRes DWORD 0 fIncUp DWORD 0 Reserv DB 32 dup(0) PAINTSTR ENDS ; --------------- SIZET STRUC X1 DWORD ? Yl DWORD ? SIZET ENDS RECT STRUC L DWORD ? ; XAbscissa of the top left corner T DWORD ? ; Y Ordinate of the top left corner R DWORD ? ; X Abscissa of the bottom right corner B DWORD ? ; Y Ordinate of the bottom right corner RECT ENDS ; The TEXT2.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall ;---------------------------------------------- include text2.inc ; Directive to the linker to include libraries c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ;---------------------------------------------- ; Data segment _DATA SEGMENT NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> PNT PAINTSTR <?> SZT SIZET <?> RCT RECT <?> HINST DD 0 TITLENAME DB 'text in the Window', 0 NAM DB 'CLASS32', 0 XT DWORD ? YT DWORD ? TEXT DB 'Text displayed in the window is green', 0 CONT DWORD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; Fill the window structure ; Style MOV [WC.CLSSTYLE], stylcl ; Message-handling procedure MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEXTRA], 0 MOV [WC.CLSCBWNDEXTRA], 0 MOV EAX,[HINST] MOV [WC.CLSHINSTANCE], EAX ;----------Window icon PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;---------- Window cursor PUSH IDC_CROSS PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;---------- PUSH RGBW ; Brush color CALL CreateSolidBrush@4 ; Create a brush MOV [WC.CLSHBRBACKGROUND], EAX MOV DWORD PTR [WC.MENNAME], 0 MOV DWORD PTR [WC.CLSNAME], OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ; Create a window of the registered class PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DY0 - Window height PUSH DX0 ; DX0 Window width PUSH 100 ; The Y coordinate PUSH 100 ; The X coordinate PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; Window name PUSH OFFSET NAM ; Class name PUSH 0 CALL CreateWindowExA@48 ; Check for error CMP EAX, 0 JZ _ERR MOV [NEWHWND], EAX ; Window descriptor ;-------------------------------- PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; Display the newly created window ;-------------------------------- PUSH [NEWHWND] CALL UpdateWindow@4 ; Redraw the visible part of the window ; Message processing MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; Exit the program (close the process) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;-------------------------------------------------------; window procedure ; Positions of the parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH], WM_PAINT JE WMPAINT JMP DEFWNDPROC WMPAINT: ;---------------- PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV CONT, EAX ; Save the context (descriptor) ;----------------Background color = window color PUSH RGBW PUSH EAX CALL SetBkColor@8 ;----------------Text color (red) PUSH RGBT PUSH CONT CALL SetTextColor@8 ; Compute text length in pixels PUSH OFFSET TEXT CALL LENSTR PUSH EBX ; Save the string length PUSH OFFSET SZT PUSH EBX PUSH OFFSET TEXT PUSH CONT CALL GetTextExtentPoint32A@16 ;----------------Window size PUSH OFFSET RCT PUSH DWORD PTR [EBP+8] CALL GetWindowRect@8 ;----------------Compute coordinates MOV EAX, RCT.R SUB EAX, RCT.L SUB EAX, SZT.X1 SHR EAX, 1 ; Text in the middle MOV XT, EAX MOV EAX, RCT.B SUB EAX, RCT.T SHR EAX, 1 SUB EAX, 25 ; Take the window header into account MOV YT, EAX ;----------------Display text ; String length in the stack PUSH OFFSET TEXT PUSH YT PUSH XT PUSH CONT CALL TextOutA@20 ;---------------- Close context PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 MOV EAX, 0 JMP FINISH WMCREATE: MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: PUSH 0 CALL PostQuitMessage@4 ; WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP ; String length ; String pointer is in the word located by the [EBP+8] address LENSTR PROC PUSH EBP MOV EBP, ESP PUSH ESI MOV ESI, DWORD PTR [EBP+8] XOR EBX, EBX LBL1: CMP BYTE PTR [ESI], 0 JZ LBL2 INC EBX INC ESI JMP LBL1 LBL2: POP ESI POP EBP RET 4 LENSTR ENDP _TEXT ENDS END START
I can't help admiring the possibilities that an assembler provides to programmers. You can pass parameters through the stack or, if desired, through registers. If you want to, you can save the registers' procedure beginning; if you don't want it, you can do without it. Assembler code can be improved beyond any perceivable limits of perfection . By the way, for the fans of string commands, I'll provide another procedure for determining the string length based on the SCAS microprocessor command. This command allows you to search for the required element (byte SCASB , word SCASW , and double word SCASD ) within a string.
; String length [EBP+08H] LENSTR PROC PUSH EBP MOV EBP, ESP PUSH EAX ;------------------ CLD MOV EDI, DWORD PTR [EBP+08H] MOV EBX, EDI MOV ECX, 100 ; Limit the string length XOR AL, AL REPNE SCASB ; Find symbol 0 SUB EDI, EBX ; String length, including 0 MOV EBX, EDI DEC EBX ; String length is saved here ;------------------ POP EAX POP EBP RET 4 LENSTR ENDP
| ||