Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win32: IProgressDialog will not disappear until you mouse over it

i'm using the Win32 progress dialog. The damnest thing is that when i call:

progressDialog.StopProgressDialog();

it doesn't disappear. It stays on screen until the user moves her mouse over it - then it suddenly disappers.

The call to StopProgressDialog returns right away (i.e. it's not a synchronous call). i can prove this by doing things after the call has returned:

private void button1_Click(object sender, EventArgs e)
{
   //Force red background to prove we've started
   this.BackColor = Color.Red;
   this.Refresh();

   //Start a progress dialog
   IProgressDialog pd = (IProgressDialog)new ProgressDialog();
   pd.StartProgressDialog(this.Handle, null, PROGDLG.Normal, IntPtr.Zero);

   //The long running operation
   System.Threading.Thread.Sleep(10000);

   //Stop the progress dialog
   pd.SetLine(1, "Stopping Progress Dialog", false, IntPtr.Zero);
   pd.StopProgressDialog();
   pd = null;

   //Return form to normal color to prove we've stopped.
   this.BackColor = SystemColors.Control;
   this.Refresh();
}

The form:

  • starts gray
  • turns red to show we've stared
  • turns back to gray color to show we've called stop

So the call to StopProgressDialog has returned, except the progress dialog is still sitting there, mocking me, showing the message:

Stopping Progress Dialog

alt text


Doesn't Appear for 10 seconds

Additionally, the progress dialog does not appear on screen until the

System.Threading.Thread.Sleep(10000); 

ten second sleep is over.


Not limited to .NET WinForms

The same code also fails in Delphi, which is also an object wrapper around Window's windows:

procedure TForm1.Button1Click(Sender: TObject);
var
   pd: IProgressDialog;
begin
   Self.Color := clRed;
   Self.Repaint;

   pd := CoProgressDialog.Create;
   pd.StartProgressDialog(Self.Handle, nil, PROGDLG_NORMAL, nil);

   Sleep(10000);

   pd.SetLine(1, StringToOleStr('Stopping Progress Dialog'), False, nil);
   pd.StopProgressDialog;
   pd := nil;

   Self.Color := clBtnFace;
   Self.Repaint;
end;

PreserveSig

An exception would be thrown if StopProgressDialog was failing.

Most of the methods in IProgressDialog, when translated into C# (or into Delphi), use the compiler's automatic mechanism of converting failed COM HRESULTS into a native language exception.

In other words the following two signatures will throw an exception if the COM call returned an error HRESULT (i.e. a value less than zero):

//C#
void StopProgressDialog();

//Delphi
procedure StopProgressDialog; safecall;

Whereas the following lets you see the HRESULT's and react yourself:

//C#
[PreserveSig]
int StopProgressDialog();

//Delphi
function StopProgressDialog: HRESULT; stdcall;

HRESULT is a 32-bit value. If the high-bit is set (or the value is negative) it is an error.

i am using the former syntax. So if StopProgressDialog is returning an error it will be automatically converted to a language exception.

Note: Just for SaG i used the [PreserveSig] syntax, the returned HRESULT is zero;


MsgWait?

The symptom is similar to what Raymond Chen described once, which has to do with the incorrect use of PeekMessage followed by MsgWaitForMultipleObjects:

"Sometimes my program gets stuck and reports one fewer record than it should. I have to jiggle the mouse to get the value to update. After a while longer, it falls two behind, then three..."

But that would mean that the failure is in IProgressDialog, since it fails equally well on CLR .NET WinForms and native Win32 code.

like image 971
Ian Boyd Avatar asked Dec 04 '25 08:12

Ian Boyd


1 Answers

To really hide the dialog, I've added the following to my C++ wrapper class:

void CProgressDlg::Stop()
{
    if ((m_isVisible)&&(m_bValid))
    {
        HWND hDlgWnd = NULL;
        //Sometimes the progress dialog sticks around after stopping it,
        //until the mouse pointer is moved over it or some other triggers.
        //This process finds the hwnd of the progress dialog and hides it
        //immediately.
        IOleWindow *pOleWindow;
        HRESULT hr=m_pIDlg->QueryInterface(IID_IOleWindow,(LPVOID *)&pOleWindow);
        if(SUCCEEDED(hr))
        {
            hr=pOleWindow->GetWindow(&hDlgWnd);
            if(FAILED(hr))
            {
                hDlgWnd = NULL;
            }
            pOleWindow->Release();
        }
        m_pIDlg->StopProgressDialog();
        if (hDlgWnd)
            ShowWindow(hDlgWnd, SW_HIDE);

        m_isVisible = false;
        m_pIDlg->Release();
        m_bValid = false;
    }
}

This is in C++, but you should be able to adapt this to C# without much problems.

like image 55
Stefan Avatar answered Dec 07 '25 15:12

Stefan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!