INF: Creating a Progress Dialog with a Cancel Option

ID Number: Q76415

3.00

WINDOWS

Summary:

The following article describes the mechanisms necessary to implement

a progress or activity indicator with a "Cancel Operation" option, to

be used for lengthy and CPU-intensive subtasks of an application.

Examples of operations using this include: copying multiple files,

directory searches, or printing large files.

More Information:

The progress dialog is implemented in two steps:

1. Initialize the dialog before starting lengthy or CPU intensive

subtask.

2. After each unit of the subtask is complete, call ProgressYield()

to determine if the user has canceled the operation and to update

the progress or activity indicator.

This is the description of the progress dialog procedure. The

procedure uses a global variable (Cancel) to inform the CPU-intensive

subtask that the user has indicated a desire to terminate the subtask.

WORD Cancel = FALSE; /* This must be global to all modules */

/* which call ProgressYield() */

BOOL FAR PASCAL ProgressDlgProc(hDlg, message, wParam, lParam)

HWND hDlg;

unsigned message;

WORD wParam;

DWORD lParam;

{

switch (message)

{

.

/* use other messages to update the progress or activity */

/* indicator */

.

case WM_COMMAND:

switch (wParam)

{

case ID_CANCEL: /* ID_CANCEL = 2 */

Cancel = TRUE;

default:

return FALSE;

}

.

.

default:

return FALSE;

}

}

The following describes the ProgressYield procedure, which should be

called after each unit of the CPU intensive subtask is completed. The

ProgressYield procedure uses the IsDialogMessage function (described

in the "Microsoft Windows Software Development Kit Reference Volume

1"). IsDialogMessage will convert keyboard messages into selection

commands for the corresponding dialog box as outlined in Table 1.7

beginning on page 1-51.

void ProgressYield(HWND hwnd)

{

MSG msg;

/* remove all available messages for any window that belong */

/* to the current application */

while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

/* Translate and Dispatch the given message if the window */

/* handle is null or the given message is not for the */

/* modeless dialog box hwnd. */

if (!hwnd || !IsDialogMessage(hwnd, &msg))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

}

The following describes how to incorporate the progress dialog as part

of an application's subtask that is CPU-intensive. The PROGRESS_DLG

resource should contain a button with an ID of 2, since this is the

wParam of the WM_COMMAND that will be sent when the user presses the

ESC key. The button should also have the BS_DEFPUSHBUTTON style so

that the ENTER key will also result in the termination of the

CPU-intensive subtask.

FARPROC lpProgressProc;

HWND hwndProgress; /* This needs to be global if */

/* accessed by other modules */

.

.

/* Initialize before starting CPU intensive work */

lpProgressProc = MakeProcInstance(ProgressDlgProc,

hInst); /* current instance */

hwndProgress = CreateDialog(hInst, /* current instance */

"PROGRESS_DLG", /* resource */

hwndParent, /* Parent handle */

lpProgressProc);/* instance address */

ShowWindow(hwndProgress);

.

.

/* Start CPU intensive work here */

.

.

/* Before or after each unit of work, the application */

/* should do the following: */

ProgressYield(hwndProgress);

if (Cancel == TRUE)

break; /* Terminate CPU-intensive work immediately */

.

.

/* End CPU-intensive work here */

.

.

DestroyWindow(hwndProgress);

FreeProcInstance(lpProgressProc);

.

.

Additional reference words: 3.00