2

我正在使用Win32 进度对话框。最糟糕的是,当我打电话时:

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 中也失败了,它也是 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;

保留签名

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 为零;


消息等待?

症状类似于Raymond Chen 曾经描述的,这与错误使用 PeekMessage 后跟 MsgWaitForMultipleObjects 有关:

“有时我的程序卡住了,报告的记录比它应该报告的少。我必须摇动鼠标才能更新值。再过一段时间,它落后了两个,然后是三个……”

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

4

3 回答 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# 而没有太多问题。

于 2009-01-16T21:50:00.183 回答
0

检查 StopProgressDialog 方法的返回值,也许这会给你更多关于正在发生的事情的信息:

HRESULT StopProgressDialog(VOID);

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

于 2008-12-18T16:18:51.350 回答
0

完整的 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实际上返回一个布尔值。

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

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

于 2008-12-18T16:29:45.290 回答