嗨,我正在努力在 Windows 10 中包装现有的凭据提供程序。我使用了来自https://github.com/Stexir/CredentialProvider/tree/501d955f02535b58bf10e09f3f3edaefa7a77fe8/samplewrapexistingcredentialprovider的示例代码。我已经修改了这段代码,所以它没有显示其他字段:
常见的.h
#include <credentialprovider.h>
#include <ntsecapi.h>
#define SECURITY_WIN32
#include <security.h>
#include <intsafe.h>
#define MAX_ULONG ((ULONG)(-1))
// The indexes of each of the fields in our credential provider's appended tiles.
enum SAMPLE_FIELD_ID
{
SFI_TILEIMAGE = 0,
SFI_NUM_FIELDS = 1, // Note: if new fields are added, keep NUM_FIELDS last. This is used as a count of the number of fields
};
// The first value indicates when the tile is displayed (selected, not selected)
// the second indicates things like whether the field is enabled, whether it has key focus, etc.
struct FIELD_STATE_PAIR
{
CREDENTIAL_PROVIDER_FIELD_STATE cpfs;
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE cpfis;
};
// These two arrays are seperate because a credential provider might
// want to set up a credential with various combinations of field state pairs
// and field descriptors.
// The field state value indicates whether the field is displayed
// in the selected tile, the deselected tile, or both.
// The Field interactive state indicates when
static const FIELD_STATE_PAIR s_rgFieldStatePairs[] =
{
{ CPFS_DISPLAY_IN_BOTH, CPFIS_NONE }, // SFI_TILEIMAGE
};
// Field descriptors for unlock and logon.
// The first field is the index of the field.
// The second is the type of the field.
// The third is the name of the field, NOT the value which will appear in the field.
static const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR s_rgCredProvFieldDescriptors[] =
{
{ SFI_TILEIMAGE, CPFT_TILE_IMAGE, L"Image" },
};
CSampleCredential.cpp
#ifndef WIN32_NO_STATUS
#include <ntstatus.h>
#define WIN32_NO_STATUS
#endif
#include <unknwn.h>
#include "CSampleCredential.h"
#include "CWrappedCredentialEvents.h"
#include "guid.h"
// CSampleCredential ////////////////////////////////////////////////////////
// NOTE: Please read the readme.txt file to understand when it's appropriate to
// wrap an another credential provider and when it's not. If you have questions
// about whether your scenario is an appropriate use of wrapping another credprov,
// please contact credprov@microsoft.com
CSampleCredential::CSampleCredential():
_cRef(1)
{
DllAddRef();
ZeroMemory(_rgCredProvFieldDescriptors, sizeof(_rgCredProvFieldDescriptors));
ZeroMemory(_rgFieldStatePairs, sizeof(_rgFieldStatePairs));
ZeroMemory(_rgFieldStrings, sizeof(_rgFieldStrings));
_pWrappedCredential = NULL;
_pWrappedCredentialEvents = NULL;
_pCredProvCredentialEvents = NULL;
_dwWrappedDescriptorCount = 0;
_dwDatabaseIndex = 0;
}
CSampleCredential::~CSampleCredential()
{
for (int i = 0; i < ARRAYSIZE(_rgFieldStrings); i++)
{
CoTaskMemFree(_rgFieldStrings[i]);
CoTaskMemFree(_rgCredProvFieldDescriptors[i].pszLabel);
}
_CleanupEvents();
if (_pWrappedCredential)
{
_pWrappedCredential->Release();
}
DllRelease();
}
// Initializes one credential with the field information passed in. We also keep track
// of our wrapped credential and how many fields it has.
HRESULT CSampleCredential::Initialize(
__in const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
__in const FIELD_STATE_PAIR* rgfsp,
__in ICredentialProviderCredential *pWrappedCredential,
__in DWORD dwWrappedDescriptorCount
)
{
HRESULT hr = S_OK;
// Grab the credential we're wrapping for future reference.
if (_pWrappedCredential != NULL)
{
_pWrappedCredential->Release();
}
_pWrappedCredential = pWrappedCredential;
_pWrappedCredential->AddRef();
// We also need to remember how many fields the inner credential has.
_dwWrappedDescriptorCount = dwWrappedDescriptorCount;
// Copy the field descriptors for each field. This is useful if you want to vary the field
// descriptors based on what Usage scenario the credential was created for.
for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(_rgCredProvFieldDescriptors); i++)
{
_rgFieldStatePairs[i] = rgfsp[i];
hr = FieldDescriptorCopy(rgcpfd[i], &_rgCredProvFieldDescriptors[i]);
}
return hr;
}
// LogonUI calls this in order to give us a callback in case we need to notify it of
// anything. We'll also provide it to the wrapped credential.
HRESULT CSampleCredential::Advise(
__in ICredentialProviderCredentialEvents* pcpce
)
{
HRESULT hr = S_OK;
_CleanupEvents();
// We keep a strong reference on the real ICredentialProviderCredentialEvents
// to ensure that the weak reference held by the CWrappedCredentialEvents is valid.
_pCredProvCredentialEvents = pcpce;
_pCredProvCredentialEvents->AddRef();
_pWrappedCredentialEvents = new CWrappedCredentialEvents();
if (_pWrappedCredentialEvents != NULL)
{
_pWrappedCredentialEvents->Initialize(this, pcpce);
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->Advise(_pWrappedCredentialEvents);
}
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
// LogonUI calls this to tell us to release the callback.
// We'll also provide it to the wrapped credential.
HRESULT CSampleCredential::UnAdvise()
{
HRESULT hr = S_OK;
if (_pWrappedCredential != NULL)
{
_pWrappedCredential->UnAdvise();
}
_CleanupEvents();
return hr;
}
// LogonUI calls this function when our tile is selected (zoomed)
// If you simply want fields to show/hide based on the selected state,
// there's no need to do anything here - you can set that up in the
// field definitions. In fact, we're just going to hand it off to the
// wrapped credential in case it wants to do something.
HRESULT CSampleCredential::SetSelected(__out BOOL* pbAutoLogon)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->SetSelected(pbAutoLogon);
}
return hr;
}
// Similarly to SetSelected, LogonUI calls this when your tile was selected
// and now no longer is. We'll let the wrapped credential do anything it needs.
HRESULT CSampleCredential::SetDeselected()
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->SetDeselected();
}
return hr;
}
// Get info for a particular field of a tile. Called by logonUI to get information to
// display the tile. We'll check to see if it's for us or the wrapped credential, and then
// handle or route it as appropriate.
HRESULT CSampleCredential::GetFieldState(
__in DWORD dwFieldID,
__out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
__out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis
)
{
HRESULT hr = E_UNEXPECTED;
// Make sure we have a wrapped credential.
if (_pWrappedCredential != NULL)
{
// Validate parameters.
if ((pcpfs != NULL) && (pcpfis != NULL))
{
// If the field is in the wrapped credential, hand it off.
if (_IsFieldInWrappedCredential(dwFieldID))
{
hr = _pWrappedCredential->GetFieldState(dwFieldID, pcpfs, pcpfis);
}
// Otherwise, we need to see if it's one of ours.
else
{
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
// If the field ID is valid, give it info it needs.
if (pfsp != NULL)
{
*pcpfs = pfsp->cpfs;
*pcpfis = pfsp->cpfis;
hr = S_OK;
}
else
{
hr = E_INVALIDARG;
}
}
}
else
{
hr = E_INVALIDARG;
}
}
return hr;
}
// Sets ppwsz to the string value of the field at the index dwFieldID. We'll check to see if
// it's for us or the wrapped credential, and then handle or route it as appropriate.
HRESULT CSampleCredential::GetStringValue(
__in DWORD dwFieldID,
__deref_out PWSTR* ppwsz
)
{
HRESULT hr = E_UNEXPECTED;
// Make sure we have a wrapped credential.
if (_pWrappedCredential != NULL)
{
// If this field belongs to the wrapped credential, hand it off.
if (_IsFieldInWrappedCredential(dwFieldID))
{
hr = _pWrappedCredential->GetStringValue(dwFieldID, ppwsz);
}
// Otherwise determine if we need to handle it.
else
{
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
if (pfsp != NULL)
{
//hr = SHStrDupW(_rgFieldStrings[SFI_I_WORK_IN_STATIC], ppwsz);
}
else
{
hr = E_INVALIDARG;
}
}
}
return hr;
}
// Returns the number of items to be included in the combobox (pcItems), as well as the
// currently selected item (pdwSelectedItem). We'll check to see if it's for us or the
// wrapped credential, and then handle or route it as appropriate.
HRESULT CSampleCredential::GetComboBoxValueCount(
__in DWORD dwFieldID,
__out DWORD* pcItems,
__out_range(<,*pcItems) DWORD* pdwSelectedItem
)
{
UNREFERENCED_PARAMETER(dwFieldID);
UNREFERENCED_PARAMETER(pcItems);
UNREFERENCED_PARAMETER(pdwSelectedItem);
return E_NOTIMPL;
}
// Called iteratively to fill the combobox with the string (ppwszItem) at index dwItem.
// We'll check to see if it's for us or the wrapped credential, and then handle or route
// it as appropriate.
HRESULT CSampleCredential::GetComboBoxValueAt(
__in DWORD dwFieldID,
__in DWORD dwItem,
__deref_out PWSTR* ppwszItem
)
{
UNREFERENCED_PARAMETER(dwFieldID);
UNREFERENCED_PARAMETER(dwItem);
UNREFERENCED_PARAMETER(ppwszItem);
return E_NOTIMPL;
}
// Called when the user changes the selected item in the combobox. We'll check to see if
// it's for us or the wrapped credential, and then handle or route it as appropriate.
HRESULT CSampleCredential::SetComboBoxSelectedValue(
__in DWORD dwFieldID,
__in DWORD dwSelectedItem
)
{
UNREFERENCED_PARAMETER(dwFieldID);
UNREFERENCED_PARAMETER(dwSelectedItem);
return E_NOTIMPL;
}
//-------------
// The following methods are for logonUI to get the values of various UI elements and
// then communicate to the credential about what the user did in that field. Even though
// we don't offer these field types ourselves, we need to pass along the request to the
// wrapped credential.
HRESULT CSampleCredential::GetBitmapValue(
__in DWORD dwFieldID,
__out 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(
__in DWORD dwFieldID,
__out DWORD* pdwAdjacentTo
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->GetSubmitButtonValue(dwFieldID, pdwAdjacentTo);
}
return hr;
}
HRESULT CSampleCredential::SetStringValue(
__in DWORD dwFieldID,
__in PCWSTR pwz
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->SetStringValue(dwFieldID, pwz);
}
return hr;
}
HRESULT CSampleCredential::GetCheckboxValue(
__in DWORD dwFieldID,
__out BOOL* pbChecked,
__deref_out PWSTR* ppwszLabel
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
if (_IsFieldInWrappedCredential(dwFieldID))
{
hr = _pWrappedCredential->GetCheckboxValue(dwFieldID, pbChecked, ppwszLabel);
}
}
return hr;
}
HRESULT CSampleCredential::SetCheckboxValue(
__in DWORD dwFieldID,
__in BOOL bChecked
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->SetCheckboxValue(dwFieldID, bChecked);
}
return hr;
}
HRESULT CSampleCredential::CommandLinkClicked(__in DWORD dwFieldID)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->CommandLinkClicked(dwFieldID);
}
return hr;
}
//------ end of methods for controls we don't have ourselves ----//
//
// Collect the username and password into a serialized credential for the correct usage scenario
// (logon/unlock is what's demonstrated in this sample). LogonUI then passes these credentials
// back to the system to log on.
//
HRESULT CSampleCredential::GetSerialization(
__out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
__out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
__deref_out_opt PWSTR* ppwszOptionalStatusText,
__out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->GetSerialization(pcpgsr, pcpcs, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
}
return hr;
}
// ReportResult is completely optional. However, we will hand it off to the wrapped
// credential in case they want to handle it.
HRESULT CSampleCredential::ReportResult(
__in NTSTATUS ntsStatus,
__in NTSTATUS ntsSubstatus,
__deref_out_opt PWSTR* ppwszOptionalStatusText,
__out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
)
{
HRESULT hr = E_UNEXPECTED;
if (_pWrappedCredential != NULL)
{
hr = _pWrappedCredential->ReportResult(ntsStatus, ntsSubstatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
}
return hr;
}
BOOL CSampleCredential::_IsFieldInWrappedCredential(
__in DWORD dwFieldID
)
{
return (dwFieldID < _dwWrappedDescriptorCount);
}
FIELD_STATE_PAIR *CSampleCredential::_LookupLocalFieldStatePair(
__in DWORD dwFieldID
)
{
// Offset into the ID to account for the wrapped fields.
dwFieldID -= _dwWrappedDescriptorCount;
// If the index if valid, give it the info it wants.
if (dwFieldID < SFI_NUM_FIELDS)
{
return &(_rgFieldStatePairs[dwFieldID]);
}
return NULL;
}
void CSampleCredential::_CleanupEvents()
{
// Call Uninitialize before releasing our reference on the real
// ICredentialProviderCredentialEvents to avoid having an
// invalid reference.
if (_pWrappedCredentialEvents != NULL)
{
_pWrappedCredentialEvents->Uninitialize();
_pWrappedCredentialEvents->Release();
_pWrappedCredentialEvents = NULL;
}
if (_pCredProvCredentialEvents != NULL)
{
_pCredProvCredentialEvents->Release();
_pCredProvCredentialEvents = NULL;
}
}
并获得第一个屏幕:Initial Screen。
当我提供用户名和密码时,它会成功登录。即使提供了正确的信息,我也不希望直接登录,而是创建将要求第二个因素的新字段(例如,静态密码)。
由于我是初学者,我需要知道在上面的凭据提供程序文件中必须做什么才能为密码字段添加第二个屏幕。
任何帮助/建议将不胜感激!