Download the example here.
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include
\masm32\include\windows.inc
include
\masm32\include\user32.inc
include
\masm32\include\kernel32.inc
include
\masm32\include\gdi32.inc
includelib
\masm32\lib\user32.lib
includelib
\masm32\lib\kernel32.lib
includelib
\masm32\lib\gdi32.lib
.data
ClassName
db "SimpleWinClass",0
AppName
db "Our First Window",0
char
WPARAM 20h
; the character the program receives from keyboard
.data?
hInstance
HINSTANCE ?
CommandLine
LPSTR ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain
proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain
endp
WndProc
proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke TextOut,hdc,0,0,ADDR char,1
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc
endp
end
start
char
WPARAM 20h
; the character the program receives from keyboard
This is the variable that will store the character received from the keyboard. Since the character is sent in WPARAM of the window procedure, we define the variable as type WPARAM for simplicity. The initial value is 20h or the space since when our window refreshes its client area the first time, there is no character input. So we want to display space instead.
.ELSEIF uMsg==WM_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
This is added in the window procedure to handle the WM_CHAR message. It just puts the character into the variable named "char" and then calls InvalidateRect. InvalidateRect makes the specified rectangle in the client area invalid which forces Windows to send WM_PAINT message to the window procedure. Its syntax is as follows:
InvalidateRect
proto hWnd:HWND,\
lpRect:DWORD,\
bErase:DWORD
lpRect
is a pointer to the rectagle in the client area that we want to declare
invalid. If this parameter is null, the entire client area will be marked
as invalid.
bErase
is a flag telling Windows if it needs to erase the background. If this
flag is TRUE, then Windows will erase the backgroud of the invalid rectangle
when BeginPaint is called.
So
the strategy we used here is that: we store all necessary information relating
to painting the client area and generate WM_PAINT message to paint the
client area. Of course, the codes in WM_PAINT section must know beforehand
what's expected of them. This seems a roundabout way of doing things but
it's the way of Windows.
Actually
we can paint the client area during processing WM_CHAR message by calling
GetDC and ReleaseDC pair. There is no problem there. But the fun begins
when our window needs to repaint its client area. Since the codes that
paint the character are in WM_CHAR section, the window procedure will not
be able to repaint our character in the client area. So the bottom line
is: put all necessary data and codes that do painting in WM_PAINT. You
can send WM_PAINT message from anywhere in your code anytime you want to
repaint the client area.
invoke TextOut,hdc,0,0,ADDR char,1
When
InvalidateRect is called, it sends a WM_PAINT message back to the window
procedure. So the codes in WM_PAINT section is called. It calls BeginPaint
as usual to get the handle to device context and then call TextOut which
draws our character in the client area at x=0, y=0. When you run the program
and press any key, you will see that character echo in the upper left corner
of the client window. And when the window is minimized and maximized again,
the character is still there since all the codes and data essential to
repaint are all gathered in WM_PAINT section.