News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Demo of a progress bar in a window.

Started by hutch--, March 04, 2017, 08:08:40 PM

Previous topic - Next topic

hutch--

Normally you use a progress bar in a dialog box but there occasions where you have a window based app that needs to have a  horizontal progress bar to indicate the progress of a long operation. This demo sets a normal timer (not a multi-media one) to the shortest interval possible (which is still slow) and runs the progress bar to indicate progress of the timer messages. The start button code sets up the timer and begins the process. The shutdown of the timer is done in the WM_TIMER message processing once it reaches the count set to the progress bar.

Mikl__

#1
asm-file; GUI #
include win64a.inc
IDC_PROGRESS equ 1; control IDs
IDC_STATUS equ 2
IDC_TIMER  equ 3
btn_id equ 4
bmp_id1 equ 600
bmp_id2 equ 601

.code
WinMain proc
local msg:MSG     

invoke InitCommonControls
xor ebx,ebx
mov eax,10027h
mov edi,offset ClassName
mov esi,IMAGE_BASE
push rax ;hIconSm
push rdi ;lpszClassName
push rbx ;lpszMenuName
push COLOR_APPWORKSPACE;hbrBackground
push 10003h ;hCursor
push rax        ;hIcon
push rsi ;hInstance
push rbx        ;cbClsExtra & cbWndExtra
pushaddr WndProc;lpfnWndProc
push sizeof WNDCLASSEX;cbSize & style
invoke RegisterClassEx,esp ;addr WNDCLASSEX

push rbx
push rsi ;rsi=400000h
shl esi,9 ;rsi=CW_USEDEFAULT
push rbx
push rbx
push 290
push 400
push rsi
push rsi
sub esp,20h
    invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW+WS_VISIBLE
    lea edi,msg
@@:    invoke GetMessage,edi,0,0,0
invoke DispatchMessage,edi
      jmp @b
WinMain endp
;-----------------------------------------
WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
local oldBrush:QWORD
local ps:PAINTSTRUCT
local tempX:dword
local tempY:dword
local wXY:RECT
local wid:qword
local hei:qword

mov hWnd,rcx

      cmp  edx,WM_DESTROY
      je   wmDESTROY
cmp  edx,WM_PAINT
je   wmPAINT
      cmp  edx,WM_CREATE
je   wmCREATE
      cmp  edx,WM_COMMAND
je   wmCOMMAND
leave
      jmp DefWindowProc
wmPAINT:lea edx,ps
invoke BeginPaint
invoke SelectObject,ps.hdc,blueBrush
mov oldBrush,rax
invoke SelectObject,ps.hdc,hPen
mov oldPen,rax
;Display a pie chart
finit
fld const_80
fld angle
fsincos
fmul st,st(2)
fistp tempX  ;tempX = sin(angle)*80
fmul st,st(1)
fchs
fistp tempY  ;tempY = -cos(angle)*80
mov eax,tempY
add eax,130
push rax ;nYRadial2
mov eax,tempX
add eax,190
push rax ;nXRadial2
      push 80       ;nYRadial1
push 190      ;nXRadial1
push 180      ;nBottomRect
sub esp,20h
invoke Pie,ps.hdc,110,80,270
invoke SelectObject,ps.hdc,oldBrush       
lea edx,ps
invoke EndPaint,hWnd
jmp wmBYE
wmDESTROY:mov rdx,TimerID
or edx,edx
jne  @f
      invoke KillTimer;,hWnd
@@: invoke SelectObject,ps.hdc,oldPen
invoke ExitProcess,NULL

wmCREATE:mov edi,5 ;six different progress-bars
@@: push rbx ;lpParam
push IMAGE_BASE ;hInstance
push IDC_PROGRESS       ;ID of control
; CreateWindowEx call contains hWnd as the parent window handle.
; It also specifies the control ID for identifying the latter. All subsidiaries
; Windows should be WS_CHILD style
push hWnd ;hWndParent
push nHeight[rdi*8]
;nHeight 22,22,22,100,100
push nWidth[rdi*8]
;nWidth 351,351,351,25,25
push y[rdi*8]
;y      10,190,40,80,80
push x[rdi*8]
;x      19,19,19,305,350
mov r9d,dwStyle[rdi*4]
;WS_CHILD + WS_VISIBLE + SS_BITMAP + SS_SUNKEN
;WS_CHILD + WS_VISIBLE horizontal smooth
;WS_CHILD + WS_VISIBLE + PBS_SMOOTH  horizontal bar
;WS_CHILD + WS_VISIBLE + PBS_VERTICAL vertical smooth
;WS_CHILD + WS_VISIBLE + PBS_VERTICAL + PBS_SMOOTH vertical bar
;WS_CHILD + WS_VISIBLE + SS_BITMAP + SS_SUNKEN
mov edx,Class[rdi*4] ;lpClassName
;Static, 4 msctls_progress32, Static
mov ecx,dwExStyle[rdi*4]
;0,WS_EX_DLGMODALFRAME,WS_EX_DLGMODALFRAME+WS_EX_STATICEDGE,0,0,0
      sub esp,20h
invoke CreateWindowEx,,,0
;Get the hwnd for each progressbar
mov hw3000[rdi*8],rax
;Sending each "normal" progress-bar step = 2
; A gradient progressbar does not care what we sent :)
      invoke SendMessage,eax,PBM_SETSTEP,2,0
;CurrentStep=100 timer for each step robs CurrentStep 2 when CurrentStep
; Becomes zero through stop, so we do not set the range
; With a message PBM_SETRANGE (invoke SendMessage, hwndprogress, PBM_SETRANGE, 0,100)
dec edi;we go to the next progress-bar
jns @b
; Get STATIC sizes to draw horizontal ProgressBar
invoke  GetClientRect,hw3000,&wXY
        mov     eax,wXY.right
        sub     eax,wXY.left
        mov     nWidth,rax
        mov     eax,wXY.bottom
        sub     eax,wXY.top
        mov     nHeight,rax
;Upload an image to the gradient of the horizontal progress bar
invoke LoadImage,IMAGE_BASE,bmp_id1,IMAGE_BITMAP,nWidth,nHeight,LR_DEFAULTCOLOR
;Set the image on the STATIC
invoke SendMessage,hw3000,STM_SETIMAGE,IMAGE_BITMAP,rax
; Get STATIC sizes to draw vertical ProgressBar
invoke  GetClientRect,hw3001,&wXY
        mov     eax,wXY.right
        sub     eax,wXY.left
        mov     nWidth+5*8,rax
        mov     eax,wXY.bottom
        sub     eax,wXY.top
        mov     nHeight+5*8,rax
;Upload an image to the gradient of the vertical progress bar
invoke LoadImage,IMAGE_BASE,bmp_id2,IMAGE_BITMAP,nWidth+5*8,nHeight+5*8,LR_DEFAULTCOLOR
;Set the image on the STATIC
invoke SendMessage,hw3001,STM_SETIMAGE,IMAGE_BITMAP,rax
;Create a child window first slider
push rbx;0
push IMAGE_BASE
push rbx;0
push hw3000;hwnd of "Static"
push nHeight;height of slider
        push nWidth; width of slider
push rbx;y-координата верхнего левого угла слайдера
push rbx;x-координата верхнего левого угла слайдера
        sub esp,20h
mov edx,offset stclass
invoke CreateWindowEx,0,,0,WS_CHILD + WS_VISIBLE
mov hw3100,rax
;создадим дочернее  слайдера 2
push rbx;0
push IMAGE_BASE
push rbx;0
push hw3001;hwnd элемента "Static"
push nHeight+5*8;высота слайдера 2
push nWidth+5*8;ширина слайдера 2
push rbx;y-координата верхнего левого угла слайдера 2
push rbx;x-координата верхнего левого угла слайдера 2
        sub esp,20h
mov edx,offset stclass
invoke CreateWindowEx,0,,0,WS_CHILD + WS_VISIBLE
mov hw3101,rax
;button create
push rbx;NULL
push IMAGE_BASE
push btn_id
push hWnd
push 30;высота кнопки
push 100;ширина кнопки
push 90;y-координата верхнего левого угла кнопки
push 10;x-координата верхнего левого угла кнопки
mov r8d,offset btnTxt;надпись на кнопке
mov edx,offset ctlClsNameBtn;класс окна - кнопка
sub esp,20h
invoke CreateWindowEx,NULL,,,WS_CHILD + WS_VISIBLE + BS_PUSHBUTTON
mov btn,rax
;status-bar create
invoke CreateStatusWindow,WS_CHILD + WS_VISIBLE,NULL,hWnd,IDC_STATUS
mov hwndStatus,rax
;создаем кисть для закраски кругового прогресс-бара
invoke CreateSolidBrush,0FF9933h
mov blueBrush,rax
;создаем перо того же цвета, что и фон окна
invoke CreatePen,PS_SOLID,1,0FFFFFFh
mov hPen,rax
jmp wmBYE
wmCOMMAND:cmp r8,BN_CLICKED shl 16 or 4
jne wmBYE
;if click on button -- progress-bars start
wmCOMMAND_btn1:;timer create
mov r9d,offset TimerProc
invoke SetTimer,,IDC_TIMER,100
      mov TimerID,rax
;делаем кнопку неактивной
invoke EnableWindow,btn,FALSE
;устанавливаем цвет пера таким же, как и цвет кругового прогресс-бара
invoke CreatePen,PS_SOLID,1,0FF9933h
mov hPen,rax
wmBYE: leave
retn
WndProc endp

TimerProc proc hWnd:HWND
local buffer[16]:BYTE

mov hWnd,rcx

mov edi,3
@@: invoke SendMessage,pb[rdi*8],PBM_STEPIT,0,0
      invoke SendMessage,pb[rdi*8],PBM_GETPOS,0,0
dec edi
jns @b
;выводим на статус-бар сколько процентов осталось
lea ecx,buffer
mov edx,offset format
invoke wsprintf,,,rax
lea r9,buffer
invoke SendMessage,hwndStatus,SB_SETTEXT,0
invoke InvalidateRect,hWnd,0,0
;увеличиваем градус поворота для кругового прогрессбара
finit
fld  angle
fadd delta
fstp angle
; Передвинуть слайдеры на градиентных прогресс-барах
sub nWidth,7 ;350/50=7
invoke MoveWindow,hw3100,0,0,nWidth,nHeight,TRUE
sub nHeight+5*8,2
invoke MoveWindow,hw3101,0,0,nWidth+5*8,nHeight+5*8,TRUE
sub CurrentStep,2;100/2=50
jne wmBYE
invoke KillTimer,hWnd,TimerID
mov TimerID,0
mov r9d,offset Message
invoke SendMessage,hwndStatus,SB_SETTEXT,0
wmBYE: leave
retn
TimerProc endp
;---------------------------------------
ClassName db 'Win64 Iczelion''s lesson #18: Progress-bars',0
progressClass db 'msctls_progress32',0
stclass db 'STATIC',0
Class dd stclass,4 dup(progressClass),stclass
TimerID dq ?
hw3000 dq ?
pb dq 4 dup(?);hwndprogress
hw3001 dq ?
btn dq ?
hwndStatus dq ?
ctlClsNameBtn db 'BUTTON',0
btnTxt db 'Click on me!',0
CurrentStep dq 100 ;increase step value
blueBrush dq ?
const_80 dd 80.0
angle dd 1.57952297305486826711594014548;90.5*pi/180
delta dd 0.12548917321839229658081336625;7.19*pi/180
dwStyle dd WS_CHILD + WS_VISIBLE + SS_BITMAP + SS_SUNKEN
dd WS_CHILD + WS_VISIBLE
dd WS_CHILD + WS_VISIBLE + PBS_SMOOTH
dd WS_CHILD + WS_VISIBLE + PBS_VERTICAL
dd WS_CHILD + WS_VISIBLE + PBS_VERTICAL + PBS_SMOOTH
                dd WS_CHILD + WS_VISIBLE + SS_BITMAP + SS_SUNKEN
x               dq 19,19,19,280,315,350;305,350
y               dq 10,190,40,80,80,80
nHeight         dq 22,22,22,100,100,100
nWidth          dq 351,351,351,25,25,25
dwExStyle       dd 0,WS_EX_DLGMODALFRAME,WS_EX_DLGMODALFRAME+WS_EX_STATICEDGE,0,0,0
format db ' Process : %i %%',0
Message db ' 100% Completed',0
hw3100 dq ?
hw3101 dq ?
hPen dq ?
oldPen dq ?
end
rc-file#define bmp_id1 600
#define bmp_id2 601

bmp_id1 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "Images\\progress_h.bmp"
bmp_id2 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "Images\\progress_v.bmp"

hutch--

Mikl__,

Looks great and works well.  :t

Raistlin

Thanks for this guys - will interrogate and make sense of it  :t

Quick related question A) - Using windows dialog (message pumps) - what's the best way to go about setting up multiple timers?
RE:  1) Need a timer to perform a 'ping' every 5 minutes.
        2) Need another timer to flush write cache to disk file every {1} minute.
        3) Need yet another timer to calculate network band-with to dynamically resize read/write socket buffers every 10 minutes.
        4) Need yet another..... I'am sure you're getting the picture by now  :P

Quick related question B) - saw the PIE invoke call - is there a nice library (GPL lic, and MASM compat) around for GDI type graphs/charts/simple graphics

Sorry if all "this is well known" and has been asked and answered before :icon_redface:

Are you pondering what I'm pondering? It's time to take over the world ! - let's use ASSEMBLY...

hutch--

The example does show how to use a single timer but the technique is fully extendable. You can start as many as you like from the WM_CREATE message processing and with the WM_TIMER message that you process in the WndProc you just separate them by the ID number of each timer thread you start. On app exit, just shut all the timer down from the WM_CLOSE message for a tidy exit. The ID's must have global scope.

jj2007

Quote from: hutch-- on March 06, 2017, 05:50:35 PMOn app exit, just shut all the timer down from the WM_CLOSE message for a tidy exit.

Sure?

Why won't my program terminate?
QuoteThe solution was simple: don't destroy the timer window at all, as the OS destroys it automatically when the process exits.

Unfortunately, ExitProcess is badly documented. M$ apparently doesn't have a list of the small handful of things that really should be shut down before calling ExitProcess. Global atoms, for example.

There is a pretty strange discussion here: Is KillTimer necessary?, saying basically "it's not necessary, but nonetheless it's good practice" ::)

Btw nobody else seems to care, except assembly programmers. Try googling killtimer exitprocess 8)

hutch--

Huh,

> Why won't my program terminate?

Who cares with a .NET application ?

> Is KillTimer necessary?

:Law of gravity applies, what goes up must come down.

Timers are simple things and they have worked the same way for many years going back into 16 bit Windows. Just use the standard Windows API functions and it will work.

> M$ apparently doesn't have a list of the small handful of things that really should be shut down before calling ExitProcess

Simple rule related to the law of gravity. Shut down everything that should be shut down. Short cuts related to unsound assumptions lead to code that goes BANG in inconvenient places and the operating system says horrible things about your level of competence.  :P

jj2007

Quote from: hutch-- on March 06, 2017, 11:25:51 PMShut down everything that should be shut down.

Unless somebody else does it for you. This is what ExitProcess is for.

Now the funny thing is that KillTimer gets indeed called when you exitprocess, even if you don't ask for it explicitly. Fourteen times :biggrin:

The real nuisance here is that Micros**t is really shy on documenting clearly what is needed, and what is just unnecessary bloat. Example CreateWindowEx:
QuoteFor information on removing a window, see the DestroyWindow function.

And not a single word in the DestroyWindow page when it is necessary 8)

hutch--

The documentation for ExitProcess has been with us for a long time, I don't see what the problem is unless you are trying to redefine it.

This is the second 64 bit MASM demo thread that has had a pile of superfluous discussion added to it, I wonder why ? When I post them I leave the thread open in case anyone has a question about the demo or something related to it.

If you have a problem with the design of Windows (any version) you should really take it up with Microsoft, I am simply a fellow sufferer of their OS design.