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
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.