未为所有用户显示凭据提供程序(包括其他用户)
Credential provider not displayed for all users (Other user included)
我试图在签名选项中为所有本地用户和其他用户(域用户)显示凭据提供程序,但我做不到。我参考 here 开发了这个凭证提供程序。我对 CSampleprovider.cpp 文件的 _EnumerateCredentials 和 GetCredentialCount 函数进行了以下更改。
GetCredentialCount()
HRESULT GetCredentialCount([out] DWORD* pdwCount,
[out] DWORD* pdwDefault,
[out] BOOL* pbAutoLogonWithDefault)
{
*pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
*pbAutoLogonWithDefault = FALSE;
if (_fRecreateEnumeratedCredentials)
{
_fRecreateEnumeratedCredentials = false;
_ReleaseEnumeratedCredentials();
_CreateEnumeratedCredentials();
}
DWORD dwUserCount;
HRESULT hr;
if (_pCredProviderUserArray != nullptr) {
hr = _pCredProviderUserArray->GetCount(&dwUserCount);
}
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
dwUserCount += 1;//display additional empty tile
}
*pdwCount = dwUserCount;
return S_OK;
}
_EnumerateCredentials()
HRESULT CSampleProvider::_EnumerateCredentials()
{
HRESULT hr = E_UNEXPECTED;
DWORD dwUserCount;
if (_pCredProviderUserArray != nullptr)
{
//DWORD dwUserCount = 0;
_pCredProviderUserArray->GetCount(&dwUserCount);
if (dwUserCount > 0)
{
//_pCredential = new CSampleCredential*[dwUserCount];
for (DWORD i = 0; i < dwUserCount; i++) {
ICredentialProviderUser* pCredUser;
hr = _pCredProviderUserArray->GetAt(i, &pCredUser);
if (SUCCEEDED(hr))
{
//_pCredential[i] = new(std::nothrow) CSampleCredential();
_pCredential.push_back(new(std::nothrow) CSampleCredential());
if (_pCredential[i] != nullptr)
{
//logfile << "new CSampleCredential()\n";
hr = _pCredential[i]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
if (FAILED(hr))
{
_pCredential[i]->Release();
_pCredential[i] = nullptr;
}
}
else
{
hr = E_OUTOFMEMORY;
}
pCredUser->Release();
}
}
}
//if you are in a domain or have no users on the list you have to show "Other user tile"
if (DEVELOPING) PrintLn(L"IsOS(OS_DOMAINMEMBER): %d", IsOS(OS_DOMAINMEMBER));
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
if (DEVELOPING) PrintLn(L"Adding empty user tile");
_pCredential.push_back(new(std::nothrow) CSampleCredential());
if (_pCredential[_pCredential.size() - 1] != nullptr) {
hr = _pCredential[_pCredential.size() - 1]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, nullptr);
}
else {
if (DEVELOPING) PrintLn(L"Error adding user: %d", _pCredential.size());
}
}
return hr;
}
}
并且我已经将 CSampleCredential
类型的私有 header 更改为
std::vector<CSampleCredential> _pCredentialVector;
// SampleV2CredentialCSampleProvider.h
在 CSampleProvider.h 文件中。
当我测试我的这个凭据提供程序时,它工作正常,即当没有添加域(没有其他用户)但是当添加域(启用其他用户)时,它会为所有本地用户显示登录选项我一直卡在欢迎壁纸上,屏幕一直在闪烁。
那么,我如何在登录选项中为所有本地和域用户(其他用户)显示我的凭据提供程序并克服屏幕闪烁。我是新手 VC++ 请帮助我。
当我尝试为包括其他用户磁贴在内的所有磁贴启用自定义凭据提供程序时,我对 SampleProvider.cpp 文件中的 GetCredentialCount()
方法和 _EnumerateCredentials()
方法进行了一些更改Microsoft 提供的凭据提供程序示例。我所做的更改是:
HRESULT CServiceProvider::GetCredentialCount(
_Out_ DWORD *pdwCount,
_Out_ DWORD *pdwDefault,
_Out_ BOOL *pbAutoLogonWithDefault){
*pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
*pbAutoLogonWithDefault = FALSE;
if (_fRecreateEnumeratedCredentials)
{
_fRecreateEnumeratedCredentials = false;
_ReleaseEnumeratedCredentials();
_CreateEnumeratedCredentials();
}
DWORD dwUserCount;
HRESULT hr;
if (_pCredProviderUserArray != nullptr) {
hr = _pCredProviderUserArray->GetCount(&dwUserCount);
}
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
dwUserCount += 1;//display additional empty tile
}
*pdwCount = dwUserCount;
return S_OK;}
HRESULT CServiceProvider::_EnumerateCredentials(){
HRESULT hr = E_UNEXPECTED;
DWORD dwUserCount;
if (_pCredProviderUserArray != nullptr)
{
_pCredProviderUserArray->GetCount(&dwUserCount);
if (dwUserCount > 0)
{
//You need to initialize all the fields in LogonUI for each and every user
for (DWORD i = 0; i < dwUserCount; i++) {
ICredentialProviderUser* pCredUser;
hr = _pCredProviderUserArray->GetAt(i, &pCredUser);
if (SUCCEEDED(hr))
{
_pCredential.push_back(new(std::nothrow) CUserCredential());
if (_pCredential[i] != nullptr)
{
hr = _pCredential[i]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
if (FAILED(hr))
{
_pCredential[i]->Release();
_pCredential[i] = nullptr;
}
}
else
{
hr = E_OUTOFMEMORY;
}
pCredUser->Release();
}
}
}
//if you are in a domain or have no users on the list you have to show "Other user tile"
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
_pCredential.push_back(new(std::nothrow) CUserCredential());
if (_pCredential[_pCredential.size() - 1] != nullptr) {
hr = _pCredential[_pCredential.size() - 1]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, nullptr);
}
}
return hr;
}
return hr;
}
现在如您所见,我们在检查系统是否连接到域后调用 Initialize()
方法时将 nullptr
作为参数之一发送,我们需要在中处理 nullptr
Initialize()
方法通过检查 SampleCredential.cpp 文件中的 if 条件。
HRESULT CUserCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
_In_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR const* rgcpfd,
_In_ FIELD_STATE_PAIR const* rgfsp,
_In_ ICredentialProviderUser* pcpUser){
HRESULT hr = S_OK;
_cpus = cpus;
_nNextScreenID = e_ARSNone;
GUID guidProvider;
LPOLESTR clsid;
if (pcpUser != nullptr) {
pcpUser->GetProviderID(&guidProvider);
StringFromCLSID(guidProvider, &clsid);
CoTaskMemFree(clsid);
_fIsLocalUser = (guidProvider == Identity_LocalUserProvider);
}
else {
_fIsLocalUser = true;//CP V1 or Domain
}
// 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]);
}
// Initialize the String value of all the fields.
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"SomeLable1", &_rgFieldStrings[SFI_LABEL]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"SomeLable2", &_rgFieldStrings[SFI_LARGE_TEXT]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"", &_rgFieldStrings[SFI_PASSWORD]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"Somelabel4", &_rgFieldStrings[SFI_SUBMIT_BUTTON]);
}
hr = S_OK;
if (SUCCEEDED(hr))
{
if (pcpUser != nullptr) {
hr = pcpUser->GetStringValue(PKEY_Identity_QualifiedUserName, &_pszQualifiedUserName);//get username from the LogonUI user object
PWSTR pszUserName1;
pcpUser->GetStringValue(PKEY_Identity_UserName, &pszUserName1);
if (_fIsLocalUser) {
PWSTR pszUserName;
pcpUser->GetStringValue(PKEY_Identity_UserName, &pszUserName);
if (pszUserName != nullptr)
{
wchar_t szString[256];
StringCchPrintf(szString, ARRAYSIZE(szString), L"User Name: %s", pszUserName);
if (DEVELOPING) PrintLn(szString);
hr = SHStrDupW(pszUserName, &_rgFieldStrings[SFI_LARGE_TEXT]);
CoTaskMemFree(pszUserName);
}
else
{
hr = SHStrDupW(L"User Name is NULL", &_rgFieldStrings[SFI_LARGE_TEXT]);
}
}
else {
if (DEVELOPING) PrintLn(L"Domain user, skip SFI_LARGE_TEXT");
}
}
else {
PWSTR connectedDomainName = getNetworkName();
wchar_t szString[256];
StringCchPrintf(szString, ARRAYSIZE(szString), L"Sign in to: %s", connectedDomainName);
hr = SHStrDupW(szString, &_rgFieldStrings[SFI_DOMAIN_NAME_TEXT]);
if (DEVELOPING) PrintLn("Unknown user -> display LoginName");
hr = SHStrDupW(L"", &_pszQualifiedUserName);
_fUserNameVisible = true;
_rgFieldStatePairs[SFI_LOGIN_NAME].cpfs = CPFS_DISPLAY_IN_SELECTED_TILE;//unhide login name
//switch focus to login
_rgFieldStatePairs[SFI_LOGIN_NAME].cpfis = CPFIS_FOCUSED;
_rgFieldStatePairs[SFI_PASSWORD].cpfis = CPFIS_NONE;
//Don't panic!!!
}
}
if (pcpUser != nullptr)
{
hr = pcpUser->GetSid(&_pszUserSid);
}
return hr;}
使用上面的代码,您可以解决闪烁(CP 崩溃)以及为所有用户图块启用凭据提供程序。
我试图在签名选项中为所有本地用户和其他用户(域用户)显示凭据提供程序,但我做不到。我参考 here 开发了这个凭证提供程序。我对 CSampleprovider.cpp 文件的 _EnumerateCredentials 和 GetCredentialCount 函数进行了以下更改。
GetCredentialCount()
HRESULT GetCredentialCount([out] DWORD* pdwCount,
[out] DWORD* pdwDefault,
[out] BOOL* pbAutoLogonWithDefault)
{
*pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
*pbAutoLogonWithDefault = FALSE;
if (_fRecreateEnumeratedCredentials)
{
_fRecreateEnumeratedCredentials = false;
_ReleaseEnumeratedCredentials();
_CreateEnumeratedCredentials();
}
DWORD dwUserCount;
HRESULT hr;
if (_pCredProviderUserArray != nullptr) {
hr = _pCredProviderUserArray->GetCount(&dwUserCount);
}
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
dwUserCount += 1;//display additional empty tile
}
*pdwCount = dwUserCount;
return S_OK;
}
_EnumerateCredentials()
HRESULT CSampleProvider::_EnumerateCredentials()
{
HRESULT hr = E_UNEXPECTED;
DWORD dwUserCount;
if (_pCredProviderUserArray != nullptr)
{
//DWORD dwUserCount = 0;
_pCredProviderUserArray->GetCount(&dwUserCount);
if (dwUserCount > 0)
{
//_pCredential = new CSampleCredential*[dwUserCount];
for (DWORD i = 0; i < dwUserCount; i++) {
ICredentialProviderUser* pCredUser;
hr = _pCredProviderUserArray->GetAt(i, &pCredUser);
if (SUCCEEDED(hr))
{
//_pCredential[i] = new(std::nothrow) CSampleCredential();
_pCredential.push_back(new(std::nothrow) CSampleCredential());
if (_pCredential[i] != nullptr)
{
//logfile << "new CSampleCredential()\n";
hr = _pCredential[i]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
if (FAILED(hr))
{
_pCredential[i]->Release();
_pCredential[i] = nullptr;
}
}
else
{
hr = E_OUTOFMEMORY;
}
pCredUser->Release();
}
}
}
//if you are in a domain or have no users on the list you have to show "Other user tile"
if (DEVELOPING) PrintLn(L"IsOS(OS_DOMAINMEMBER): %d", IsOS(OS_DOMAINMEMBER));
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
if (DEVELOPING) PrintLn(L"Adding empty user tile");
_pCredential.push_back(new(std::nothrow) CSampleCredential());
if (_pCredential[_pCredential.size() - 1] != nullptr) {
hr = _pCredential[_pCredential.size() - 1]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, nullptr);
}
else {
if (DEVELOPING) PrintLn(L"Error adding user: %d", _pCredential.size());
}
}
return hr;
}
}
并且我已经将 CSampleCredential
类型的私有 header 更改为
std::vector<CSampleCredential> _pCredentialVector;
// SampleV2CredentialCSampleProvider.h
在 CSampleProvider.h 文件中。
当我测试我的这个凭据提供程序时,它工作正常,即当没有添加域(没有其他用户)但是当添加域(启用其他用户)时,它会为所有本地用户显示登录选项我一直卡在欢迎壁纸上,屏幕一直在闪烁。
那么,我如何在登录选项中为所有本地和域用户(其他用户)显示我的凭据提供程序并克服屏幕闪烁。我是新手 VC++ 请帮助我。
当我尝试为包括其他用户磁贴在内的所有磁贴启用自定义凭据提供程序时,我对 SampleProvider.cpp 文件中的 GetCredentialCount()
方法和 _EnumerateCredentials()
方法进行了一些更改Microsoft 提供的凭据提供程序示例。我所做的更改是:
HRESULT CServiceProvider::GetCredentialCount(
_Out_ DWORD *pdwCount,
_Out_ DWORD *pdwDefault,
_Out_ BOOL *pbAutoLogonWithDefault){
*pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
*pbAutoLogonWithDefault = FALSE;
if (_fRecreateEnumeratedCredentials)
{
_fRecreateEnumeratedCredentials = false;
_ReleaseEnumeratedCredentials();
_CreateEnumeratedCredentials();
}
DWORD dwUserCount;
HRESULT hr;
if (_pCredProviderUserArray != nullptr) {
hr = _pCredProviderUserArray->GetCount(&dwUserCount);
}
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
dwUserCount += 1;//display additional empty tile
}
*pdwCount = dwUserCount;
return S_OK;}
HRESULT CServiceProvider::_EnumerateCredentials(){
HRESULT hr = E_UNEXPECTED;
DWORD dwUserCount;
if (_pCredProviderUserArray != nullptr)
{
_pCredProviderUserArray->GetCount(&dwUserCount);
if (dwUserCount > 0)
{
//You need to initialize all the fields in LogonUI for each and every user
for (DWORD i = 0; i < dwUserCount; i++) {
ICredentialProviderUser* pCredUser;
hr = _pCredProviderUserArray->GetAt(i, &pCredUser);
if (SUCCEEDED(hr))
{
_pCredential.push_back(new(std::nothrow) CUserCredential());
if (_pCredential[i] != nullptr)
{
hr = _pCredential[i]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser);
if (FAILED(hr))
{
_pCredential[i]->Release();
_pCredential[i] = nullptr;
}
}
else
{
hr = E_OUTOFMEMORY;
}
pCredUser->Release();
}
}
}
//if you are in a domain or have no users on the list you have to show "Other user tile"
if ((dwUserCount == 0) || (IsOS(OS_DOMAINMEMBER) == 1)) {
_pCredential.push_back(new(std::nothrow) CUserCredential());
if (_pCredential[_pCredential.size() - 1] != nullptr) {
hr = _pCredential[_pCredential.size() - 1]->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, nullptr);
}
}
return hr;
}
return hr;
}
现在如您所见,我们在检查系统是否连接到域后调用 Initialize()
方法时将 nullptr
作为参数之一发送,我们需要在中处理 nullptr
Initialize()
方法通过检查 SampleCredential.cpp 文件中的 if 条件。
HRESULT CUserCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
_In_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR const* rgcpfd,
_In_ FIELD_STATE_PAIR const* rgfsp,
_In_ ICredentialProviderUser* pcpUser){
HRESULT hr = S_OK;
_cpus = cpus;
_nNextScreenID = e_ARSNone;
GUID guidProvider;
LPOLESTR clsid;
if (pcpUser != nullptr) {
pcpUser->GetProviderID(&guidProvider);
StringFromCLSID(guidProvider, &clsid);
CoTaskMemFree(clsid);
_fIsLocalUser = (guidProvider == Identity_LocalUserProvider);
}
else {
_fIsLocalUser = true;//CP V1 or Domain
}
// 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]);
}
// Initialize the String value of all the fields.
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"SomeLable1", &_rgFieldStrings[SFI_LABEL]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"SomeLable2", &_rgFieldStrings[SFI_LARGE_TEXT]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"", &_rgFieldStrings[SFI_PASSWORD]);
}
if (SUCCEEDED(hr))
{
hr = SHStrDupW(L"Somelabel4", &_rgFieldStrings[SFI_SUBMIT_BUTTON]);
}
hr = S_OK;
if (SUCCEEDED(hr))
{
if (pcpUser != nullptr) {
hr = pcpUser->GetStringValue(PKEY_Identity_QualifiedUserName, &_pszQualifiedUserName);//get username from the LogonUI user object
PWSTR pszUserName1;
pcpUser->GetStringValue(PKEY_Identity_UserName, &pszUserName1);
if (_fIsLocalUser) {
PWSTR pszUserName;
pcpUser->GetStringValue(PKEY_Identity_UserName, &pszUserName);
if (pszUserName != nullptr)
{
wchar_t szString[256];
StringCchPrintf(szString, ARRAYSIZE(szString), L"User Name: %s", pszUserName);
if (DEVELOPING) PrintLn(szString);
hr = SHStrDupW(pszUserName, &_rgFieldStrings[SFI_LARGE_TEXT]);
CoTaskMemFree(pszUserName);
}
else
{
hr = SHStrDupW(L"User Name is NULL", &_rgFieldStrings[SFI_LARGE_TEXT]);
}
}
else {
if (DEVELOPING) PrintLn(L"Domain user, skip SFI_LARGE_TEXT");
}
}
else {
PWSTR connectedDomainName = getNetworkName();
wchar_t szString[256];
StringCchPrintf(szString, ARRAYSIZE(szString), L"Sign in to: %s", connectedDomainName);
hr = SHStrDupW(szString, &_rgFieldStrings[SFI_DOMAIN_NAME_TEXT]);
if (DEVELOPING) PrintLn("Unknown user -> display LoginName");
hr = SHStrDupW(L"", &_pszQualifiedUserName);
_fUserNameVisible = true;
_rgFieldStatePairs[SFI_LOGIN_NAME].cpfs = CPFS_DISPLAY_IN_SELECTED_TILE;//unhide login name
//switch focus to login
_rgFieldStatePairs[SFI_LOGIN_NAME].cpfis = CPFIS_FOCUSED;
_rgFieldStatePairs[SFI_PASSWORD].cpfis = CPFIS_NONE;
//Don't panic!!!
}
}
if (pcpUser != nullptr)
{
hr = pcpUser->GetSid(&_pszUserSid);
}
return hr;}
使用上面的代码,您可以解决闪烁(CP 崩溃)以及为所有用户图块启用凭据提供程序。