Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

IProgressDialog .NET

0.00/5 (No votes)
24 Jan 2005 1  
A .NET wrapper for IProgressDialog interface and a COM object to allow use of the standard Windows progress dialog with AVI animation and built-in time remaining calculation.

Introduction

The reason for this article is to show a "pure" .NET wrapper for IProgressDialog [^] unleashed by sytelus in his Using Windows Explorer Progress Dialog In Your Application article.

Whilst the original article is quite good and I liked the idea greatly, I have found it a bit too "slow and heavy" with .tlb imports. I was also unable to find other articles/examples of implementation for .NET, so I decided to see if I can do it myself.

So here is what I have come up with.

Declaration of the Interface and COM for .NET

To start with, I've searched MSDN, Google and its groups for some clues about the interface. I've worked out that I need C# version of:

IProgressDialog * ppd;
CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, 
                                IID_IProgressDialog, (void **)&ppd);

After some Googling, I have come across an article at MSDN - COM Interop Part 1: C# Client Tutorial [^], which got me going:

[ComImport]
[Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")] 
internal class ProgressDialog {
}

[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IProgressDialog {
    ...
}

where respective GUIDs are CLSID_ProgressDialog and IID_IProgressDialog.

The next step was translating interface definition from C++ to C#. For those who are interested, it is defined in ShlObj.h.

To cut a long story short, it was done, and a test app was put together without any serious problems except one - problem with marshaling bool to BOOL and vice versa. As it turned out there are two sides to the problem - first that there are two different BOOL in C++ - 2 and 4 bytes in size, whilst bool is only 1 byte (I'm not entirely sure in numbers, and can't find the page where I've seen it). Secondly, there is a bug in VS.NET 2003.

I tried various marshaling without any luck until Wraith, suggested using [PreserveSig] attribute. Not surprisingly, it did work for HasUserCancelled() method, which was the most problematic one.

Wraith, also saved me some time by sharing with us the results of his research of LoadLibraryEx which allows loading up a DLL as resource without processing of DllMain().

.NET Wrapper

Methods

After the hurdles were overcome, it was a matter of wrapping them in some user-friendly manner (especially having MS providing an example along with the interface definition in ShlObj.h).

Instead of exposing all interface methods to the end-user (well, developer that is), I only expose three:

  • public void Start( ProgressOperationCallback callback )

    The callback function is the one which will be doing the work (i.e., file copying, data mining etc.) and reporting the progress done back to the progress dialog.

    This function shows a progress dialog, sets title, cancel message, and animation. Upon Complete reaching Total, it closes the dialog by calling Stop() function.

  • public void Stop()

    Stops processing and hides the dialog.

  • public void SetLine( IPD_Lines line, string text, bool compactPath )

    Sets text for a specified line.

Properties

All other interface's methods are substituted by the properties:

  • Animation

    Gets or sets an AVI clip that will run in the dialog box.

  • CancelMessage

    Gets or sets a message to be displayed if the user cancels the operation.

  • Complete

    Gets or sets the application-defined value that indicates what proportion of the operation has been completed at the time the method was called.

  • Complete64

    Gets or sets the application-defined value that indicates what proportion of the operation has been completed at the time the method was called.

  • Flags

    Gets or sets flags that control the operation of the progress dialog box.

  • Title

    Gets or sets the title of the progress dialog box.

  • Total

    Gets or sets application-defined value that specifies what value Complete will have when the operation is complete.

  • Total64

    Gets or sets application-defined value that specifies what value Complete will have when the operation is complete.

Events

I've decided to make only two events - OnUserCancelled and OnBeforeProgressUpdate.

  • OnBeforeProgressUpdate is fired just before the dialog is about to call the callback function, so the program can update text messages if required.
  • OnUserCancelled is fired when a user has clicked "Cancel" button.

Using the code

Using the .NET wrapper is pretty much straight forward (so I hope anyway):

WinProgressDialog pDialog;
private uint _max, _step = 0;

private void button1_Click(object sender, System.EventArgs e) {
    this.pDialog = new WinProgressDialog( this.Handle );

    if( this.pDialog != null ) {
        this.pDialog.Title            = "Hello world!";
        this.pDialog.CancelMessage    = "hold on a sec...";
        this.pDialog.Flags = 
            WinProgressDialog.IPD_Flags.Normal | 
            WinProgressDialog.IPD_Flags.Modal |
            WinProgressDialog.IPD_Flags.NoMinimize |
            WinProgressDialog.IPD_Flags.AutoTime
            ;
        this.pDialog.Animation = WinProgressDialog.IPD_Animations.FileMove;

        this.pDialog.Complete        = ( this._step = 0 );
        this.pDialog.Total            = ( this._max = DoCalc() );

        this.pDialog.OnUserCancelled += 
            new WinProgressDialog.UserCancelledHandler( 
                        pDialog_OnUserCancelled 
                        );
        this.pDialog.OnBeforeProgressUpdate += 
            new WinProgressDialog.BeforeProgressUpdateHandler( 
                        pDialog_OnBeforeProgressUpdate 
                        );

        WinProgressDialog.ProgressOperationCallback progressUpdate =
            new WinProgressDialog.ProgressOperationCallback( this.DoStep );

        this.pDialog.Start( progressUpdate );
    }

    this.pDialog.Dispose();
}

private uint DoCalc() {
    // pretend to do some calc 

    System.Threading.Thread.Sleep( 2000 );
    // get some biggish number

    Random rand = new Random();
    return (uint)rand.Next( 150, 500 );
}

private uint DoStep() {
    // pretend to do some calc

    System.Threading.Thread.Sleep( 250 );
    this._step += 13;
    return this._step;
}

Points of Interest

There are a few things that can/need be done, including a way for loading custom resource instead of hard-coded shell32.dll. Later, it can be transformed to a true .NET control with all fancy designer stuff >:).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here