Click here to Skip to main content
15,887,485 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
Hello,

I am a software developer and I encounter a LogonUI problem on Windows 7 when I use a credential provider in particular conditions.
I have modified the SampleHardwareEventCredentialProvider sample of the RTMCredentialProviderSamples__FINAL archive (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b1b3cbd1-2d3a-4fac-982f-289f4f4b9300&displaylang=en).
The original sample creates one credential at a time.
My credential provider creates a list of credentials.
This list is cleared and created each time that one calls the OnConnectStatusChanged method below.
My program has a loop that send a message to the CommandWindow.
When the CommandWindow receives this message it calls OnConnectStatusChanged.
The loop is the following:

while (1) {
    ::PostMessage(pCommandWindow->_hWnd, WM_TOGGLE_CONNECTED_STATUS, 0, 0);
    Sleep(100);
}


This way each 100 milliseconds, the credentials list will be destroyed and created again.

We use here 100 milliseconds to reproduce quickly the problem that we entounter in our credential.
Of course, the loop above do something more useful in our final credential.
Here we want just to illustrate that we change the credentials list dynamically.

If we click many times very quickly on one of the displayed credentials during this loop, at some point, LogonUI freezes: there is no more credential displayed and the restart/stop button do not respond.
The only choice we have at this point is to either hard reboot the computer or reboot it remotely.
Notice that when we do remote reboot LogonUI diplays correctly the reboot message when make me think it is not totally frozen...

To reproduce the problem:
- Download the base source package at http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b1b3cbd1-2d3a-4fac-982f-289f4f4b9300&displaylang=en
- Replace the following files with the source code listed at the end of this post:
CommandWindow.cpp, CommandWindow.h, CSampleCredential.cpp, CSampleCredential.h, CSampleProvider.cpp, CSampleProvider.h

Is it justified to reset the list of credentials in this way (I mean asynchronously at any time)? If not, how should we do?
Is there a mechanism to be notified by LogonUI when it finished displaying the list of credentials?

Thank you in advance for your help.

======================================== CommandWindow.cpp ================================

#include "CommandWindow.h"
#include <strsafe.h>

#define WM_EXIT_THREAD              WM_USER + 1
#define WM_TOGGLE_CONNECTED_STATUS  WM_USER + 2

const TCHAR *g_wszClassName = "EventWindow";
const TCHAR *g_wszConnected = "Connected";
const TCHAR *g_wszDisconnected = "Disconnected";

CCommandWindow::CCommandWindow(void)
{
    _hWnd       = NULL;
    _hInst      = NULL;
    _fConnected = TRUE;
    _pProvider  = NULL;
    wnd_ready   = 0;
}

CCommandWindow::~CCommandWindow(void)
{
    if (_hWnd != NULL) {
        ::PostMessage(_hWnd, WM_EXIT_THREAD, 0, 0);
        _hWnd = NULL;
    }

    if (_pProvider != NULL) {
        _pProvider->Release();
        _pProvider = NULL;
    }
}

HRESULT CCommandWindow::Initialize(CSampleProvider *pProvider)
{
    HRESULT hr = S_OK;

    if (_pProvider != NULL) {
        _pProvider->Release();
    }
    _pProvider = pProvider;
    _pProvider->AddRef();
    
    HANDLE hThread = ::CreateThread(NULL, 0, CCommandWindow::_ThreadProc, (LPVOID) this, 0, NULL);
    if (hThread == NULL) {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }

    HANDLE th = ::CreateThread(NULL, 0, CCommandWindow::_LoopProc, (LPVOID) this, 0, NULL);
    if (th == NULL) {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }

    return hr;
}

BOOL CCommandWindow::GetConnectedStatus()
{
    return _fConnected;
}

HRESULT CCommandWindow::_MyRegisterClass(void)
{
    HRESULT hr = S_OK;

    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = CCommandWindow::_WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = _hInst;
    wcex.hIcon          = NULL;
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName   = NULL;
    wcex.lpszClassName  = ::g_wszClassName;
    wcex.hIconSm        = NULL;

    if (!RegisterClassEx(&wcex)) {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }

    return hr;
}


HRESULT CCommandWindow::_InitInstance()
{
    HRESULT hr = S_OK;

    _hWnd = ::CreateWindowEx(
        WS_EX_TOPMOST, 
        ::g_wszClassName, 
        ::g_wszDisconnected, 
        WS_DLGFRAME,
        200, 200, 200, 80, 
        NULL,
        NULL, _hInst, NULL);
    if (_hWnd == NULL) {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }
    else {
        InterlockedExchange(&wnd_ready, 1);
    }

    if (SUCCEEDED(hr)) {
        _hWndButton = ::CreateWindow("Button", "Press to connect", 
                             WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 
                             10, 10, 180, 30, 
                             _hWnd, 
                             NULL,
                             _hInst,
                             NULL);
        if (_hWndButton == NULL) {
            hr = HRESULT_FROM_WIN32(::GetLastError());
        }

        if (SUCCEEDED(hr)) {
            if (SUCCEEDED(hr)) {
                if (!::UpdateWindow(_hWnd)) {
                   hr = HRESULT_FROM_WIN32(::GetLastError());
                }
            }
        }
    }

    return hr;
}

BOOL CCommandWindow::_ProcessNextMessage()
{
    MSG msg;
    (void) ::GetMessage(&(msg), _hWnd, 0, 0);
    (void) ::TranslateMessage(&(msg));
    (void) ::DispatchMessage(&(msg));

    switch (msg.message) {
    case WM_EXIT_THREAD: return FALSE;

    case WM_TOGGLE_CONNECTED_STATUS:
        _pProvider->OnConnectStatusChanged();
        break;
    }
    return TRUE;
}

LRESULT CALLBACK CCommandWindow::_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_DEVICECHANGE:
        ::MessageBox(NULL, TEXT("Device change"), TEXT("Device change"), 0);
        break;

    case WM_CLOSE:
        ::ShowWindow(hWnd, SW_HIDE);
        ::PostMessage(hWnd, WM_EXIT_THREAD, 0, 0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

DWORD WINAPI CCommandWindow::_ThreadProc(LPVOID lpParameter)
{
    CCommandWindow *pCommandWindow = static_cast<CCommandWindow *>(lpParameter);
    if (pCommandWindow == NULL) {
        return 0;
    }

    HRESULT hr = S_OK;

    pCommandWindow->_hInst = ::GetModuleHandle(NULL);
    if (pCommandWindow->_hInst != NULL) {            
        hr = pCommandWindow->_MyRegisterClass();
        if (SUCCEEDED(hr)) {
            hr = pCommandWindow->_InitInstance();
        }
    }
    else {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }

    if (SUCCEEDED(hr)) {        
        while (pCommandWindow->_ProcessNextMessage()) {
        }
    }
    else {
        if (pCommandWindow->_hWnd != NULL) {
            pCommandWindow->_hWnd = NULL;
        }
    }

    return 0;
}

DWORD WINAPI CCommandWindow::_LoopProc(LPVOID lpParameter)
{
    CCommandWindow *pCommandWindow = static_cast<CCommandWindow *>(lpParameter);
    if (pCommandWindow == NULL) {
        return 0;
    }

    while (InterlockedCompareExchange(&pCommandWindow->wnd_ready, 1, 1) != 1) {
        Sleep(500);
    }

    while (1) {
        ::PostMessage(pCommandWindow->_hWnd, WM_TOGGLE_CONNECTED_STATUS, 0, 0);
        Sleep(100);
    }

    return 1;
}

======================================== CommandWindow.h ================================
#pragma once

#include <windows.h>
#include "CSampleProvider.h"

class CCommandWindow
{
public:
    CCommandWindow(void);
    ~CCommandWindow(void);
    HRESULT Initialize(CSampleProvider *pProvider);
    BOOL GetConnectedStatus();

private:
    HRESULT _MyRegisterClass(void);
    HRESULT _InitInstance();
    BOOL _ProcessNextMessage();
    
    static DWORD WINAPI _LoopProc(LPVOID lpParameter);
    static DWORD WINAPI _ThreadProc(LPVOID lpParameter);
    static LRESULT CALLBACK _WndProc(HWND, UINT, WPARAM, LPARAM);

    CSampleProvider             *_pProvider;
    HWND                        _hWnd;
    HWND                        _hWndButton;
    HINSTANCE                   _hInst;
    BOOL                        _fConnected;
    LONG                        wnd_ready;
};


======================================== CSampleCredential.cpp ================================
#ifndef WIN32_NO_STATUS
#include <ntstatus.h>
#define WIN32_NO_STATUS
#endif
#include <unknwn.h>
#include "CSampleCredential.h"
#include "guid.h"

CSampleCredential::CSampleCredential():
    _cRef(1),
    _pCredProvCredentialEvents(NULL)
{
    DllAddRef();

    ZeroMemory(_rgCredProvFieldDescriptors, sizeof(_rgCredProvFieldDescriptors));
    ZeroMemory(_rgFieldStatePairs, sizeof(_rgFieldStatePairs));
    ZeroMemory(_rgFieldStrings, sizeof(_rgFieldStrings));
}

CSampleCredential::~CSampleCredential()
{
    if (_rgFieldStrings[SFI_PASSWORD]) {
        size_t lenPassword;
        HRESULT hr = StringCchLengthW(_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));
        if (SUCCEEDED(hr)) {
            SecureZeroMemory(_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*_rgFieldStrings[SFI_PASSWORD]));
        }
        else {
        }
    }
    for (int i = 0; i < ARRAYSIZE(_rgFieldStrings); i++) {
        CoTaskMemFree(_rgFieldStrings[i]);
        CoTaskMemFree(_rgCredProvFieldDescriptors[i].pszLabel);
    }

    DllRelease();
}

HRESULT CSampleCredential::Initialize(
    CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
    const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
    const FIELD_STATE_PAIR* rgfsp
    )
{
    HRESULT hr = S_OK;

    _cpus = cpus;

    for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(_rgCredProvFieldDescriptors); i++) {
        _rgFieldStatePairs[i] = rgfsp[i];
        hr = FieldDescriptorCopy(rgcpfd[i], &_rgCredProvFieldDescriptors[i]);
    }

    if (SUCCEEDED(hr)) {
        hr = SHStrDupW(L"administrator", &_rgFieldStrings[SFI_USERNAME]);
    }
    if (SUCCEEDED(hr)) {
        hr = SHStrDupW(L"", &_rgFieldStrings[SFI_PASSWORD]);
    }
    if (SUCCEEDED(hr)) {
        hr = SHStrDupW(L"Submit", &_rgFieldStrings[SFI_SUBMIT_BUTTON]);
    }

    return S_OK;
}

HRESULT CSampleCredential::Advise(ICredentialProviderCredentialEvents* pcpce)
{
    if (_pCredProvCredentialEvents != NULL) {
        _pCredProvCredentialEvents->Release();
    }
    _pCredProvCredentialEvents = pcpce;
    _pCredProvCredentialEvents->AddRef();

    return S_OK;
}

HRESULT CSampleCredential::UnAdvise()
{
    if (_pCredProvCredentialEvents) {
        _pCredProvCredentialEvents->Release();
    }
    _pCredProvCredentialEvents = NULL;
    return S_OK;
}

HRESULT CSampleCredential::SetSelected(BOOL* pbAutoLogon)  
{
    *pbAutoLogon = FALSE;  
    return S_OK;
}

HRESULT CSampleCredential::SetDeselected()
{
    HRESULT hr = S_OK;
    if (_rgFieldStrings[SFI_PASSWORD]) {
        size_t lenPassword;
        hr = StringCchLengthW(_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));
        if (SUCCEEDED(hr)) {
            SecureZeroMemory(_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*_rgFieldStrings[SFI_PASSWORD]));
        
            CoTaskMemFree(_rgFieldStrings[SFI_PASSWORD]);
            hr = SHStrDupW(L"", &_rgFieldStrings[SFI_PASSWORD]);
        }

        if (SUCCEEDED(hr) && _pCredProvCredentialEvents) {
            _pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, _rgFieldStrings[SFI_PASSWORD]);
        }
    }

    return hr;
}

HRESULT CSampleCredential::GetFieldState(
    DWORD dwFieldID,
    CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
    CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis
    )
{
    HRESULT hr;
    
    if (dwFieldID < ARRAYSIZE(_rgFieldStatePairs) && pcpfs && pcpfis) {
        *pcpfis = _rgFieldStatePairs[dwFieldID].cpfis;
        *pcpfs = _rgFieldStatePairs[dwFieldID].cpfs;

        hr = S_OK;
    }
    else {
        hr = E_INVALIDARG;
    }
    return hr;
}

HRESULT CSampleCredential::GetStringValue(DWORD dwFieldID, PWSTR* ppwsz)
{
    HRESULT hr;

    if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) && ppwsz) {
        hr = SHStrDupW(_rgFieldStrings[dwFieldID], ppwsz);
    }
    else {
        hr = E_INVALIDARG;
    }

    return hr;
}

HRESULT CSampleCredential::GetBitmapValue(DWORD dwFieldID, HBITMAP* phbmp)
{
    HRESULT hr;
    if ((SFI_TILEIMAGE == dwFieldID) && phbmp) {
        HBITMAP hbmp = LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_TILE_IMAGE));
        if (hbmp != NULL) {
            hr = S_OK;
            *phbmp = hbmp;
        }
        else {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
    }
    else {
        hr = E_INVALIDARG;
    }

    return hr;
}

HRESULT CSampleCredential::GetSubmitButtonValue(DWORD dwFieldID, DWORD* pdwAdjacentTo)
{
    HRESULT hr;

    if (SFI_SUBMIT_BUTTON == dwFieldID && pdwAdjacentTo) {
        *pdwAdjacentTo = SFI_PASSWORD;
        hr = S_OK;
    }
    else {
        hr = E_INVALIDARG;
    }
    return hr;
}

HRESULT CSampleCredential::SetStringValue(DWORD dwFieldID, PCWSTR pwz)
{
    HRESULT hr;

    if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) && 
       (CPFT_EDIT_TEXT == _rgCredProvFieldDescriptors[dwFieldID].cpft || 
        CPFT_PASSWORD_TEXT == _rgCredProvFieldDescriptors[dwFieldID].cpft)) {
        PWSTR* ppwszStored = &_rgFieldStrings[dwFieldID];
        CoTaskMemFree(*ppwszStored);

        hr = SHStrDupW(pwz, ppwszStored);
    }
    else {
        hr = E_INVALIDARG;
    }

    return hr;
}

HRESULT CSampleCredential::GetCheckboxValue(DWORD dwFieldID, BOOL* pbChecked, PWSTR* ppwszLabel)
{
    UNREFERENCED_PARAMETER(dwFieldID);
    UNREFERENCED_PARAMETER(pbChecked);
    UNREFERENCED_PARAMETER(ppwszLabel);

    return E_NOTIMPL;
}

HRESULT CSampleCredential::GetComboBoxValueCount(DWORD dwFieldID, DWORD* pcItems, DWORD* pdwSelectedItem)
{
    UNREFERENCED_PARAMETER(dwFieldID);
    UNREFERENCED_PARAMETER(pcItems);
    UNREFERENCED_PARAMETER(pdwSelectedItem);
    return E_NOTIMPL;
}

HRESULT CSampleCredential::GetComboBoxValueAt(DWORD dwFieldID, DWORD dwItem, PWSTR* ppwszItem)
{
    UNREFERENCED_PARAMETER(dwFieldID);
    UNREFERENCED_PARAMETER(dwItem);
    UNREFERENCED_PARAMETER(ppwszItem);
    return E_NOTIMPL;
}

HRESULT CSampleCredential::SetCheckboxValue(DWORD dwFieldID, BOOL bChecked)
{
    UNREFERENCED_PARAMETER(dwFieldID);
    UNREFERENCED_PARAMETER(bChecked);

    return E_NOTIMPL;
}

HRESULT CSampleCredential::SetComboBoxSelectedValue(DWORD dwFieldId, DWORD dwSelectedItem)
{
    UNREFERENCED_PARAMETER(dwFieldId);
    UNREFERENCED_PARAMETER(dwSelectedItem);
    return E_NOTIMPL;
}

HRESULT CSampleCredential::CommandLinkClicked(DWORD dwFieldID)
{
    UNREFERENCED_PARAMETER(dwFieldID);
    return E_NOTIMPL;
}

HRESULT CSampleCredential::GetSerialization(
    CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 
    PWSTR* ppwszOptionalStatusText, 
    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
    )
{
    UNREFERENCED_PARAMETER(ppwszOptionalStatusText);
    UNREFERENCED_PARAMETER(pcpsiOptionalStatusIcon);

    KERB_INTERACTIVE_LOGON kil;
    ZeroMemory(&kil, sizeof(kil));

    HRESULT hr;

    WCHAR wsz[MAX_COMPUTERNAME_LENGTH+1];
    DWORD cch = ARRAYSIZE(wsz);
    if (GetComputerNameW(wsz, &cch)) {
        PWSTR pwzProtectedPassword;

        hr = ProtectIfNecessaryAndCopyPassword(_rgFieldStrings[SFI_PASSWORD], _cpus, &pwzProtectedPassword);

        if (SUCCEEDED(hr)) {
            KERB_INTERACTIVE_UNLOCK_LOGON kiul;

            // Initialize kiul with weak references to our credential.
            hr = KerbInteractiveUnlockLogonInit(wsz, _rgFieldStrings[SFI_USERNAME], pwzProtectedPassword, _cpus, &kiul);

            if (SUCCEEDED(hr)) {
                // We use KERB_INTERACTIVE_UNLOCK_LOGON in both unlock and logon scenarios.  It contains a
                // KERB_INTERACTIVE_LOGON to hold the creds plus a LUID that is filled in for us by Winlogon
                // as necessary.
                hr = KerbInteractiveUnlockLogonPack(kiul, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);

                if (SUCCEEDED(hr)) {
                    ULONG ulAuthPackage;
                    hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
                    if (SUCCEEDED(hr)) {
                        pcpcs->ulAuthenticationPackage = ulAuthPackage;
                        pcpcs->clsidCredentialProvider = CLSID_CSampleProvider;
 
                        // At this point the credential has created the serialized credential used for logon
                        // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
                        // that we have all the information we need and it should attempt to submit the 
                        // serialized credential.
                        *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
                    }
                }
            }

            CoTaskMemFree(pwzProtectedPassword);
        }
    }
    else {
        DWORD dwErr = GetLastError();
        hr = HRESULT_FROM_WIN32(dwErr);
    }

    return hr;
}
struct REPORT_RESULT_STATUS_INFO
{
    NTSTATUS ntsStatus;
    NTSTATUS ntsSubstatus;
    PWSTR     pwzMessage;
    CREDENTIAL_PROVIDER_STATUS_ICON cpsi;
};

static const REPORT_RESULT_STATUS_INFO s_rgLogonStatusInfo[] =
{
    { STATUS_LOGON_FAILURE, STATUS_SUCCESS, L"Incorrect password or username.", CPSI_ERROR, },
    { STATUS_ACCOUNT_RESTRICTION, STATUS_ACCOUNT_DISABLED, L"The account is disabled.", CPSI_WARNING },
};

HRESULT CSampleCredential::ReportResult(
    NTSTATUS ntsStatus, 
    NTSTATUS ntsSubstatus,
    PWSTR* ppwszOptionalStatusText, 
    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
    )
{
    *ppwszOptionalStatusText = NULL;
    *pcpsiOptionalStatusIcon = CPSI_NONE;

    DWORD dwStatusInfo = (DWORD)-1;

    for (DWORD i = 0; i < ARRAYSIZE(s_rgLogonStatusInfo); i++) {
        if (s_rgLogonStatusInfo[i].ntsStatus == ntsStatus && s_rgLogonStatusInfo[i].ntsSubstatus == ntsSubstatus) {
            dwStatusInfo = i;
            break;
        }
    }

    if ((DWORD)-1 != dwStatusInfo) {
        if (SUCCEEDED(SHStrDupW(s_rgLogonStatusInfo[dwStatusInfo].pwzMessage, ppwszOptionalStatusText))) {
            *pcpsiOptionalStatusIcon = s_rgLogonStatusInfo[dwStatusInfo].cpsi;
        }
    }

    if (!SUCCEEDED(HRESULT_FROM_NT(ntsStatus))) {
        if (_pCredProvCredentialEvents) {
            _pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, L"");
        }
    }

    return S_OK;
}

======================================== CSampleCredential.h ================================
#pragma once

#include <windows.h>
#include <strsafe.h>
#include <shlguid.h>
#include "helpers.h"
#include "dll.h"
#include "resource.h"
#include "CommandWindow.h"

class CSampleCredential : public ICredentialProviderCredential
{
    public:
    // IUnknown
    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&_cRef);
    }
    
    STDMETHOD_(ULONG, Release)()
    {
        LONG cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHOD (QueryInterface)(REFIID riid, void** ppv)
    {
        HRESULT hr;
        if (ppv != NULL)
        {
            if (IID_IUnknown == riid ||
                IID_ICredentialProviderCredential == riid)
            {
                *ppv = static_cast<IUnknown*>(this);
                reinterpret_cast<IUnknown*>(*ppv)->AddRef();
                hr = S_OK;
            }
            else
            {
                *ppv = NULL;
                hr = E_NOINTERFACE;
            }
        }
        else
        {
            hr = E_INVALIDARG;
        }
        return hr;
    }
  public:
    // ICredentialProviderCredential
    IFACEMETHODIMP Advise(ICredentialProviderCredentialEvents* pcpce);
    IFACEMETHODIMP UnAdvise();

    IFACEMETHODIMP SetSelected(BOOL* pbAutoLogon);
    IFACEMETHODIMP SetDeselected();

    IFACEMETHODIMP GetFieldState(DWORD dwFieldID,
                                 CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
                                 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis);

    IFACEMETHODIMP GetStringValue(DWORD dwFieldID, PWSTR* ppwsz);
    IFACEMETHODIMP GetBitmapValue(DWORD dwFieldID, HBITMAP* phbmp);
    IFACEMETHODIMP GetCheckboxValue(DWORD dwFieldID, BOOL* pbChecked, PWSTR* ppwszLabel);
    IFACEMETHODIMP GetComboBoxValueCount(DWORD dwFieldID, DWORD* pcItems, DWORD* pdwSelectedItem);
    IFACEMETHODIMP GetComboBoxValueAt(DWORD dwFieldID, DWORD dwItem, PWSTR* ppwszItem);
    IFACEMETHODIMP GetSubmitButtonValue(DWORD dwFieldID, DWORD* pdwAdjacentTo);

    IFACEMETHODIMP SetStringValue(DWORD dwFieldID, PCWSTR pwz);
    IFACEMETHODIMP SetCheckboxValue(DWORD dwFieldID, BOOL bChecked);
    IFACEMETHODIMP SetComboBoxSelectedValue(DWORD dwFieldID, DWORD dwSelectedItem);
    IFACEMETHODIMP CommandLinkClicked(DWORD dwFieldID);

    IFACEMETHODIMP GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, 
                                    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 
                                    PWSTR* ppwszOptionalStatusText, 
                                    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon);
    IFACEMETHODIMP ReportResult(NTSTATUS ntsStatus, 
                                NTSTATUS ntsSubstatus,
                                PWSTR* ppwszOptionalStatusText, 
                                CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon);

  public:
    HRESULT Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
                       const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
                       const FIELD_STATE_PAIR* rgfsp);
    CSampleCredential();
    virtual ~CSampleCredential();

  private:
    LONG                                  _cRef;
    CREDENTIAL_PROVIDER_USAGE_SCENARIO    _cpus;
    CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR  _rgCredProvFieldDescriptors[SFI_NUM_FIELDS];
    FIELD_STATE_PAIR                      _rgFieldStatePairs[SFI_NUM_FIELDS];
    PWSTR                                 _rgFieldStrings[SFI_NUM_FIELDS];
    ICredentialProviderCredentialEvents* _pCredProvCredentialEvents;
};


======================================== CSampleProvider.cpp ================================
#include <credentialprovider.h>
#include "CSampleCredential.h"
#include "CommandWindow.h"
#include "guid.h"

CSampleProvider::CSampleProvider():
    _cRef(1)
{
    DllAddRef();

    _pcpe            = NULL;
    _pCommandWindow  = NULL;
    _CredentialList  = NULL;
	_CredentialCount = 0;
    _upAdviseContext = NULL;
	
    InitializeCriticalSection(&cs);
    InitializeCriticalSection(&cs2);
}

CSampleProvider::~CSampleProvider()
{
    ClearCredentialList();
    
    if (_pCommandWindow != NULL) {
        delete _pCommandWindow;
    }

    DeleteCriticalSection(&cs);
    DeleteCriticalSection(&cs2);

    DllRelease();
}

void CSampleProvider::ClearCredentialList() 
{
	EnterCriticalSection(&cs);

	if (_CredentialCount) {
        for (DWORD i = 0; i < _CredentialCount; i++) {
			_CredentialList[i]->Release();
        }
	
		delete _CredentialList;

		_CredentialCount = 0;
        _CredentialList  = NULL;
	}

	LeaveCriticalSection(&cs);
}

void CSampleProvider::CreateCredentialList(const int Count) 
{
	EnterCriticalSection(&cs);

	_CredentialList  = new TPtrCredentialList[Count];
	_CredentialCount = Count;

	LeaveCriticalSection(&cs);
}

void CSampleProvider::OnConnectStatusChanged()
{
    HRESULT hr;
    int count = 0;

    for (int i = 0; i < 2; i++) {    
        EnterCriticalSection(&cs);
        count++;
        ClearCredentialList();
        CreateCredentialList(count);

        for (int j = 0; j < count; j++) {
	        _CredentialList[j] = new TCredential();
	        hr = _CredentialList[j]->Initialize(_cpus, 
                                                s_rgCredProvFieldDescriptors, 
                                                s_rgFieldStatePairs);
            if (FAILED(hr)) {
                _CredentialList[j]->Release();
                _CredentialList[j] = NULL;
            }
        }

        LeaveCriticalSection(&cs);

        EnterCriticalSection(&cs2);
        if (_pcpe != NULL)
        {
            _pcpe->CredentialsChanged(_upAdviseContext);
        }
        LeaveCriticalSection(&cs2);
    }
}

HRESULT CSampleProvider::SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD dwFlags)
{
    UNREFERENCED_PARAMETER(dwFlags);
    HRESULT hr;

    switch (cpus) {
    case CPUS_LOGON:
    case CPUS_UNLOCK_WORKSTATION:       
        _cpus = cpus;        
        if (!_pCommandWindow) {           
            _pCommandWindow = new CCommandWindow();
            if (_pCommandWindow != NULL) {
                hr = _pCommandWindow->Initialize(this);
                if (SUCCEEDED(hr)){   
                }
            }
            else {
                hr = E_OUTOFMEMORY;
            }

            if (FAILED(hr)) {
                if (_pCommandWindow != NULL) {
                    delete _pCommandWindow;
                    _pCommandWindow = NULL;
                }
            }
        }
        else {
            hr = S_OK;
        }
        break;

    case CPUS_CREDUI:
    case CPUS_CHANGE_PASSWORD:
        hr = E_NOTIMPL;
        break;

    default:
        hr = E_INVALIDARG;
        break;
    }

    return hr;
}

STDMETHODIMP CSampleProvider::SetSerialization(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs)
{
    UNREFERENCED_PARAMETER(pcpcs);
    return E_NOTIMPL;
}

HRESULT CSampleProvider::Advise(ICredentialProviderEvents* pcpe, UINT_PTR upAdviseContext)
{
    EnterCriticalSection(&cs2);
    if (_pcpe != NULL) {
        _pcpe->Release();
    }
    _pcpe = pcpe;
    _pcpe->AddRef();
    _upAdviseContext = upAdviseContext;
    LeaveCriticalSection(&cs2);

    return S_OK;
}

HRESULT CSampleProvider::UnAdvise()
{
    EnterCriticalSection(&cs2);
    if (_pcpe != NULL) {
        _pcpe->Release();
        _pcpe = NULL;
    }
    LeaveCriticalSection(&cs2);

    return S_OK;
}

HRESULT CSampleProvider::GetFieldDescriptorCount(DWORD* pdwCount)
{
    if (_pCommandWindow->GetConnectedStatus()) {
        *pdwCount = SFI_NUM_FIELDS;
    }
    else {
        *pdwCount = SMFI_NUM_FIELDS;
    }
  
    return S_OK;
}

HRESULT CSampleProvider::GetFieldDescriptorAt(DWORD dwIndex, CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd)
{    
    HRESULT hr;

    if (_pCommandWindow->GetConnectedStatus()) {
        if ((dwIndex < SFI_NUM_FIELDS) && ppcpfd) {
            hr = FieldDescriptorCoAllocCopy(s_rgCredProvFieldDescriptors[dwIndex], ppcpfd);
        }
        else { 
            hr = E_INVALIDARG;
        }
    }
    else {
        if ((dwIndex < SMFI_NUM_FIELDS) && ppcpfd) {
            hr = FieldDescriptorCoAllocCopy(s_rgMessageCredProvFieldDescriptors[dwIndex], ppcpfd);
        }
        else { 
            hr = E_INVALIDARG;
        }
    }

    return hr;
}

HRESULT CSampleProvider::GetCredentialCount(
    DWORD* pdwCount,
    DWORD* pdwDefault,
    BOOL* pbAutoLogonWithDefault
    )
{
    *pdwCount = _CredentialCount;
    *pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
    *pbAutoLogonWithDefault = FALSE;

    return S_OK;
}

HRESULT CSampleProvider::GetCredentialAt(
    DWORD dwIndex, 
    ICredentialProviderCredential** ppcpc
    )
{
    HRESULT hr = E_INVALIDARG;
    
    EnterCriticalSection(&cs);

    if ((dwIndex >= 0) 
        && (dwIndex < (DWORD)_CredentialCount)
        && ppcpc 
        && _CredentialList[dwIndex] != NULL) {
		hr = _CredentialList[dwIndex]->QueryInterface(
           IID_ICredentialProviderCredential, reinterpret_cast<void**>(ppcpc));	
    }

    LeaveCriticalSection(&cs);
        
    return hr;
}

HRESULT CSampleProvider_CreateInstance(REFIID riid, void** ppv)
{
    HRESULT hr;

    CSampleProvider* pProvider = new CSampleProvider();

    if (pProvider) {
        hr = pProvider->QueryInterface(riid, ppv);
        pProvider->Release();
    }
    else {
        hr = E_OUTOFMEMORY;
    }
    
    return hr;
}


======================================== CSampleProvider.h ================================
#pragma once

#include <credentialprovider.h>
#include <windows.h>
#include <strsafe.h>

#include "CommandWindow.h"
#include "CSampleCredential.h"
#include "MessageCredential.h"
#include "helpers.h"

class CCommandWindow;
class CSampleCredential;
class CMessageCredential;

class CSampleProvider : public ICredentialProvider
{
  public:
    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&_cRef);
    }
    
    STDMETHOD_(ULONG, Release)()
    {
        LONG cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHOD (QueryInterface)(REFIID riid, void** ppv)
    {
        HRESULT hr;
        if (IID_IUnknown == riid || 
            IID_ICredentialProvider == riid)
        {
            *ppv = this;
            reinterpret_cast<IUnknown*>(*ppv)->AddRef();
            hr = S_OK;
        }
        else
        {
            *ppv = NULL;
            hr = E_NOINTERFACE;
        }
        return hr;
    }

  public:
    IFACEMETHODIMP SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD dwFlags);
    IFACEMETHODIMP SetSerialization(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs);

    IFACEMETHODIMP Advise(__in ICredentialProviderEvents* pcpe, UINT_PTR upAdviseContext);
    IFACEMETHODIMP UnAdvise();

    IFACEMETHODIMP GetFieldDescriptorCount(__out DWORD* pdwCount);
    IFACEMETHODIMP GetFieldDescriptorAt(DWORD dwIndex,  __deref_out CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR** ppcpfd);

    IFACEMETHODIMP GetCredentialCount(__out DWORD* pdwCount,
                                      __out DWORD* pdwDefault,
                                      __out BOOL* pbAutoLogonWithDefault);
    IFACEMETHODIMP GetCredentialAt(DWORD dwIndex, 
                                   __out ICredentialProviderCredential** ppcpc);

    friend HRESULT CSampleProvider_CreateInstance(REFIID riid, __deref_out void** ppv);

public:
    void OnConnectStatusChanged();

  protected:
    CSampleProvider();
    __override ~CSampleProvider();

    void ClearCredentialList();
	void CreateCredentialList(const int Count);
    
private:
    typedef CSampleCredential   TCredential;
	typedef TCredential*        TPtrCredentialList;
	typedef TPtrCredentialList* TCredentialList;
	CRITICAL_SECTION            cs, cs2;
    TCredentialList             _CredentialList;
	DWORD						_CredentialCount;
    CCommandWindow              *_pCommandWindow;
    LONG                        _cRef;
    ICredentialProviderEvents   *_pcpe;
    UINT_PTR                    _upAdviseContext;
    CREDENTIAL_PROVIDER_USAGE_SCENARIO      _cpus;
};
Posted
Updated 14-Oct-10 7:11am
v2
Comments
Marc A. Brown 14-Oct-10 13:12pm    
Wrapped your code in code blocks for readability (such as it is).
Mr Nukealizer 17-Dec-10 16:51pm    
Wow that's a lot of code for one page!!! It gave my scroll wheel a workout!

1 solution

Hummm Credential development, better than the old GINA development, but need a very fast reboot computer, I used VMWare in another life.
I think that you have to synchronize on something, end of WM_PAINT may be,
and ignore all user request sent before this...
Also look quickly your code, did'nt see any registry call, if I remember each credential has to be registered in order to be available ?
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900