text: //
  // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  // PARTICULAR PURPOSE.
  //
  // Copyright (c) Microsoft Corporation. All rights reserved.
  //
  // CSampleProvider implements ICredentialProvider, which is the main
  // interface that logonUI uses to decide which tiles to display.
  // In this sample, we will display one tile that uses each of the nine
  // available UI controls.
  
  #include <initguid.h>
  #include "CSampleProvider.h"
  #include "CSampleCredential.h"
  #include "guid.h"
  
  CSampleProvider::CSampleProvider():
      _cRef(1),
      _pCredential(nullptr),
      _pCredProviderUserArray(nullptr)
  {
      DllAddRef();
  }
  
  CSampleProvider::~CSampleProvider()
  {
      if (_pCredential != nullptr)
      {
          _pCredential->Release();
          _pCredential = nullptr;
      }
      if (_pCredProviderUserArray != nullptr)
      {
          _pCredProviderUserArray->Release();
          _pCredProviderUserArray = nullptr;
      }
  
      DllRelease();
  }
  
  // SetUsageScenario is the provider's cue that it's going to be asked for tiles
  // in a subsequent call.
  HRESULT CSampleProvider::SetUsageScenario(
      CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
      DWORD /*dwFlags*/)
  {
      HRESULT hr;
  
      // Decide which scenarios to support here. Returning E_NOTIMPL simply tells the caller
      // that we're not designed for that scenario.
      switch (cpus)
      {
      case CPUS_LOGON:
      case CPUS_UNLOCK_WORKSTATION:
          // The reason why we need _fRecreateEnumeratedCredentials is because ICredentialProviderSetUserArray::SetUserArray() is called after ICredentialProvider::SetUsageScenario(),
          // while we need the ICredentialProviderUserArray during enumeration in ICredentialProvider::GetCredentialCount()
          _cpus = cpus;
          _fRecreateEnumeratedCredentials = true;
          hr = S_OK;
          break;
  
      case CPUS_CHANGE_PASSWORD:
      case CPUS_CREDUI:
          hr = E_NOTIMPL;
          break;
  
      default:
          hr = E_INVALIDARG;
          break;
      }
  
      return hr;
  }
  
  // SetSerialization takes the kind of buffer that you would normally return to LogonUI for
  // an authentication attempt.  It's the opposite of ICredentialProviderCredential::GetSerialization.
  // GetSerialization is implement by a credential and serializes that credential.  Instead,
  // SetSerialization takes the serialization and uses it to create a tile.
  //
  // SetSerialization is called for two main scenarios.  The first scenario is in the credui case
  // where it is prepopulating a tile with credentials that the user chose to store in the OS.
  // The second situation is in a remote logon case where the remote client may wish to
  // prepopulate a tile with a username, or in some cases, completely populate the tile and
  // use it to logon without showing any UI.
  //
  // If you wish to see an example of SetSerialization, please see either the SampleCredentialProvider
  // sample or the SampleCredUICredentialProvider sample.  [The logonUI team says, "The original sample that
  // this was built on top of didn't have SetSerialization.  And when we decided SetSerialization was
  // important enough to have in the sample, it ended up being a non-trivial amount of work to integrate
  // it into the main sample.  We felt it was more important to get these samples out to you quickly than to
  // hold them in order to do the work to integrate the SetSerialization changes from SampleCredentialProvider
  // into this sample.]
  HRESULT CSampleProvider::SetSerialization(
      _In_ CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION const * /*pcpcs*/)
  {
      return E_NOTIMPL;
  }
  
  // Called by LogonUI to give you a callback.  Providers often use the callback if they
  // some event would cause them to need to change the set of tiles that they enumerated.
  HRESULT CSampleProvider::Advise(
      _In_ ICredentialProviderEvents * /*pcpe*/,
      _In_ UINT_PTR /*upAdviseContext*/)
  {
      return E_NOTIMPL;
  }
  
  // Called by LogonUI when the ICredentialProviderEvents callback is no longer valid.
  HRESULT CSampleProvider::UnAdvise()
  {
      return E_NOTIMPL;
  }
  
  // Called by LogonUI to determine the number of fields in your tiles.  This
  // does mean that all your tiles must have the same number of fields.
  // This number must include both visible and invisible fields. If you want a tile
  // to have different fields from the other tiles you enumerate for a given usage
  // scenario you must include them all in this count and then hide/show them as desired
  // using the field descriptors.
  HRESULT CSampleProvider::GetFieldDescriptorCount(
      _Out_ DWORD *pdwCount)
  {
      *pdwCount = SFI_NUM_FIELDS;
      return S_OK;
  }
  
  // Gets the field descriptor for a particular field.
  HRESULT CSampleProvider::GetFieldDescriptorAt(
      DWORD dwIndex,
      _Outptr_result_nullonfailure_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **ppcpfd)
  {
      HRESULT hr;
      *ppcpfd = nullptr;
  
      // Verify dwIndex is a valid field.
      if ((dwIndex < SFI_NUM_FIELDS) && ppcpfd)
      {
          hr = FieldDescriptorCoAllocCopy(s_rgCredProvFieldDescriptors[dwIndex], ppcpfd);
      }
      else
      {
          hr = E_INVALIDARG;
      }
  
      return hr;
  }
  
  // Sets pdwCount to the number of tiles that we wish to show at this time.
  // Sets pdwDefault to the index of the tile which should be used as the default.
  // The default tile is the tile which will be shown in the zoomed view by default. If
  // more than one provider specifies a default the last used cred prov gets to pick
  // the default. If *pbAutoLogonWithDefault is TRUE, LogonUI will immediately call
  // GetSerialization on the credential you've specified as the default and will submit
  // that credential for authentication without showing any further UI.
  HRESULT CSampleProvider::GetCredentialCount(
      _Out_ DWORD *pdwCount,
      _Out_ DWORD *pdwDefault,
      _Out_ BOOL *pbAutoLogonWithDefault)
  {
      *pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
      *pbAutoLogonWithDefault = FALSE;
  
      if (_fRecreateEnumeratedCredentials)
      {
          _fRecreateEnumeratedCredentials = false;
          _ReleaseEnumeratedCredentials();
          _CreateEnumeratedCredentials();
      }
  
      *pdwCount = 1;
  
      return S_OK;
  }
  
  // Returns the credential at the index specified by dwIndex. This function is called by logonUI to enumerate
  // the tiles.
  HRESULT CSampleProvider::GetCredentialAt(
      DWORD dwIndex,
      _Outptr_result_nullonfailure_ ICredentialProviderCredential **ppcpc)
  {
      HRESULT hr = E_INVALIDARG;
      *ppcpc = nullptr;
  
      if ((dwIndex == 0) && ppcpc)
      {
          hr = _pCredential->QueryInterface(IID_PPV_ARGS(ppcpc));
      }
      return hr;
  }
  
  // This function will be called by LogonUI after SetUsageScenario succeeds.
  // Sets the User Array with the list of users to be enumerated on the logon screen.
  HRESULT CSampleProvider::SetUserArray(_In_ ICredentialProviderUserArray *users)
  {
      if (_pCredProviderUserArray)
      {
          _pCredProviderUserArray->Release();
      }
      _pCredProviderUserArray = users;
      _pCredProviderUserArray->AddRef();
      return S_OK;
  }
  
  void CSampleProvider::_CreateEnumeratedCredentials()
  {
      switch (_cpus)
      {
      case CPUS_LOGON:
      case CPUS_UNLOCK_WORKSTATION:
          {
              _EnumerateCredentials();
              break;
          }
      default:
          break;
      }
  }
  
  void CSampleProvider::_ReleaseEnumeratedCredentials()
  {
      if (_pCredential != nullptr)
      {
          _pCredential->Release();
          _pCredential = nullptr;
      }
  }
  
  HRESULT CSampleProvider::_EnumerateCredentials()
  {
      HRESULT hr = E_UNEXPECTED;
      if (_pCredProviderUserArray != nullptr)
      {
          DWORD dwUserCount;
          _pCredProviderUserArray->GetCount(&dwUserCount);
          if (dwUserCount > 0)
          {
              ICredentialProviderUser *pCredUser;
              hr = _pCredProviderUserArray->GetAt(0, &pCredUser);
              if (SUCCEEDED(hr))
              {
                  _pCredential = new(std::nothrow) CSampleCredential();
                  if (_pCredential != nullptr)
                  {
                      hr = _pCredential->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
                      if (FAILED(hr))
                      {
                          _pCredential->Release();
                          _pCredential = nullptr;
                      }
                  }
                  else
                  {
                      hr = E_OUTOFMEMORY;
                  }
                  pCredUser->Release();
              }
          }
      }
      return hr;
  }
  
  // Boilerplate code to create our provider.
  HRESULT CSample_CreateInstance(_In_ REFIID riid, _Outptr_ void **ppv)
  {
      HRESULT hr;
      CSampleProvider *pProvider = new(std::nothrow) CSampleProvider();
      if (pProvider)
      {
          hr = pProvider->QueryInterface(riid, ppv);
          pProvider->Release();
      }
      else
      {
          hr = E_OUTOFMEMORY;
      }
      return hr;
  }
  