5

I use the IProgressDialog interface as per normal, everything works all right but the progress dialog only appears after a few seconds when it decides that the operation is indeed lengthy. Although this is nowhere documented, it seems to be intended behavior and not bad in many cases. However, in this particular application I'd need to display it immediately.

I found a single reference on the whole wide web to acknowledge this and to suggest a workaround. However, maybe because there were quite a few new Windows versions during that nine years, it doesn't seem to work today:

IProgressDialog dialog;
dialog.StartProgressDialog(owner, null, flags, IntPtr.Zero);
...
IntPtr DialogHWnd;
((IOleWindow)dialog).GetWindow(out DialogHWnd);
SendMessage(DialogHWnd, WM_TIMER, new IntPtr(1), IntPtr.Zero);

The window handle is OK, I checked it, there is no error, just there is no change in behavior. The dialog only appears after a few seconds.

Does anybody have a suggestion short of re-creating the same dialog and functionality in C# again? Not impossible, of course, but the stock dialog has nice features like animation and canceling behavior, in addition to being very familiar to users.

1 Answer 1

5

Call dialog.Timer(PDTIMER_RESUME, null) immediately before or after StartProgressDialog. Here is a minimal sample:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsFormsApplication1 {

[Flags]
public enum IPD_Flags: uint {
    Normal = 0x00000000,
    Modal = 0x00000001,
    AutoTime = 0x00000002,
    NoTime = 0x00000004,
    NoMinimize = 0x00000008,
    NoProgressBar = 0x00000010 
}

[Flags]
public enum IPDTIMER_Flags: uint {
    Reset = 0x00000001,
    Pause = 0x00000002,
    Resume = 0x00000003
}

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

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

    [PreserveSig]
    void StartProgressDialog(IntPtr hwndParent
        , [MarshalAs(UnmanagedType.IUnknown)] object  punkEnableModless
        , uint dwFlags
        , IntPtr pvResevered);

    [PreserveSig]
    void StopProgressDialog();

    [PreserveSig]
    void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pwzTitle);

    [PreserveSig]
    void SetAnimation(IntPtr hInstAnimation, ushort idAnimation);

    [PreserveSig]
    [return: MarshalAs(UnmanagedType.Bool)]
    bool HasUserCancelled();

    [PreserveSig]
    void SetProgress(uint dwCompleted, uint dwTotal);

    [PreserveSig]
    void SetProgress64(ulong ullCompleted, ulong ullTotal );

    [PreserveSig]
    void SetLine(uint dwLineNum
        , [MarshalAs(UnmanagedType.LPWStr)] string pwzString
        , [MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath
        , IntPtr pvResevered);

    [PreserveSig]
    void SetCancelMsg([MarshalAs(UnmanagedType.LPWStr)]string pwzCancelMsg, object pvResevered);

    [PreserveSig]
    void Timer(uint dwTimerAction, object pvResevered);

}


public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e) {
        ProgressDialog progressDialog = new ProgressDialog();
        IProgressDialog iProgressDialog = (IProgressDialog)progressDialog;

        iProgressDialog.Timer((uint) IPDTIMER_Flags.Resume, null);
        iProgressDialog.StartProgressDialog(this.Handle
            , null
            , (uint)(IPD_Flags.Normal | IPD_Flags.NoMinimize)
            , IntPtr.Zero);
    }
}

}

3
  • The idea is very nice (I actually tried Timer() but not with the resume flag). But it's not yet perfect, I'm afraid. It works in this simple sample of yours but in the real application where I also need to handle the progress bar, right now, I either get the immediate start or the correct progress bar later. :-) I keep trying and will report back. Thanks.
    – Gábor
    Commented May 24, 2016 at 19:43
  • 1
    OK, found it. The important point was that this was not a simple progress bar. The progress dialog animates the bar in the background between the calls to SetProgress(), to make it move smoother between your specified values instead of jumping. As a result, in addition to your idea, I also had to bracket each SetProgress() call like this: dialog.Timer(PDTIMER_PAUSE, null); dialog.SetProgress(...); dialog.Timer(PDTIMER_RESUME, null);
    – Gábor
    Commented May 24, 2016 at 20:01
  • While this works, it has all sorts of weird side-effects: the title and line 1 gets replaced with "Processing", content is cleared when you click cancel, and there may be other issues I didn't notice. Commented Nov 15, 2021 at 14:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.