2

I am developing a custom credential provider and I have to show a progress screen with a cancel button. I have seen in some credentials providers and pgina plugins that a screen is displayed with a Cancel button when credential provider is working. I have attached a screenshot of it. I have managed to show the error screen with an Ok button using the following code:

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

Now I need to show this progress screen with a cancel button. Any advice how can it be achieved? Also, how to handle the event that fires when this button is pressed?This screen should be displayed when credential provider is working

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
js.hrt
  • 159
  • 2
  • 14
  • Anyway, I am at the same position as @js.hrt right now. I am working on credential provider and willing to put cancel button. Does this problem solved? I just want to show the cancel button and make it works based on thread, so it won't intervene the main thread –  Jul 19 '21 at 01:58

4 Answers4

1

As I understand your scenario you want to do something in background presenting to the user "wait screen".

You must run a separate thread for background work and change the layout of your credential tile to leave visible only one text element with "Wait..." content and no submit button.

Once your background thread complete its work you may reveal submit button and let user to continue to logon.

For example, have a look at embedded Smartcard Credential Porvider and its behaviour on insertion and removal of the card.

Alexander
  • 1,232
  • 1
  • 15
  • 24
  • Yes you understood it right. Yes I have currently implemented it like you suggested (I have hidden all other elements and just shown the text 'Please wait...'). But in this approach, there is no cancel button. Like, for example, if a background thread is running and a user wants to cancel and go back to change authentication method (assuming different authentication methods are available), how will he do that? He would have to wait for the process to complete or timeout. So I need to include a cancel button. – js.hrt Aug 22 '18 at 13:35
  • Though, we can handle this via SetDeselected() method as well but I need to take the approach that is shown in screenshot (with a cancel button) – js.hrt Aug 22 '18 at 13:35
  • Have to try using `CPFT_COMMAND_LINK` element(s). – Alexander Aug 22 '18 at 13:53
  • in my knowledge CPFT_COMMAND_LINK is a hyperlink. Can it be shown as a button? – js.hrt Aug 26 '18 at 09:47
  • @js.hrt No, hyperlink is a link-style control (underscored text), but it is clickable! – Alexander Aug 26 '18 at 20:40
1

@js.hrt You can run your main thread as dialog, while your background thread does the job. The cancel button would be the control in the dialog, allowing to cancel it. If you need more info, let me know, I can provide some details, as this is the way we do it.

@js.hrt Briefly, you need two classes: dialog and thread. When you create a dialog, it will create a thread, which will run what you need, and show cancel button. Clicking on it will terminate your thread. Some code below. Hope it helps.

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;
};

Then, class for your UI, similar to this

#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;
 };

The resource for dialog could be like this:

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 If you don't mind to post your code, I'll make it run. Can't comment to your's message directly, as I limit by site requirements

t1x2zy
  • 5
  • 2
Valeca
  • 122
  • 8
  • Thanks. Can you give an example of how you are showing the dialog? – js.hrt Aug 26 '18 at 09:49
  • I am trying your code but getting a lot of errors. I am new to c++ so facing difficulties identifying the issues. Can you please tell which files to include and how to make this sample working? – js.hrt Sep 02 '18 at 13:00
0

@js.hrt Per your request.

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;

}; Then, class for your UI, similar to this

#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;

}; The resource for dialog could be like this:

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
t1x2zy
  • 5
  • 2
0

I assume you are looking for IConnectableCredentialProviderCredential::Connect(). You need to implement the IConnectableCredentialProviderCredentialinterface and put your logic to the Connect() function. It is called right after Submit button pressed. The Connect() function will give you the IQueryContinueWithStatus interface. In this interface you need to call the QueryContinue() function periodically, to handle Cancel button or some system events.

For more information look at this article: https://learn.microsoft.com/en-us/windows/win32/api/credentialprovider/nf-credentialprovider-iconnectablecredentialprovidercredential-connect

mr_vl4d
  • 1
  • 1