Экран прогресса поставщика учетных данных Windows C++

Я разрабатываю собственный поставщик учетных данных, и мне нужно показать экран прогресса с кнопкой отмены. Я видел в некоторых поставщиках учетных данных и плагинах pgina, что экран отображается с кнопкой «Отмена», когда поставщик учетных данных работает. Я прикрепил его скриншот. Мне удалось показать экран ошибки с помощью кнопки «ОК», используя следующий код:

*pcpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
SHStrDupW(L"Authentication Failed", ppwszOptionalStatusText);
*pcpsiOptionalStatusIcon = CPSI_ERROR;

Теперь мне нужно показать этот экран прогресса с помощью кнопки отмены. Любой совет, как это может быть достигнуто? Кроме того, как обработать событие, которое срабатывает при нажатии этой кнопки?Этот экран должен отображаться, когда поставщик учетных данных работает


person js.hrt    schedule 20.08.2018    source источник
comment
В любом случае, сейчас я нахожусь в том же положении, что и @js.hrt. Я работаю над поставщиком учетных данных и хочу поставить кнопку отмены. Решается ли эта проблема? Я просто хочу показать кнопку отмены и заставить ее работать на основе потока, чтобы она не вмешивалась в основной поток.   -  person MH Rahman    schedule 19.07.2021


Ответы (4)


Насколько я понимаю ваш сценарий, вы хотите что-то сделать в фоновом режиме, представляя пользователю «экран ожидания».

Вы должны запустить отдельный поток для фоновой работы и изменить макет плитки учетных данных, чтобы оставить видимым только один текстовый элемент с содержимым «Подождите...» и без кнопки отправки.

Как только ваш фоновый поток завершит свою работу, вы можете открыть кнопку отправки и позволить пользователю продолжить вход в систему.

Например, взгляните на встроенный Smartcard Credential Porvider и его поведение при вставке и извлечении карты.

person Alexander    schedule 21.08.2018
comment
Да вы правильно поняли. Да, в настоящее время я реализовал это, как вы предложили (я скрыл все остальные элементы и просто показал текст «Пожалуйста, подождите...»). Но в этом подходе нет кнопки отмены. Например, если запущен фоновый поток, и пользователь хочет отменить и вернуться, чтобы изменить метод аутентификации (при условии, что доступны разные методы аутентификации), как он это сделает? Ему придется дождаться завершения процесса или тайм-аута. Поэтому мне нужно включить кнопку отмены. - person js.hrt; 22.08.2018
comment
Хотя мы можем справиться с этим и с помощью метода SetDeselected(), но мне нужно использовать подход, показанный на скриншоте (с кнопкой отмены) - person js.hrt; 22.08.2018
comment
Нужно попробовать использовать CPFT_COMMAND_LINK элемент(ы). - person Alexander; 22.08.2018
comment
насколько мне известно, CPFT_COMMAND_LINK — это гиперссылка. Можно ли его показать как кнопку? - person js.hrt; 26.08.2018
comment
@js.hrt Нет, гиперссылка — это элемент управления в стиле ссылки (подчеркнутый текст), но на нее можно кликнуть! - person Alexander; 26.08.2018

@js.hrt Вы можете запускать свой основной поток как диалог, в то время как ваш фоновый поток выполняет эту работу. Кнопка отмены будет элементом управления в диалоговом окне, позволяющим отменить его. Если вам нужна дополнительная информация, дайте мне знать, я могу предоставить некоторые подробности, так как мы делаем это так.

@js.hrt Вкратце, вам нужно два класса: диалог и поток. Когда вы создаете диалог, он создает поток, который запустит то, что вам нужно, и покажет кнопку отмены. Нажав на нее, вы завершите свою цепочку. Некоторый код ниже. Надеюсь, поможет.

class Thread  {
    public:
        Thread(GUI* object);
        virtual ~Thread();
        bool start( bool )   {
        ::CreateThread( NULL, 0, threadRun, lpParameter, dwCreationFlags, 
             &m_dwThreadId );
        }
        static DWORD WINAPI threadRun( void* lpVoid )   {
            DWORD dwReturn( 0 );
            dwReturn = m_object->yourProcessToRun();
            return dwReturn;
         }
    protected:
        GUI* m_object;
        Runnable* m_lpRunnable;
};

Затем класс для вашего пользовательского интерфейса, подобный этому

#include "atlwin.h"

class GUI: public CDialogImpl<GUI>  {
   public:
      enum { IDD = IDD_FOR_YOUR_DIALOG  };
      GUI();
     ~GUI();
      BEGIN_MSG_MAP(GUI)
          MESSAGE_HANDLER(WM_INITDIALOG,OnInitDialog)
          COMMAND_ID_HANDLER(ID_CANCEL,OnCancel)
          MESSAGE_HANDLER(WM_TIMER,OnTimer)
          MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
      END_MSG_MAP()
      LRESULT OnInitDialog(UINT,WPARAM,LPARAM, BOOL&) {
          myThread = new Thread(this);
          m_nTimerID = SetTimer(1,3000,NULL);
          myThread->start();
      }
      LRESULT OnCancel(WORD,WORD,HWND,BOOL& )  {
          if(NULL != myThread)  {
             DWORD exitCode = 0;
             myThread->getExitCode(exitCode);
             if(exitCode == STILL_ACTIVE)
                 myThread->terminate();
             delete myThread;
              myThread = NULL;
           }
           EndDialog(IDCANCEL);
           return true;
      }        
      LRESULT OnTimer(UINT,WPARAM wParam,LPARAM,BOOL&)  {
          if(wParam != m_nTimerID)
              return FALSE;
          m_timerticks++;
          return FALSE;
      }
      LRESULT OnDestroy(UINT,WPARAM,LPARAM,BOOL&)  {
          KillTimer(m_nTimerID);
          return FALSE;
      }
      virtual int yourProcessToRun() {};
      void onFinishProgress(int retCode = IDOK) {
          if (retCode != IDCANCEL)  {
              delete myThread;
              myThread = NULL;
              KillTimer(m_nTimerID);
              EndDialog(retCode);
           }
       }

      private:
          Thread* myThread;
           UINT    m_nTimerID;
           UINT    m_timerticks;
 };

Ресурс для диалога может быть таким:

IDD_FOR_YOUR_DIALOG DIALOGEX 0, 0, 309, 80
       STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP 
       | WS_CAPTION
CAPTION "Whatever"
FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
    PUSHBUTTON      "Cancel",ID_CANCEL,113,50,84,14
    CTEXT           "Static",IDC_FOR_SOMETHING,7,7,295,20
END

@js.hrt Если вы не против опубликовать свой код, я заставлю его работать. Не могу комментировать ваше сообщение напрямую, так как ограничиваю требования сайта

person Valeca    schedule 22.08.2018
comment
Спасибо. Можете ли вы привести пример того, как вы показываете диалог? - person js.hrt; 26.08.2018
comment
Я пробую ваш код, но получаю много ошибок. Я новичок в С++, поэтому сталкиваюсь с трудностями при выявлении проблем. Не могли бы вы сказать, какие файлы включать и как заставить этот образец работать? - person js.hrt; 02.09.2018

@js.hrt По вашему запросу.

class Thread  {
public:
    Thread(GUI* object);
    virtual ~Thread();
    bool start( bool )   {
    ::CreateThread( NULL, 0, threadRun, lpParameter, dwCreationFlags, 
         &m_dwThreadId );
    }
    static DWORD WINAPI threadRun( void* lpVoid )   {
        DWORD dwReturn( 0 );
        dwReturn = m_object->yourProcessToRun();
        return dwReturn;
     }
protected:
    GUI* m_object;
    Runnable* m_lpRunnable;

}; Затем класс для вашего пользовательского интерфейса, подобный этому

#include "atlwin.h"

class GUI: public CDialogImpl<GUI>  {
public:
  enum { IDD = IDD_FOR_YOUR_DIALOG  };
  GUI();
 ~GUI();
  BEGIN_MSG_MAP(GUI)
      MESSAGE_HANDLER(WM_INITDIALOG,OnInitDialog)
      COMMAND_ID_HANDLER(ID_CANCEL,OnCancel)
      MESSAGE_HANDLER(WM_TIMER,OnTimer)
      MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
  END_MSG_MAP()
  LRESULT OnInitDialog(UINT,WPARAM,LPARAM, BOOL&) {
      myThread = new Thread(this);
      m_nTimerID = SetTimer(1,3000,NULL);
      myThread->start();
  }
  LRESULT OnCancel(WORD,WORD,HWND,BOOL& )  {
      if(NULL != myThread)  {
         DWORD exitCode = 0;
         myThread->getExitCode(exitCode);
         if(exitCode == STILL_ACTIVE)
             myThread->terminate();
         delete myThread;
          myThread = NULL;
       }
       EndDialog(IDCANCEL);
       return true;
  }        
  LRESULT OnTimer(UINT,WPARAM wParam,LPARAM,BOOL&)  {
      if(wParam != m_nTimerID)
          return FALSE;
      m_timerticks++;
      return FALSE;
  }
  LRESULT OnDestroy(UINT,WPARAM,LPARAM,BOOL&)  {
      KillTimer(m_nTimerID);
      return FALSE;
  }
  virtual int yourProcessToRun() {};
  void onFinishProgress(int retCode = IDOK) {
      if (retCode != IDCANCEL)  {
          delete myThread;
          myThread = NULL;
          KillTimer(m_nTimerID);
          EndDialog(retCode);
       }
   }

  private:
      Thread* myThread;
       UINT    m_nTimerID;
       UINT    m_timerticks;

}; Ресурс для диалога может быть таким:

IDD_FOR_YOUR_DIALOG DIALOGEX 0, 0, 309, 80
   STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP 
   | WS_CAPTION
CAPTION "Whatever"
FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
    PUSHBUTTON      "Cancel",ID_CANCEL,113,50,84,14
    CTEXT           "Static",IDC_FOR_SOMETHING,7,7,295,20
END
person t1x2zy    schedule 28.08.2018

Я предполагаю, что вы ищете IConnectableCredentialProviderCredential::Connect(). Вам нужно реализовать IConnectableCredentialProviderCredentialinterface и поместить свою логику в функцию Connect(). Вызывается сразу после нажатия кнопки Submit. Функция Connect() предоставит вам интерфейс IQueryContinueWithStatus. В этом интерфейсе вам нужно периодически вызывать функцию QueryContinue() для обработки кнопки «Отмена» или некоторых системных событий.

Дополнительные сведения см. в этой статье: https://docs.microsoft.com/en-us/windows/win32/api/credentialprovider/nf-credentialprovider-iconnectablecredentialprovidercredential-connect

person mr_vl4d    schedule 28.12.2019