Visual Basic Common Control API Routines
CreateWindowEx: Create the Common Control ProgressBar
     
Posted:   Sunday June 29, 1997
Updated:   Monday December 26, 2011
     
Applies to:   VB4-32, VB5, VB6
Developed with:   VB4-32, Windows 95
OS restrictions:   None
Author:   Brad Martinez
     

Related:  

SetParent: Add a VB Progress Bar to a VB StatusBar
SendMessage: Change the Colour of a VB ProgressBar
InitCommonControlsEx: Common Control Initialization Module
Pure VB: Customizable PhotoShop-Style ProgressBar
Pure VB: Customizable PhotoShop-Style ProgressBar in a MDI App
CreateWindowEx: Creating a Common Control ProgressBar- Overview
SetParent: Display Modal Form Activity on a Parent Form's 'PhotoShop-style' ProgressBar
SetParent: Display Modal Form Activity in a Parent Form's VB ProgressBar
     
 Prerequisites
None. For additional information on the progress bar members used, see Creating the Common Control ProgressBarvia the API - An Overview

This is based on the original article written for VBnet by Brad Martinez explaining the ins & outs of the Win32 / NT4 "comctl32.dll" common control library, here showing how to implement a fully-functional API ProgressBarexposed by comctl32.dll without the use of either comctl32.ocx or mscomctl.ocx.

The advantages of this implementation are obviously beneficial. First and foremost, the need to distribute an ocx control with your application is removed reducing distribution size. Also, an application's memory footprint is significantly reduced by not loading an ActiveX control. Finally, there is a noticeable improvement in performance. As long as you do not require your progress bar to handle and process mouse events, this demo is for you. (To work around this you can implement your own events via either a subclassing control or the AddressOf operator. This however is not shown here.)

The dwIterations variable used in the Command1_Click code represents the number of items you are processing (e.g. the number of files if iterating through a folder, or the number of records if iterating through a database). The progress bar whose values are set via PBM_SETRANGE - as in this demo - is limited to a maximum of 32k items. For larger number of items the PBM_SETRANGE32 message is used instead, however this requires a different SendMessage calling syntax to pass the low and high points of the progress bar range.

 BAS Module Code
None.

 Form Code
Create a new form and add a text box (Text1), check box (Check1), command button (Command1), and two labels (Label1, Label2). The blue labels show in the illustration are not required for this demo. Add the following to the general declarations section of the form; the Load code will position and label the controls. Note that the code creates the progress bar along the bottom of the form, so don't size the demo form window too large for testing.

Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'The progress bar's Form_Resize() module level variables
Private hProgBar As Long     'hWnd
Private dwPBPosLeft As Long  'static x position when vertical
Private dwPBPosTop As Long   'static y position when horizontal

'SetWindowLong param
Private Const GWL_STYLE As Long = (-16)
 
'Restricts input in the text box control to digits only
Private Const ES_NUMBER As Long = &H2000
Private Const WS_VISIBLE As Long = &H10000000
Private Const WS_CHILD As Long = &H40000000
Private Const WM_USER = &H400
Private Const PBM_SETRANGE = (WM_USER + 1)
Private Const PBM_SETPOS = (WM_USER + 2)
Private Const PBM_SETSTEP = (WM_USER + 4)
Private Const PBM_STEPIT = (WM_USER + 5)
Private Const PBM_SETRANGE32 = (WM_USER + 6)
Private Const PBM_GETRANGE = (WM_USER + 7)
Private Const PROGRESS_CLASS = "msctls_progress32"
Private Const PBS_VERTICAL = &H4
Private Const ICC_PROGRESS_CLASS = &H20

Private Type tagINITCOMMONCONTROLSEX
   dwSize As Long
   dwICC As Long
End Type

Private Declare Function InitCommonControlsEx Lib "comctl32.dll" _
   (lpInitCtrls As tagINITCOMMONCONTROLSEX) As Boolean
   
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()

Private Declare Function CreateWindowEx Lib "user32" _
   Alias "CreateWindowExA" _
  (ByVal dwExStyle As Long, _
   ByVal lpClassName As String, _
   ByVal lpWindowName As String, _
   ByVal dwStyle As Long, _
   ByVal x As Long, ByVal y As Long, _
   ByVal nWidth As Long, _
   ByVal nHeight As Long, _
   ByVal hWndParent As Long, _
   ByVal hMenu As Long, _
   ByVal hInstance As Long, _
   lpParam As Any) As Long

Private Declare Function DestroyWindow Lib "user32" _
   (ByVal hwnd As Long) As Long

Private Declare Function IsWindow Lib "user32" _
  (ByVal hwnd As Long) As Long

Private Declare Function MoveWindow Lib "user32" _
  (ByVal hwnd As Long, _
   ByVal x As Long, _
   ByVal y As Long, _
   ByVal nWidth As Long, _
   ByVal nHeight As Long, _
   ByVal bRepaint As Long) As Long

Private Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long
 
Private Declare Function GetWindowLong Lib "user32" _
   Alias "GetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long) As Long
 
Private Declare Function SetWindowLong Lib "user32" _
   Alias "SetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long, _
   ByVal dwNewLong As Long) As Long
   
Private Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (Destination As Any, Source As Any, _
   ByVal Length As Long)


Private Sub Form_Load()
  
   Call IsNewComctl32
   
   With Text1
      .Text = 25000
      .Move 1000, 300, 1000, 285
      
      Call SetWindowLong(.hwnd, _
                         GWL_STYLE, _
                         GetWindowLong(Text1.hwnd, GWL_STYLE) _
                         Or ES_NUMBER)
   End With
   
   With Check1
      .Caption = "Vertical"
      .Value = vbUnchecked
      .Move 2200, 300, 1200, 285
   End With
     
   With Command1
      .Caption = "Create && Run ProgressBar..."
      .Cancel = True
      .Move 1000, 700, 2500, 375
   End With
  
   With Label1
      .AutoSize = True
      .WordWrap = False
      .Caption = ""
      .Move 900, 1200
   End With
   
   With Label2
      .AutoSize = True
      .WordWrap = False
      .Caption = "n %"
      .Move 3700, 780 ', 2500, 375
   End With
   
   Me.ScaleMode = vbPixels
   
End Sub


Private Sub Form_Resize()
  
  'adjust the width & height w/the form
   If IsWindow(hProgBar) Then
   
      MoveWindow hProgBar, _
                 dwPBPosLeft, dwPBPosTop, _
                 Me.ScaleWidth - dwPBPosLeft, _
                 Me.ScaleHeight - dwPBPosTop, _
                 1
    
   'display the progress bar's current size
    Label1.Caption = "Width: " & Me.ScaleWidth - dwPBPosLeft & _
                     "   Height: " & Me.ScaleHeight - dwPBPosTop & _
                     "   (pixels)"
  Else
  
    Label1.Caption = "(click button to create a progress bar...)"
    
  End If

End Sub


Private Sub Command1_Click()
  
  'Dynamically create a progress bar and place it at the
  'bottom (or the right) of the form, do stuff that takes a
  'while & show the progress, then destroy the progress bar.
  
  '(The progress bar could be hidden using ShowWindow() instead
  'of it being destroyed, but the time it takes to create it is
  'negligible & its resources are also freed with this method)
   Static bRunning As Boolean   'cancel flag
   Dim bIsIE3 As Boolean
   Dim dwIterations As Long
   Dim dwRange As Long
   Dim dwStyle As Long
   Dim cnt As Long
   
   On Local Error GoTo progbar_exit
   
   If bRunning Then bRunning = False: Exit Sub
         
  'Set the default progress bar styles
   dwStyle = WS_CHILD Or WS_VISIBLE
   
  'if vertical selected, make it so!
   If Check1.Value = vbChecked Then
      dwStyle = dwStyle Or PBS_VERTICAL
   End If
  
  'Create and show the status bar. Additional standard
  'or extended window styles can be specified to alter the
  'default appearance of the progress bar. The progress
  'bar can also be easily created as a child window of a
  'status bar part (VB "Panel"). Substitute the status bar's
  'hWnd and a part's bounding rectangle (via SB_GETRECT) in
  'CreateWindowEx()'s respective params below.
   hProgBar = CreateWindowEx(0, PROGRESS_CLASS, _
                             vbNullString, _
                             dwStyle, _
                             0, 0, 0, 0, _
                             hwnd, 0, _
                             App.hInstance, _
                             ByVal 0)
  
   If hProgBar = 0 Then MsgBox "CreateWindowEx failed.": Exit Sub
     
  'Here we go... the progress bar's style can't
  'be changed after its been created.
   bRunning = True
   Command1.Caption = "Stop"
   Text1.Enabled = False
   Check1.Enabled = False

  'Set the progress bar's static x (or y) position
  'so its initially 20 pixels wide (or high). The code
  'in the form_resize event uses these values to position
  'the progress bar
   If Check1.Value = vbChecked Then
      dwPBPosLeft = Me.ScaleWidth - 20
      dwPBPosTop = 0
   Else
      dwPBPosTop = Me.ScaleHeight - 20
      dwPBPosLeft = 0
   End If
  
  'MoveWindow() in the Form_Resize event will set the
  'progress bar's initial position & size. It will also
  'display the prog bar's current size in the label.
   Call Form_Resize
  
  'get the number of iterations entered into the textbox
   dwIterations = Val(Text1.Text)
   
  'set the range and step interval of the progress bar
   dwRange = MAKELPARAM(0, dwIterations)
   Call SendMessage(hProgBar, PBM_SETRANGE, 0&, ByVal dwRange)
   Call SendMessage(hProgBar, PBM_SETSTEP, ByVal 1, 0&)
  
  'Let's do some stuff...
   For cnt = 1 To dwIterations
  
     'call DoEvents to allow the loop to
     'respond to a cancel (stop) command
      DoEvents
      If Not bRunning Then Exit For
      
     '<your app-specific code goes here>
        
     'advance the current position of the progress bar
     'by the step increment.
      Call SendMessage(hProgBar, PBM_STEPIT, 0&, ByVal 0&)
      Label2.Caption = Int((cnt / dwIterations) * 100) & " %"
    
   Next

progbar_exit:

  'Free all resources associated with the progress bar.
  'If not destroyed here, the progress bar will automatically
  'be destroyed when the parent window - the window specified in
  'hWndParent of CreateWindowEx() - is destroyed.
   If IsWindow(hProgBar) Then Call DestroyWindow(hProgBar)
  
  're-initialize form's controls
   bRunning = False
   Command1.Caption = "Create && Run ProgressBar..."
   Text1.Enabled = True
   Check1.Enabled = True
   Label2.Caption = "n %"

End Sub


Private Function MAKELPARAM(wLow As Long, wHigh As Long) As Long

  'Combines two integers into a long
   MAKELPARAM = MAKELONG(wLow, wHigh)
  
End Function


Private Function MAKELONG(wLow As Long, wHigh As Long) As Long

   MAKELONG = LoWord(wLow) Or (&H10000 * LoWord(wHigh))
  
End Function


Private Function LoWord(dwValue As Long) As Integer

   CopyMemory LoWord, dwValue, 2
  
End Function


Private Function IsNewComctl32() As Boolean

  'ensures that the Comctl32.dll library is loaded
   Dim icc As tagINITCOMMONCONTROLSEX
   
   On Error GoTo Err_InitOldVersion
   
   icc.dwSize = Len(icc)
   icc.dwICC = ICC_PROGRESS_CLASS
   
  'VB will generate error 453 "Specified DLL function not found"
  'here if the new version isn't installed
   IsNewComctl32 = InitCommonControlsEx(icc)
   
   Exit Function
   
Err_InitOldVersion:
   InitCommonControls
   
End Function
 Comments
Save then run the project, enter the number of iterations to execute (maximum 32k) and click the command button.

If, in testing just the progress bar, this code runs the bar too fast for you to debug, add a declaration for the Sleep API to the form and add a Sleep call in the loop to simulate the time taken to process your application data. When simulating 100 to 500 files 100 milliseconds should be enough for you to see the effect. If simulating less than 100 files up the delay to 200 or 250. If simulating more than 500 or so files, decrease it as low as 10 or you'll spend all day watching it.


 
 

PayPal Link
Make payments with PayPal - it's fast, free and secure!

 
 
 
 

Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy

 

Hit Counter