Win32:IProgressDialog在鼠标悬停之前不会消失

时间:2008-12-18 15:51:32

标签: winapi message-queue

我正在使用Win32 progress dialog。最可靠的是,当我打电话时:

progressDialog.StopProgressDialog();

它不会消失。它会一直停留在屏幕上,直到用户将鼠标移到屏幕上 - 然后它会突然消失。

StopProgressDialog的呼叫立即返回(即,它不是同步呼叫)。我可以通过在电话回来后做事来证明这一点:

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();
}

表格:

  • 开始灰色
  • 变成红色表示我们已经凝视
  • 变回灰色,表示我们称之为停止

所以对StopProgressDialog的调用已经返回,除了进度对话框仍然在那里,嘲笑我,显示消息:

  

停止进度对话框

alt text


不会出现10秒

此外,在

之前,屏幕上不会显示进度对话框
System.Threading.Thread.Sleep(10000); 
十秒钟的睡眠结束了。


不限于.NET WinForms

同样的代码也在Delphi中失败,Delphi也是Window窗口的对象包装器:

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

如果StopProgressDialog失败,则会抛出异常。

IProgressDialog中的大多数方法,当转换为C#(或Delphi)时,使用编译器将失败的COM HRESULTS转换为本机语言异常的自动机制。

换句话说,如果COM调用返回错误HRESULT(即小于零的值),则以下两个签名将抛出异常:

//C#
void StopProgressDialog();

//Delphi
procedure StopProgressDialog; safecall;

以下内容让您看到HRESULT并做出反应:

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

//Delphi
function StopProgressDialog: HRESULT; stdcall;

HRESULT是32位值。如果设置了高位(或者值为负),那就是错误。

我正在使用以前的语法。因此,如果StopProgressDialog返回错误,它将自动转换为语言异常。

注意:仅针对SaG我使用[PreserveSig]语法,返回的HRESULT为零;


MsgWait?

症状类似于Raymond Chen described once,这与错误使用PeekMessage后跟MsgWaitForMultipleObjects有关:

  

“有时我的程序卡住了   报告的记录少于它   应该。我必须摇动鼠标   获取要更新的值。过了一会儿   然后,它会落后两个   3 ...“

但这意味着失败在IProgressDialog中,因为它在CLR .NET WinForms和本机Win32代码上同样失败。

3 个答案:

答案 0 :(得分:4)

为了真正隐藏对话框,我已将以下内容添加到我的C ++包装器类中:

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;
    }
}

这是在C ++中,但你应该能够在没有太多问题的情况下将其适应C#。

答案 1 :(得分:0)

检查StopProgressDialog方法的返回值,这可能会为您提供有关正在发生的事情的更多信息:

  

HRESULT StopProgressDialog(VOID);

     

如果成功则返回S_OK,否则返回错误值。

答案 2 :(得分:0)

完整的P / Invoke签名is available,但这里是精简版本以便于阅读:

[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IProgressDialog
{
    void StartProgressDialog(IntPtr hwndParent,
    [MarshalAs(UnmanagedType.IUnknown)]    object punkEnableModless, //IUnknown
        PROGDLG dwFlags,  //DWORD
        IntPtr pvResevered //LPCVOID
        );
    void StopProgressDialog();
    void SetTitle(
        [MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
        );
    void SetAnimation(
        IntPtr hInstAnimation, //HINSTANCE
        ushort idAnimation //UINT
        );
    [PreserveSig]
    [return: MarshalAs(UnmanagedType.Bool)]
    bool HasUserCancelled();
    void SetProgress(
        uint dwCompleted, //DWORD
        uint dwTotal //DWORD
        );
    void SetProgress64(
        ulong ullCompleted, //ULONGLONG
        ulong ullTotal //ULONGLONG
        );
    void SetLine(
        uint dwLineNum, //DWORD
        [MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
        [MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
        IntPtr pvResevered //LPCVOID
        );
    void SetCancelMsg(
        [MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg,
        object pvResevered
        );
    void Timer(PDTIMER dwTimerAction, object pvResevered);
}

注意:几乎所有方法都遵循适当的COM规则进行签名。 HasUserCancelled除外。它不遵循COM类中方法签名的规则。所有方法都应该返回 HRESULT ,并且返回值应该在out retval参数中。 HasUserCancelled 实际上返回一个布尔值。

注意:几乎所有这些世界都是你的。 Europa除外。试图不在那里着陆。

注意:您的几乎所有基地都属于我们。 WhatYouSay除外。主灯亮。