Download sample project - 20K
The progress dialog
It's one of those boring tasks we all hate to do: writing a progress dialog. It ranks right up there with typing
"ListView_SetExtendedListViewStyleEx". And even if you're able to reuse an existing dialog, it might
not look just the way you want it to, or it might be tough to integrate into your code.
Fortunately, IE 5 saves us from having to build a dialog or write code to handle a Cancel button ever again!
BROWSEUI.DLL file implements a COM interface called
IProgressDialog that provides
a dialog that looks like the one displayed by Explorer and
The progress dialog has these features:
The AVI file should be 272x60 and is subject to the normal restrictions of the animation common control. If you
don't have an AVI of your own, MSVC comes with some sample ones (in the
\common\graphics\avis directory on the CD), or you can open up shell32.dll in the
resource editor and swipe one from there. (If you were wondering, I snagged the AVI in the screen shot above from
Even though the documentation states that you can remove the minimize button from the dialog, this appears to
be broken, even in build 2195 of Windows 2000. The dialog will always have a minimize button (for the time
being, until Microsoft fixes it).
Since the COM methods have several flags and reserved parameters, I wrote a thin MFC wrapper class,
that makes your code much more readable. The MFC class is not derived from CWnd since the COM interface doesn't
provide access to the progress window directly. That means you can use the wrapper even in console apps or other
situations where you don't use the CWnd framework. I don't use much MFC stuff (the features I use most are memory
validation routines like
AfxIsValidString()), so you can very easily remove the dependency on MFC
if you so desire.
If you're wondering about the name, I put the "SH" in the class name because MSDN states that the
IProgressDialog interface is actually part of shell32.dll (and only available on Windows 2000!), and
I used "SH" to indicate that it was a shell feature. I later discovered that the dialog works fine on
older versions of Windows, but by that time I'd finished the code.
CSHProgressWnd constructor creates an
IProgressDialog object. On a properly-working
system, this will always succeed. However, you can call the
IsValid() function if you want to double-check
that everything worked. Note that you must initialize OLE (such as with
AfxOleInit()) before constructing
CSHProgressWnd destructor destroys the dialog and releases the COM interface.
Call these functions to determine how the dialog will look.
void SetTitle ( LPCTSTR szTitle )
Sets the text that appears in the caption bar of the progress dialog.
void SetAnimation ( HINSTANCE hinst, UINT uRsrcID )
void SetAnimation ( UINT uRsrcID )
Specifies a resource containing an AVI that will be shown in the dialog. The first function takes the HINSTANCE
of the module containing the resource. The second form uses the return from
as the module.
void SetCancelMessage ( LPCTSTR szMessage )
Sets the text that is shown on line 3 when the user clicks the Cancel button.
void SetCalculateTime ( bool bCalculate = true )
Sets whether the progress dialog shows an estimate of the time remaining on line 3. If you don't call
the dialog defaults to showing the time remaining.
void SetAllowMinimize ( bool bAllow = true )
Sets whether the progress dialog includes a minimize button. If you don't call
the dialog defaults to having a minimize button. NOTE: Calling
SetAllowMinimize(false) will not
remove the minimize button; this appears to be a bug in the dialog implementation.
void SetShowProgressBar ( bool bShow = true )
Sets whether the progress dialog shows a progress bar. If you don't call
dialog defaults to having a progress bar.
Showing the dialog
The progress dialog can be modal or modeless. Call one of these two functions to display the dialog.
HRESULT ShowModal ( CWnd* pwndParent )
Displays the dialog as a modal dialog.
pwndParent is a pointer to the parent window. Test the return
value with the
SUCCEEDED() macro to determine if the dialog was created successfully. If the dialog
is not created, the return value is the error returned by the
HRESULT ShowModeless ( CWnd* pwndParent )
ShowModal(), but the progress dialog is modeless instead of modal.
Updating the progress
void SetLineText ( DWORD dwLine, LPCTSTR szText, bool bCompactPath = false )
Sets one of the three lines of text in the dialog.
dwLine can be 1, 2, or 3. Lines 1 and 2 appear
between the AVI and the progress bar, and line 3 appears under the progress bar. If you have the dialog calculate
the time remaining (by calling
SetCalculateTime(true)), the dialog uses line 3 to display the estimated
time remaining, and that line is unavailable to
SetLineText(). If you are displaying a file name or
true for the third parameter to have the dialog shorten the path so it fits in the dialog.
void UpdateProgress ( DWORD dwProgress, DWORD dwMax )
void UpdateProgress ( DWORD dwProgress )
UpdateProgress() sets the dialog's progress indicator. The first form of the function sets the current
and maximum progress values. These may be, for example, 0 and 100, but the exact values are up to you. You must
call the first form the first time, then afterwards you can call the second form as long as your maximum progress
value does not change. You may freely change the maximum value if you need to, as long as you call the first form
whenever the maximum value changes.
void UpdateProgress ( ULONGLONG u64Progress, ULONGLONG u64ProgressMax )
void UpdateProgress ( ULONGLONG u64Progress )
These two functions work like the previous two, except they take 64-bit numbers instead of
You should call
HasUserCanceled() periodically during your processing, and break out if the function
EndDialog() to close the progress dialog. The
CSHProgressWnd destructor will also
close the dialog if it is still visible.
If you want to reset the timer used by the dialog to estimate the time remaining, call
at the beginning of your processing loop. The dialog uses the time between calls to
UpdateProgress() to calculate the time remaining. Normally you won't need to call this function; if
you don't call it, the dialog will base its estimate on the time elapsed between creation of the dialog (via
ShowModeless()) and the first call to
Sample code that uses CSHProgressWnd
const DWORD dwMax = 100;
dlg.SetTitle ( _T("Hang on a sec...") );
dlg.SetAnimation ( IDR_PROGRESS_AVI );
dlg.SetCancelMessage ( _T("Cancelling this operation...") );
dlg.SetLineText ( 1, _T("Unzipping files...") );
if ( FAILED( dlg.ShowModal ( this )))
dlg.UpdateProgress ( 0, dwMax );
for ( DWORD dwProgress = 0;
dwProgress < dwMax && !dlg.HasUserCanceled();
dwProgress += 5 )
wsprintf ( szMsg, _T("I'm %lu%% done"), dwProgress );
dlg.SetLineText ( 2, szMsg );
dlg.UpdateProgress ( dwProgress );
You can get updates to this and my other articles at http://home.inreach.com/mdunn/code/