; #########################################################################

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

; #########################################################################

      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

; #########################################################################

      ;=============
      ; Local macros
      ;=============

      szText MACRO Name, Text:VARARG
        LOCAL lbl
          jmp lbl
            Name db Text,0
          lbl:
        ENDM

      m2m MACRO M1, M2
        push M2
        pop  M1
      ENDM

      return MACRO arg
        mov eax, arg
        ret
      ENDM

        ;=================
        ; Local prototypes
        ;=================
        WinMain   PROTO :DWORD,:DWORD,:DWORD,:DWORD
        WndProc   PROTO :DWORD,:DWORD,:DWORD,:DWORD
        TopXY     PROTO :DWORD,:DWORD
        ListBox   PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        EnmProc   PROTO :DWORD,:DWORD
        Static    PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        ListProc  PROTO :DWORD,:DWORD,:DWORD,:DWORD
        EnumChilds PROTO :DWORD,:DWORD
        
    .data
        szDisplayName db "Enumerate Windows",0
        CommandLine   dd 0
        hWnd          dd 0
        hInstance     dd 0
        hList         dd 0
        hStat1        dd 0
        hStat2        dd 0
        lpfnListProc  dd 0

    .code

start:
        invoke GetModuleHandle, NULL
        mov hInstance, eax

        invoke GetCommandLine
        mov CommandLine, eax

        invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
        invoke ExitProcess,eax

; #########################################################################

WinMain proc hInst     :DWORD,
             hPrevInst :DWORD,
             CmdLine   :DWORD,
             CmdShow   :DWORD

        ;====================
        ; Put LOCALs on stack
        ;====================

        LOCAL wc   :WNDCLASSEX
        LOCAL msg  :MSG

        LOCAL Wwd  :DWORD
        LOCAL Wht  :DWORD
        LOCAL Wtx  :DWORD
        LOCAL Wty  :DWORD

        ;==================================================
        ; Fill WNDCLASSEX structure with required variables
        ;==================================================

        mov wc.cbSize,         sizeof WNDCLASSEX
        mov wc.style,          CS_HREDRAW or CS_VREDRAW \
                               or CS_BYTEALIGNWINDOW
        mov wc.lpfnWndProc,    offset WndProc
        mov wc.cbClsExtra,     NULL
        mov wc.cbWndExtra,     NULL
        m2m wc.hInstance,      hInst   ;<< NOTE: macro not mnemonic
        mov wc.hbrBackground,  COLOR_BTNFACE+1
        mov wc.lpszMenuName,   NULL
        mov wc.lpszClassName,  offset szClassName
          invoke LoadIcon,hInst,500    ; icon ID
        mov wc.hIcon,          eax
          invoke LoadCursor,NULL,IDC_ARROW
        mov wc.hCursor,        eax
        mov wc.hIconSm,        0

        invoke RegisterClassEx, ADDR wc

        ;================================
        ; Centre window at following size
        ;================================

        mov Wwd, 600
        mov Wht, 350

        invoke GetSystemMetrics,SM_CXSCREEN
        invoke TopXY,Wwd,eax
        mov Wtx, eax

        invoke GetSystemMetrics,SM_CYSCREEN
        invoke TopXY,Wht,eax
        mov Wty, eax

        szText szClassName,"Enumerator_Class"

        invoke CreateWindowEx,WS_EX_OVERLAPPEDWINDOW,
                              ADDR szClassName,
                              ADDR szDisplayName,
                              WS_OVERLAPPEDWINDOW,
                              Wtx,Wty,Wwd,Wht,
                              NULL,NULL,
                              hInst,NULL
        mov   hWnd,eax

        invoke EnumWindows,ADDR EnmProc,0

        invoke LoadMenu,hInst,600  ; menu ID
        invoke SetMenu,hWnd,eax

        invoke ShowWindow,hWnd,SW_SHOWNORMAL
        invoke UpdateWindow,hWnd

      ;===================================
      ; Loop until PostQuitMessage is sent
      ;===================================

    StartLoop:
      invoke GetMessage,ADDR msg,NULL,0,0
      cmp eax, 0
      je ExitLoop
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage,  ADDR msg
      jmp StartLoop
    ExitLoop:

      return msg.wParam

WinMain endp

; #########################################################################

WndProc proc hWin   :DWORD,
             uMsg   :DWORD,
             wParam :DWORD,
             lParam :DWORD

    LOCAL Rc      :RECT
    LOCAL rLeft   :DWORD
    LOCAL rTop    :DWORD
    LOCAL rRight  :DWORD
    LOCAL rBottom :DWORD

    .if uMsg == WM_COMMAND
    ;======== menu commands ========
        .if wParam == 1000
            invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL
        .elseif wParam == 1001
            invoke SendMessage,hList,LB_RESETCONTENT,0,0
            invoke EnumWindows,ADDR EnmProc,0
        .elseif wParam == 1900
            szText TheMsg,"Assembler, Pure & Simple"
            invoke MessageBox,hWin,ADDR TheMsg,ADDR szDisplayName,MB_OK
        .endif
    ;====== end menu commands ======

    .elseif uMsg == WM_CREATE
      invoke ListBox,20,20,550,200,hWin,600
      mov hList, eax

      invoke SetWindowLong,hList,GWL_WNDPROC,ListProc
      mov lpfnListProc, eax

      jmp @F
          lbl1 db " hWnd",0
          lbl2 db " Window Class Name",0
      @@:

      invoke Static,ADDR lbl1,hWin,20,5,52,18,500
      invoke Static,ADDR lbl2,hWin,95,5,160,18,501

    .elseif uMsg == WM_SIZE

      invoke GetClientRect,hWin,ADDR Rc

        m2m rLeft, Rc.left
        add rLeft, 20

        m2m rTop, Rc.top
        add rTop, 25

        m2m rRight, Rc.right
        sub rRight, 40

        m2m rBottom, Rc.bottom
        sub rBottom, 45
        
      invoke MoveWindow,hList,rLeft,rTop,rRight,rBottom,TRUE

    .elseif uMsg == WM_CLOSE
        invoke SendMessage,hWin,WM_DESTROY,NULL,NULL
    .elseif uMsg == WM_DESTROY
        invoke PostQuitMessage,NULL
        return 0 
    .endif

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

    ret

WndProc endp

; ########################################################################

TopXY proc wDim:DWORD, sDim:DWORD

    shr sDim, 1      ; divide screen dimension by 2
    shr wDim, 1      ; divide window dimension by 2
    mov eax, wDim    ; copy window dimension into eax
    sub sDim, eax    ; sub half win dimension from half screen dimension

    return sDim

TopXY endp

; ########################################################################

ListBox proc a:DWORD,b:DWORD,wd:DWORD,ht:DWORD,hParent:DWORD,ID:DWORD

    LOCAL hFont :DWORD
    LOCAL hLst  :DWORD

    szText lstBox,"LISTBOX"

    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR lstBox,0,
              WS_VSCROLL or WS_VISIBLE or \
              WS_BORDER or WS_CHILD or \
              LBS_HASSTRINGS or LBS_NOINTEGRALHEIGHT or \
              LBS_DISABLENOSCROLL,
              a,b,wd,ht,hParent,ID,hInstance,NULL

    mov hLst, eax

    invoke GetStockObject,ANSI_FIXED_FONT
    mov hFont, eax
    invoke SendMessage,hLst,WM_SETFONT,hFont, 0

    mov eax, hLst

    ret

ListBox endp

; #########################################################################

EnmProc proc eHandle :DWORD, y :DWORD

    LOCAL Buffer[256]:BYTE
    LOCAL clName[64] :BYTE

    invoke GetClassName,eHandle,ADDR clName,64

    szText ctlstr,"%-2.5lu    %s"
    invoke wsprintf,ADDR Buffer,ADDR ctlstr,eHandle,ADDR clName

    invoke SendMessage,hList,LB_ADDSTRING,0,ADDR Buffer
    invoke EnumChildWindows,eHandle,ADDR EnumChilds,0
    mov eax, eHandle
    ret

EnmProc endp

EnumChilds proc dHandle :DWORD, z :DWORD
LOCAL Buffer2[256] :BYTE
LOCAL clName2[64]  :BYTE

invoke GetClassName,dHandle,ADDR clName2,64
szText ctlstr2,"%-2.5lu    ----> %s"
invoke wsprintf,ADDR Buffer2,ADDR ctlstr2,dHandle,ADDR clName2
invoke SendMessage,hList,LB_ADDSTRING,0,ADDR Buffer2
mov eax,dHandle
ret
EnumChilds endp
; #########################################################################

Static proc lpText:DWORD,hParent:DWORD,
                 a:DWORD,b:DWORD,wd:DWORD,ht:DWORD,ID:DWORD

    LOCAL hStat :DWORD
    LOCAL hFont :DWORD

    szText statClass,"STATIC"

    invoke CreateWindowEx,WS_EX_STATICEDGE,
            ADDR statClass,lpText,
            WS_CHILD or WS_VISIBLE or SS_LEFT,
            a,b,wd,ht,hParent,ID,
            hInstance,NULL

    mov hStat, eax

    invoke GetStockObject,ANSI_FIXED_FONT
    mov hFont, eax
    invoke SendMessage,hStat,WM_SETFONT,hFont, 0

    mov eax, hStat

    ret

Static endp

; ########################################################################

ListProc proc hCtl   :DWORD,
              uMsg   :DWORD,
              wParam :DWORD,
              lParam :DWORD

    LOCAL IndexItem   :DWORD
    LOCAL Buffer[128] :BYTE

    .if uMsg == WM_CHAR
      .if wParam == 13
        call ShowItem
      .endif

    .elseif uMsg == WM_LBUTTONDBLCLK
        call ShowItem

    .endif

    invoke CallWindowProc,lpfnListProc,hCtl,uMsg,wParam,lParam

    ret

    ShowItem:
      invoke SendMessage,hCtl,LB_GETCURSEL,0,0
      mov IndexItem, eax
      invoke SendMessage,hCtl,LB_GETTEXT,IndexItem,ADDR Buffer
      invoke MessageBox,hWnd,ADDR Buffer,ADDR szDisplayName,MB_OK
      invoke SetFocus,hCtl
    ret

ListProc endp

; #########################################################################



end start
