Hello.
I'm a noob at assembly. I am having some difficulties understanding both concept and code.
I have a homework assignment which states that has three aspects:
Create a window that will...
1. write text to a point where a mouse click is started
2. draw rectangles and ellipses with different color choices
3. Send a message Box to the user
I have done all of these separately, but I do not know how to incorporate these functions into the same window/ program.
I can sort of do 1 or 2, but only if bitblt is commented out. I should be able to select "Text" from the menu, click and area, start typing, then click another area, and start a new text string.
I'm thinking it must be something to do with a DC? where that I type should be sent to the same handle as the bitmap? Then I would have to know how to erase the Keyboard String or make a loop such that the old one remains existing when I attempt to make a new textbox.
Any guidance will be much appreciated. Here is the code that I've been trying to manipulate thus far:
.386
.model flat,stdcall
option casemap:none
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 ; initialized data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
ColorName db "New Color ",0
ColorName0 db "New is Blue ",0
ColorName1 db "New is Green ",0
ColorName2 db "New is Red ",0
ColorName3 db "New is White ",0
MouseClick db 0
char WPARAM 20h ; the character the program receives from keyboard
Count db 0
.DATA? ;unitialized data
hInstance HINSTANCE ? ;instance handle of our program
CommandLine LPSTR ?
hWnd HWND ? ;Handle to the edit control
maxX DD ?
maxY DD ?
ShapeNumber dd ?
startpoint POINT <>
endpoint POINT <>
hMemDC HDC ?
Bitmap HBITMAP ?
Brush HBRUSH ?
BrushColor dd ?
CharAsByte BYTE 1 Dup(?) ; *
KeyboardString BYTE 128 Dup(?) ; *
.CODE ;here begins our code
start:
invoke GetModuleHandle, NULL ;get the instance handle of our program
mov hInstance,eax
invoke GetCommandLine ;get the command line
mov CommandLine,eax
invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT ;call the main function
invoke ExitProcess,eax ;quit our program. The exit code is returned in eax from WinMain.
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc: WNDCLASSEX ;create local variables on stack
LOCAL msg: MSG
LOCAL hwnd: HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ;create local variables on stack
mov wc.style,CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
push hInstance
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 ;register our window class
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 LoadMenu,hInst,600 ;load resource menu
invoke SetMenu,hwnd,eax ;set it to main window
invoke ShowWindow,hwnd,CmdShow ;display our window on desktop
invoke UpdateWindow,hwnd ;refresh the client area
mov ShapeNumber, 1303 ;text
.WHILE TRUE ;Enter message loop
invoke GetMessage, ADDR msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ;return exit code in eax
ret
WinMain endp
WndProc proc hwnd:HWND, uMsg:UINT, wParam:WPARAM, IParam:LPARAM
LOCAL hdc:HDC
LOCAL ps: PAINTSTRUCT
.IF uMsg==WM_DESTROY ;if the user closes our window
invoke PostQuitMessage,NULL ;quit our application
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax, IParam
and eax, 0FFFFh
mov startpoint.x, eax
mov eax, IParam
shr eax,16
mov startpoint.y, eax
ret
.ELSEIF uMsg==WM_LBUTTONUP
mov eax, IParam
and eax, 0FFFFh
mov endpoint.x, eax
mov eax, IParam
shr eax, 16
mov endpoint.y, eax
invoke CreateSolidBrush, BrushColor
mov Brush, eax
invoke SelectObject, hMemDC, Brush
.IF ShapeNumber==1302
invoke Rectangle, hMemDC, startpoint.x, startpoint.y, endpoint.x, endpoint.y
invoke InvalidateRect, hwnd, NULL, TRUE
.ELSEIF ShapeNumber==1301
invoke Ellipse, hMemDC, startpoint.x, startpoint.y, endpoint.x, endpoint.y
invoke InvalidateRect, hwnd, NULL, TRUE
.ELSEIF ShapeNumber==1303
invoke lstrlen, ADDR ColorName
invoke TextOut, hMemDC, startpoint.x, startpoint.y, ADDR ColorName, eax
invoke InvalidateRect, hwnd, NULL, TRUE
.ENDIF
.ELSEIF uMsg==WM_COMMAND
.if wParam==1103
invoke SetTextColor,hMemDC,00ff0000h
mov BrushColor, 0ff0000h
invoke lstrlen, ADDR ColorName0
invoke TextOut, hMemDC, 1, 1, ADDR ColorName0, eax
invoke InvalidateRect, hwnd, NULL, TRUE
ret
.elseif wParam==1102
invoke SetTextColor,hMemDC, 0000ff00h
mov BrushColor, 000ff00h
invoke lstrlen, ADDR ColorName1
invoke TextOut, hMemDC, 1, 1, ADDR ColorName1, eax
invoke InvalidateRect, hwnd, NULL, TRUE
ret
.elseif wParam==1101
invoke SetTextColor, hMemDC, 000000ffh
mov BrushColor, 00000ffh
invoke lstrlen, ADDR ColorName2
invoke TextOut, hMemDC, 1, 1, ADDR ColorName2, eax
invoke InvalidateRect, hwnd, NULL, TRUE
ret
.elseif wParam==1301
mov ShapeNumber, 1301
.elseif wParam==1302
mov ShapeNumber, 1302
.elseif wParam==1303
mov ShapeNumber, 1303
invoke DrawText, hdc, startpoint.x, startpoint.y, ADDR KeyboardString, eax
invoke InvalidateRect, hwnd, NULL, TRUE
.endif
.ELSEIF uMsg==WM_CHAR
.IF wParam==01bh ; * for esc
nop
.ELSEIF wParam==008h
mov eax, 00000000h
mov al, Count
mov esi, eax
mov bl, KeyboardString[esi]
.IF Count > 0
dec Count
mov eax, 00000000h
mov al, Count
mov esi, eax
mov KeyboardString[esi], bl
.ENDIF
invoke InvalidateRect, hWnd, NULL, TRUE
.ELSE
GET_BYTE:
push wParam
pop char
mov eax, char
mov CharAsByte, al
ADD_TO_STRING:
mov eax, 00000000h
mov al, Count
mov esi, eax
mov bl, CharAsByte
mov KeyboardString[esi], bl
inc Count
invoke InvalidateRect, hWnd,NULL,TRUE
.ENDIF
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint, hwnd, ADDR ps
mov hdc, eax
invoke lstrlen, ADDR KeyboardString
invoke TextOut, hdc, startpoint.x, startpoint.y, ADDR KeyboardString, eax
;invoke BitBlt, hdc, 0, 0, maxX, maxY, hMemDC, 0, 0, SRCCOPY
invoke EndPaint, hwnd, ADDR ps
.ELSEIF uMsg==WM_CREATE
invoke GetSystemMetrics, SM_CXSCREEN
mov maxX, eax
invoke GetSystemMetrics, SM_CYSCREEN
mov maxY, eax
invoke GetDC, hwnd
mov hdc, eax
invoke CreateCompatibleDC, hdc
mov hMemDC, eax
invoke CreateCompatibleBitmap, hdc, maxX, maxY
mov Bitmap, eax
mov hdc, eax
invoke SelectObject, hMemDC, Bitmap
invoke PatBlt, hMemDC, 0, 0, maxX, maxY, PATCOPY
invoke SetTextColor, hMemDC, 00ff0000h
mov BrushColor, 0ff0000h
;invoke lstrlen, ADDR ColorName
invoke TextOut, hMemDC, 1, 1, ADDR ColorName, eax
invoke InvalidateRect, hwnd, NULL, TRUE
ret
.ELSE
invoke DefWindowProc, hwnd, uMsg, wParam, IParam ;default message processing
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
The general drift is to create a compatible device context, draw what you want to it in the sequence you require THEN BitBlt it to the coordinates of the window's client area that you want.
the message box is easiest :biggrin:
INCLUDE \masm32\include\masm32rt.inc
.DATA
szTitle db 'Title String',0
szMsg db 'Message String',0
.CODE
WinMain PROC
INVOKE MessageBox,0,offset szMsg,offset szTitle,MB_OK
INVOKE ExitProcess,eax
WinMain ENDP
END WinMain
the rest of it involves device contexts, as you say
there is a lot of material to read when it comes to painting and drawing
and, it isn't really easy to understand, either
it's late at night, here, and i am about to turn in
but, tomorrow, we can play a little more
i took a quick glance at your code, and it does appear to have a few basic problems
for one thing, whenever you create a DC or other type of GDI object, you should delete it when done
for brushes, pens, bitmaps, etc, we use DeleteObject
for DC's, we generally use DeleteDC, although DeleteObject is supposed to work
when you create a DC and select an object into it, SelectObject returns the handle of the original - you should save it for later
before you delete a DC, you should select the original objects back into it
Thank you both for the responses. At least now I have a starting point to begin fixing up my code. :greenclp:
; masm windows gui #
.586p
.model flat
include windows.inc
includelib kernel32.lib
includelib user32.lib
includelib gdi32.lib
extern _imp__LoadImageA@24:dword
extern _imp__ExitProcess@4:dword
extern _imp__CreatePatternBrush@4:dword
extern _imp__RegisterClassA@4:dword
extern _imp__CreateWindowExA@48:dword
extern _imp__GetMessageA@16:dword
extern _imp__DispatchMessageA@4:dword
extern _imp__DefWindowProcA@16:dword
extern _imp__BeginPaint@8:dword
extern _imp__EndPaint@8:dword
extern _imp__TextOutA@20:dword
extern _imp__SetBkMode@8:dword
.data
hDC dd ?
wTitle db "tweety78.bmp",0
Msg db "Hi, Haleakala!"
num = $ - Msg
.code
;---------------------------------------------------------
start: xor ebx,ebx
mov esi,400000h
mov edi,offset wTitle
;------------------------------
; registering the window class
;------------------------------
push edi
push ebx
push LR_LOADFROMFILE
push ebx
push ebx
push ebx
push edi
push esi
call _imp__LoadImageA@24
push eax
call _imp__CreatePatternBrush@4
push eax
push 10011h
push ebx
push esi
push ebx
push ebx
push offset window_procedure
push ebx
push esp
call _imp__RegisterClassA@4
;--------------------------+
; creating the main window |
;--------------------------+
push ebx
push esi
shl esi,9
push ebx
push ebx
push 305
push 286
push esi
push esi
push WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE
push edi
push edi
push ebx
call _imp__CreateWindowExA@48
mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
message_loop:push ebx
push ebx
push ebx
push ebp
call _imp__GetMessageA@16
push ebp
call _imp__DispatchMessageA@4
jmp message_loop
;----------------------+
; the window procedure |
;----------------------+
window_procedure:
hWnd equ [ebp+8]
uMsg equ [ebp+0Ch]
enter sizeof(PAINTSTRUCT),0
mov eax,uMsg
mov edi,hWnd
dec eax
dec eax
je short wmDESTROY
sub eax,WM_PAINT-WM_DESTROY
je wmPAINT
leave
jmp _imp__DefWindowProcA@16
wmDESTROY: push ebx
call _imp__ExitProcess@4
wmPAINT: push esp
push edi
call _imp__BeginPaint@8
push num
push offset Msg
push 100
push ebx
push eax
push TRANSPARENT
push eax
call _imp__SetBkMode@8
call _imp__TextOutA@20
push esp
push edi
call _imp__EndPaint@8
leave
retn 10h
end start
ouch !
there is a little better way to terminate the program - i didn't look at the rest of the code
after the GetMessage call in the message loop, test for a 0 or -1 result in EAX, any other value, continue looping
the -1 value doesn't seem to be very likely - a lot of programmers just tests for 0
if it is 0 or -1, call ExitProcess with the exit code value from msg.wParam
to signal the loop to exit, use PostQuitMessage in WM_DESTROY
Quoteto signal the loop to exit, use PostQuitMessage in WM_DESTROY
I know it, but this method to complete the program I used many times in Windows XP and Seven, and everything works fine!
Let's look closely the entire program - there are still things to marvel :)
ok, marveling.....
.....done