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;
hr = KerbInteractiveUnlockLogonInit(wsz, _rgFieldStrings[SFI_USERNAME], pwzProtectedPassword, _cpus, &kiul);
if (SUCCEEDED(hr)) {
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;
*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:
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:
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;
};