Win32: IProgressDialog не исчезнет, ​​пока вы не наведете на него курсор мыши - PullRequest
2 голосов
/ 18 декабря 2008

Я использую диалог прогресса 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();
}

Форма:

  • начинает серый
  • становится красным, чтобы показать, что мы смотрели
  • возвращается к серому цвету, чтобы показать, что мы назвали stop

Итак, звонок на StopProgressDialog вернулся, за исключением того, что диалог с прогрессом все еще сидит там, издевается надо мной, показывая сообщение:

Диалог остановки процесса

alt text


не появляется в течение 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;

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

Симптом похож на то, что Раймонд Чен однажды описал , что связано с неправильным использованием PeekMessage, за которым следует MsgWaitForMultipleObjects:

"Иногда моя программа зависает и сообщает на одну запись меньше, чем это должен. Я должен трясти мышью, чтобы получить значение для обновления. Спустя некоторое время дольше он отстает на два, потом три ... "

Но это будет означать, что сбой происходит в IProgressDialog, поскольку он одинаково хорошо работает на CLR .NET WinForms и собственном коде Win32.

Ответы [ 3 ]

4 голосов
/ 17 января 2009

Чтобы действительно скрыть диалоговое окно, я добавил в класс оболочки 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 голосов
/ 18 декабря 2008

Полная подпись 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. Включите основной свет.

0 голосов
/ 18 декабря 2008

Проверьте возвращаемое значение метода StopProgressDialog, возможно, это даст вам больше информации о том, что происходит:

HRESULT StopProgressDialog (VOID);

Возвращает S_OK в случае успеха или значение ошибки в противном случае.

...