We seem to have quite a few newbies here, which is a good thing. I'm posting this for them, and for any other members who might not have a good skeleton program.
A skeleton is a ready-made template you can open and use to create a new assembly-language program from, without having to reinvent the wheel each time. I'm guessing that most, if not all, of the longtime programmers here have such a file they use regularly.
Here's mine. It's a basic starting point for a Windows GUI (graphical user interface) program. It creates a window with a status bar, centers the window on the screen, and that's about it. It's up to you to add stuff to it, to make it stand up and do tricks. (The status bar is very handy for displaying debugging messages.)
This is my preferred coding style; it may not be yours. If this isn't to your liking, then go and create a template that you can live with. It'll save you a lot of time and trouble.
My style:
- Global variables start with a CapitalLetter
- Local variables start with a lowercaseLetter
- Symbolic constants (EQUates) start with a $dollarSign
- Lots of comments! Think about revisiting your code in 6 months or a year
;============================================
; -- Skeleton --
; -- [project title here] --
;
;============================================
.nolist
include \masm32\include\masm32rt.inc
.list
;============================================
; Defines, prototypes, etc.
;============================================
$mainWinWidth EQU 600
$mainWinHeight EQU 500
;===== Window styles: =====
; Change 1st 2 styles to "WS_OVERLAPPEDWINDOW" if you want a resizeable window:
$mainWinStyles EQU WS_CAPTION or WS_SYSMENU or WS_CLIPCHILDREN or WS_VISIBLE
;===== Window background colors: =====
$bkRED EQU 254
$bkGRN EQU 243
$bkBLUE EQU 199
$BkColor EQU $bkRED OR ($bkGRN SHL 8) OR ($bkBLUE SHL 16)
$SBID EQU 1111
$SBX EQU 0
$SBY EQU $mainWinHeight - $SBHeight
$SBWidth EQU $mainWinWidth
$SBHeight EQU 40
$numStatusParts EQU 4
$SB_styles EQU WS_CHILD OR WS_VISIBLE
;============================================
; HERE BE DATA
;============================================
.data
WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm
StatusParts DD 50, 150, 250, -1
NullString DB 0
MainClassName DB "[class name here]", 0
MainTitleText DB "[window title here]", 0
;============================================
; UNINITIALIZED DATA
;============================================
.data?
MainWinHandle HWND ?
InstanceHandle HINSTANCE ?
StatusHandle HWND ?
SBheight DD ?
;============================================
; CODE LIVES HERE
;============================================
.code
start: INVOKE GetModuleHandle, NULL
MOV InstanceHandle, EAX
CALL WinMain
INVOKE ExitProcess, EAX
;====================================================================
; Mainline proc
;====================================================================
WinMain PROC
LOCAL msg:MSG, brush:HBRUSH, wX:DWORD, wY:DWORD, gpRect:RECT
; Create brush to set background color:
INVOKE CreateSolidBrush, $BkColor
MOV brush, EAX
; Register class for parent window:
MOV EAX, InstanceHandle
MOV WC.hInstance, EAX
MOV WC.lpfnWndProc, OFFSET MainWindowProc
MOV EAX, brush
MOV WC.hbrBackground, EAX
MOV WC.lpszClassName, OFFSET MainClassName
; INVOKE LoadIcon, InstanceHandle, 500 ; icon ID
; MOV WC.hIcon, EAX
MOV WC.hIcon, NULL
INVOKE LoadCursor, NULL, IDC_ARROW
MOV WC.hCursor, EAX
INVOKE RegisterClassEx, OFFSET WC
INVOKE GetSystemMetrics, SM_CXSCREEN
MOV EDX, $mainWinWidth
CALL CenterDim
MOV wX, EAX
INVOKE GetSystemMetrics, SM_CYSCREEN
MOV EDX, $mainWinHeight
CALL CenterDim
MOV wY, EAX
; Create our main window:
INVOKE CreateWindowEx, WS_EX_OVERLAPPEDWINDOW, OFFSET MainClassName,
OFFSET MainTitleText, $mainWinStyles, wX, wY, $mainWinWidth,
$mainWinHeight, NULL, NULL, InstanceHandle, NULL
MOV MainWinHandle, EAX
; Create the status bar:
INVOKE CreateStatusWindow, $SB_styles, OFFSET NullString, MainWinHandle, $SBID
MOV StatusHandle, EAX
MOV EDX, EAX
; Get the height of the status bar:
INVOKE GetWindowRect, EDX, ADDR gpRect
MOV EAX, gpRect.bottom
SUB EAX, gpRect.top
MOV SBheight, EAX
; Divide status bar into parts:
INVOKE SendMessage, StatusHandle, SB_SETPARTS, $numStatusParts, OFFSET StatusParts
;============= Message loop ===================
msgloop:
INVOKE GetMessage, ADDR msg, NULL, 0, 0
TEST EAX, EAX ;EAX = 0 = exit
JZ exit99
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
JMP msgloop
exit99: MOV EAX, msg.wParam
RET
WinMain ENDP
;====================================================================
; Main Window Proc
;====================================================================
MainWindowProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL gpRect:RECT
MOV EAX, uMsg
; CMP EAX, WM_COMMAND
; JE do_command
CMP EAX, WM_CREATE
JE do_create
CMP EAX, WM_CLOSE
JE do_close
CMP EAX, WM_SIZE
JE dosize
dodefault:
; use DefWindowProc for all other messages:
INVOKE DefWindowProc, hWin, uMsg, wParam, lParam
RET
do_command:
; MOV AX, WORD PTR wParam
; CMP AX, ---- ;Check command code here.
; JE ---
do_create:
; Window creation code here
XOR EAX, EAX
RET
dosize:
INVOKE GetClientRect, hWin, ADDR gpRect
MOV EDX, gpRect.bottom
SUB EDX, gpRect.top
SUB EDX, SBheight
INVOKE MoveWindow, StatusHandle, 0, EDX, gpRect.right, SBheight, TRUE
XOR EAX, EAX
RET
do_close:
INVOKE PostQuitMessage, NULL
MOV EAX, TRUE
RET
MainWindowProc ENDP
;====================================================================
; CenterDim
;
; Returns half screen dimension (X or Y) minus half window dimension
;
; On entry,
; EAX = screen dimension
; EDX = window dimension
;
; Returns:
; sdim/2 - wdim/2
;====================================================================
CenterDim PROC
SHR EAX, 1 ;divide screen dimension by 2
SHR EDX, 1 ;divide window dimension by 2
SUB EAX, EDX
RET ;Return w/# in EAX
CenterDim ENDP
END start
Attached another version, with a menu, an edit control and a statusbar but shorter and different in style. Inter alia, it uses the Switch ... Case syntax in the WndProc:
WndProc proc uses esi edi ebx hWnd, uMsg, wParam:WPARAM, lParam:LPARAM
SWITCH uMsg
CASE WM_CREATE ; this message serves to initialise your application
mov hStatusbar, rv(CreateStatusWindow, WS_CHILD OR WS_VISIBLE, chr$("I'm the status bar"), hWnd, 110)
CASE WM_COMMAND ; react to menu clicks
... (see attachment)
CASE WM_DESTROY ; bye bye
invoke PostQuitMessage, NULL
ENDSW
invoke DefWindowProc, hWnd, uMsg, wParam, lParam ; default processing
ret
WndProc endp
The WM_SIZE handler demonstrates the difference between the invoke and push-push-call styles of coding.
BTW, I also have skeletons for console apps (command-line invoked, no GUI window) and dialog-as-program apps, where the main window is a dialog. You might want to whip up a few different flavors yourself.
thanks NoCforMe and jj, this a great idea :thumbsup:
Great noCforme
Prefer global handles in my code,
For example Hdc so i can use GDI in several proc
Used also ddraw and d3d templates many years ago
Here is an alternative WM_SIZE handler for the template attached in reply #1:
CASE WM_SIZE ; adjust edit control to main window
invoke SendMessage, hStatusbar, WM_SIZE, wParam, lParam ; autosize
; **** adjust size of edit control ****
movzx eax, word ptr lParam ; width of client area
movzx edx, word ptr lParam+2 ; height
sub eax, 6 ; width minus 3 pixels to the left and right
sub edx, 26 ; leave space under edit control for the statusbar
invoke MoveWindow, hEdit, 3, 3, eax, edx, 0
In this version, the WM_SIZE message is passed on to the statusbar child window, forcing it to resize.
best with ddraw skeleton it only produces 2.5kb exe,so 1.5kb room for a 4k demo
NoCforMe, no need "MOV WC.hIcon, NULL" because "NULL, \ ;hIcon":
.data
WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm
:bgrin:
@KSS: save some bytes
wcx WNDCLASSEX <WNDCLASSEX, CS_HREDRAW or CS_VREDRAW, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
txClass db "Masm32GUI", 0 ; class name, will be registered below
...
wc equ [ebx.WNDCLASSEX] ; we use an equate for better readability
mov ebx, offset wcx
mov wc.hInstance, rv(GetModuleHandle, 0) ; rv ("return value") is a Masm32 macro
mov wc.hIcon, rv(LoadIcon, eax, IDI_APPLICATION)
mov wc.hIconSm, eax ; the rv macro returns results in eax
mov wc.hCursor, rv(LoadCursor, NULL, IDC_ARROW) ; get a cursor
invoke RegisterClassEx, addr wc ; the window class needs to be registered
Quote from: KSS on December 21, 2023, 08:30:23 PMNoCforMe, no need "MOV WC.hIcon, NULL" because "NULL, \ ;hIcon":
.data
WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm
:bgrin:
Yes, you're correct of course; good catch.
Some of my templates are works in progress, with previous changes not completed. Maybe someday they'll be done. (Not likely ...)
Every little bit helps.