Winapi Win32:IProgressDialog将不会消失,直到您将鼠标移到它上面

Winapi Win32:IProgressDialog将不会消失,直到您将鼠标移到它上面,winapi,message-queue,Winapi,Message Queue,我在用电话。最糟糕的是当我打电话时: progressDialog.StopProgressDialog(); 它不会消失。它一直在屏幕上,直到用户将鼠标移到它上面——然后它突然消失了 对StopProgressDialog的调用会立即返回(即,它不是同步调用)。我可以通过在电话返回后做一些事情来证明这一点: private void button1_Click(object sender, EventArgs e) { //Force red background to prove w

我在用电话。最糟糕的是当我打电话时:

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
的调用已返回,但进度对话框仍在那里,模拟我,显示以下消息:

停止进度对话框


10秒钟内不会出现 此外,进度对话框直到

System.Threading.Thread.Sleep(10000); 
十秒钟的睡眠结束了


不限于.NET WinForms 同样的代码在Delphi中也会失败,Delphi也是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;

蜜饯 如果
StopProgressDialog
失败,将引发异常

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

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

鉴于以下内容,您可以查看HRESULT并自己做出反应:

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

//Delphi
function StopProgressDialog: HRESULT; stdcall;
HRESULT是一个32位的值。如果设置高位(或值为负值),则为错误

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

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


MsgWait? 该症状与以下情况类似,与不正确使用PeekMessage和MsgWaitForMultipleObjects有关:

“有时,我的程序会卡住并 报告的记录比它少一条 应该的,我得摇动鼠标 获取要更新的值。稍后 再长一点,它就落后两个了 三个……”


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

检查StopProgressDialog方法的返回值,也许这将为您提供有关发生了什么的更多信息:

HRESULT STOPPROGRESS对话框(无效)

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


完整的p/Invoke签名,但以下是简化版,便于阅读:

[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实际上返回一个布尔值

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


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

< P>为了真正隐藏对话框,我已经将下面的内容添加到我的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,而不会有太多的问题。

我不使用ReaveSimg(或者SabeCalor)。这个bug已经困扰了我一段时间。谢谢你的回答,斯蒂芬。
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;
    }
}