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

问题描述 投票:2回答:3

我正在使用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有关:

“有时候我的程序卡住并报告的记录少于它应该的记录。我必须摇动鼠标才能得到更新的值。经过一段时间后,它落后两个,然后是三个......”

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

winapi message-queue
3个回答
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#而不会出现太多问题。


0
投票

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

HRESULT StopProgressDialog(VOID);

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


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。主灯亮。

© www.soinside.com 2019 - 2024. All rights reserved.