C++ - Windows Shell API - Wrong path while iterating through a folder - windows

Consider the following code:
bool ListFolderContent(const std::wstring& fileName)
{
PIDLIST_ABSOLUTE pidl = nullptr;
if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
return false;
IShellFolder* pShellfolder = nullptr;
LPCITEMIDLIST pidlRelative = nullptr;
HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);
if (FAILED(hr))
{
::CoTaskMemFree(pidl);
return false;
}
IEnumIDList* pEnumIDList = nullptr;
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
while (1)
{
LPITEMIDLIST pChild = nullptr;
hr = pEnumIDList->Next(1, &pChild, nullptr);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
if (hr == S_FALSE)
break;
wchar_t buffer[MAX_PATH + 1];
if (::SHGetPathFromIDListW(pChild, buffer))
{
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
}
pShellfolder->Release();
::CoTaskMemFree(pidl);
return true;
}
This code works well and logs the content of the folder owning the given file I pass through the fileName parameter.
However I have an issues with this code: Whatever I pass as file name, the logged path is always C:\Users\Admin\Desktop, and NOT the path to the parent folder I'm iterating, as expected. On the other hand the file names are correct.
Can someone explain me what I'm doing wrong?

You are passing just the last component ("filename") here: SHGetPathFromIDListW(pChild, buffer). Since the desktop is the root you are basically asking to get the filesystem path of [Desktop] [Filename] from the namespace.
There are two solutions:
pShellfolder->GetDisplayNameOf(pChild, SHGDN_FORPARSING, ...)+StrRetToBuf. This is fast since you already have an instance of the folder interface.
Call SHGetPathFromIDList with an absolute (full) pidl. On the pidl from SHILCreateFromPath, call ILCloneFull, ILRemoveLastID and ILCombine(clone, pChild).
HRESULT Example()
{
WCHAR buf[MAX_PATH];
GetWindowsDirectory(buf, MAX_PATH);
PathAppend(buf, L"Explorer.exe");
PIDLIST_ABSOLUTE pidl, pidlFullItem;
HRESULT hr = SHILCreateFromPath(buf, &pidl, nullptr); // %windir%\Explorer.exe
if (FAILED(hr)) return hr;
LPITEMIDLIST pLeaf;
IShellFolder*pShellfolder; // %windir%
hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, nullptr);
if (SUCCEEDED(hr))
{
IEnumIDList*pEnum;
// Method 1:
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
STRRET sr;
hr = pShellfolder->GetDisplayNameOf(pLeaf, SHGDN_FORPARSING, &sr);
if (SUCCEEDED(hr))
{
hr = StrRetToBuf(&sr, pLeaf, buf, MAX_PATH);
if (SUCCEEDED(hr)) wprintf(L"M1: Item: %s\n", buf);
}
}
pEnum->Release();
}
// Method 2:
ILRemoveLastID(pidl); // %windir%\Explorer.exe => %windir%
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
pidlFullItem = ILCombine(pidl, pLeaf); // %windir% + Filename
if (pidlFullItem)
{
hr = SHGetPathFromIDListW(pidlFullItem, buf);
if (SUCCEEDED(hr)) wprintf(L"M2: Item: %s\n", buf);
ILFree(pidlFullItem);
}
}
pEnum->Release();
}
pShellfolder->Release();
}
ILFree(pidl);
return hr;
}

Related

A disc's recursive path : Time to long C++

I want to build a class similar to "CFindDialog" with MFC Windows with color. I route the disk with a recursive function that uses "FindFirstFile". But when I switch to settings the system disk (C: . . .). The time is to long several seconds and so I'm on an SDD.
I have two questions:
Are there any low-level functions that allow you to go faster?
How does DLL comdlg32 go so fast?
Jean Bezet
I cannot find the "CFindDialog" class you mentioned, but you could try Windows Search, First is to index the drive you want to search(Only once):
Control Panel> Index Option> Modify, check the dirves.
There are some code samples in the document, and I also provide a sample without UI here. I made some simple modifications to this sample to specify the search content:
#include <windows.h>
#include <searchapi.h>
#include <iostream>
#include <atldbcli.h>
using namespace std;
class CMyAccessor
{
public:
WCHAR _szItemUrl[2048];
__int64 _size;
BEGIN_COLUMN_MAP(CMyAccessor)
COLUMN_ENTRY(1, _szItemUrl)
COLUMN_ENTRY(2, _size)
END_COLUMN_MAP()
};
HRESULT GetSQLStringFromParams(LCID lcidContentLocaleParam,
PCWSTR pszContentPropertiesParam,
LCID lcidKeywordLocaleParam,
LONG nMaxResultsParam,
PCWSTR pszSelectColumnsParam,
PCWSTR pszSortingParam,
SEARCH_QUERY_SYNTAX sqsSyntaxParam,
SEARCH_TERM_EXPANSION steTermExpansionParam,
PCWSTR pszWhereRestrictionsParam,
PCWSTR pszExprParam,
PWSTR* ppszSQL)
{
ISearchQueryHelper* pQueryHelper;
// Create an instance of the search manager
ISearchManager* pSearchManager;
HRESULT hr = CoCreateInstance(__uuidof(CSearchManager), NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pSearchManager));
if (SUCCEEDED(hr))
{
// Get the catalog manager from the search manager
ISearchCatalogManager* pSearchCatalogManager;
hr = pSearchManager->GetCatalog(L"SystemIndex", &pSearchCatalogManager);
if (SUCCEEDED(hr))
{
// Get the query helper from the catalog manager
hr = pSearchCatalogManager->GetQueryHelper(&pQueryHelper);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentLocale(lcidContentLocaleParam);
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryContentProperties(pszContentPropertiesParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryKeywordLocale(lcidKeywordLocaleParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryMaxResults(nMaxResultsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySelectColumns(pszSelectColumnsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySorting(pszSortingParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QuerySyntax(sqsSyntaxParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryTermExpansion(steTermExpansionParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->put_QueryWhereRestrictions(pszWhereRestrictionsParam);
}
if (SUCCEEDED(hr))
{
hr = pQueryHelper->GenerateSQLFromUserQuery(pszExprParam, ppszSQL);
}
pQueryHelper->Release();
}
pSearchCatalogManager->Release();
}
pSearchManager->Release();
}
return hr;
}
void WindowsSearch(PCWSTR szPath, PCWSTR szFileName)
{
PWSTR pszSQL;
wstring script = L"AND SCOPE ='file:///";
script += szPath;
script += L"'";
wstring filename = L"FileName:";
filename += szFileName;
HRESULT hr = GetSQLStringFromParams(1033, L"", 1033, -1, L"System.ItemPathDisplay, System.Size",
L"", SEARCH_ADVANCED_QUERY_SYNTAX, SEARCH_TERM_NO_EXPANSION, script.c_str(),
filename.c_str(), &pszSQL);
if (SUCCEEDED(hr))
{
wcout << L"Generated query: " << pszSQL << endl;
CDataSource cDataSource;
hr = cDataSource.OpenFromInitializationString(L"provider=Search.CollatorDSO.1;EXTENDED PROPERTIES='Application=Windows'");
if (SUCCEEDED(hr))
{
CSession cSession;
hr = cSession.Open(cDataSource);
if (SUCCEEDED(hr))
{
// cCommand is derived from CMyAccessor which has binding information in column map
// This allows ATL to put data directly into apropriate class members.
CCommand<CAccessor<CMyAccessor>, CRowset> cCommand;
hr = cCommand.Open(cSession, pszSQL);
if (SUCCEEDED(hr))
{
__int64 maxValue = 0;
__int64 minValue = ULONG_MAX;
for (hr = cCommand.MoveFirst(); S_OK == hr; hr = cCommand.MoveNext())
{
wcout << cCommand._szItemUrl << L": " << cCommand._size << L" bytes" << endl;
maxValue = max(maxValue, cCommand._size);
minValue = min(minValue, cCommand._size);
}
wcout << L"Max:" << maxValue << L"Min:" << minValue << endl;
cCommand.Close();
}
cCommand.ReleaseCommand();
}
}
CoTaskMemFree(pszSQL);
}
}
int main()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
WindowsSearch(L"C:\\", L"test*.txt");
CoUninitialize();
}
}
Thank you for your response. That doesn't quite address my problem. My function is recursive it broswse a hard drive. The travel time is too important when I throw it on c: (approximately 660,000 files). I memorize this description in a string array. If I remove this memorization it does not change the real time. Here's my function:
<<
bool Parcours_disque::EnumerateFolder(LPCWSTR lpcszFolder, DWORD nLevel)
{
// LPWIN32_FIND_DATAA fdd = __nullptr;
WIN32_FIND_DATAW fdd;
HANDLE hFind = INVALID_HANDLE_VALUE;
hFind = FindFirstFileW((lpcszFolder),(LPWIN32_FIND_DATAW) &fdd); // (LPCWSTR)
if (INVALID_HANDLE_VALUE == hFind)
{
return false;
}
do
{
if (fdd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY))
{
if (_T('.') != fdd.cFileName[0])
{
if (_T('$') != fdd.cFileName[0])
{
// Store in buffer
.....
EnumerateFolder(((CString)lpcszFolder).SpanExcluding(_T("*")) + (CString)fdd.cFileName + _T("\\*"), nLevel + 1); // Here's the recursive call!
}
}
}
else
{
// Store in buffer
.....
}
} while (FindNextFileW(hFind, (LPWIN32_FIND_DATAW) &fdd) != 0);
FindClose(hFind);
return true;
}
Microsoft does the same with CFileDialog object (MFC) and when you go through the disk it's immediate how does it do it? hidden functions?
Thank you

Windows Custom Credentials Provider using MSV1_0 fails with INVALID_PARAMETER

I am trying to implement a local login from my custom Credentials Provider. For that, I try to use the MSV1_0 authentication package, but it keeps failing, yielding an INVALID_PARAMETER status.
The code seems like that:
static void _UnicodeStringPackedUnicodeStringCopy(
const UNICODE_STRING& rus,
PWSTR pwzBuffer,
UNICODE_STRING* pus
) {
pus->Length = rus.Length;
pus->MaximumLength = rus.Length;
pus->Buffer = pwzBuffer;
CopyMemory(pus->Buffer, rus.Buffer, pus->Length);
}
HRESULT LsaInitStringW(PUNICODE_STRING pszDestinationString, PCWSTR pszSourceString)
{
size_t cchLength;
HRESULT hr = StringCchLengthW(pszSourceString, USHORT_MAX, &cchLength);
if (SUCCEEDED(hr))
{
USHORT usLength;
hr = SizeTToUShort(cchLength, &usLength);
if (SUCCEEDED(hr))
{
pszDestinationString->Buffer = (PWCHAR)pszSourceString;
pszDestinationString->Length = usLength * sizeof(WCHAR);
pszDestinationString->MaximumLength = pszDestinationString->Length + 1;
hr = S_OK;
}
}
return hr;
}
HRESULT MsvLogonPack(
const MSV1_0_INTERACTIVE_LOGON& milIn,
BYTE** prgb,
DWORD* pcb
) {
size_t cb = sizeof(milIn)
+ milIn.LogonDomainName.Length
+ milIn.UserName.Length
+ milIn.Password.Length;
MSV1_0_INTERACTIVE_LOGON* milOut = (MSV1_0_INTERACTIVE_LOGON*)CoTaskMemAlloc(cb);
if (!milOut) {
return E_OUTOFMEMORY;
}
milOut->MessageType = milIn.MessageType;
BYTE *pbBuffer = (BYTE*)milOut + sizeof(*milOut);
_UnicodeStringPackedUnicodeStringCopy(milIn.LogonDomainName, (PWSTR)pbBuffer, &milOut->LogonDomainName);
pbBuffer += milOut->LogonDomainName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.UserName, (PWSTR)pbBuffer, &milOut->UserName);
pbBuffer += milOut->UserName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.Password, (PWSTR)pbBuffer, &milOut->Password);
pbBuffer += milOut->Password.Length;
if (pbBuffer != (BYTE*)milOut + cb) {
return E_ABORT;
}
*prgb = (BYTE*)milOut;
*pcb = cb;
return S_OK;
}
HRESULT GetMsvPackage(ULONG * pulAuthPackage) {
HRESULT hr;
HANDLE hLsa;
NTSTATUS status = LsaConnectUntrusted(&hLsa);
if (SUCCEEDED(HRESULT_FROM_NT(status))) {
ULONG ulAuthPackage;
LSA_STRING lsaszKerberosName;
LsaInitString(&lsaszKerberosName, MSV1_0_PACKAGE_NAME);
status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
if (SUCCEEDED(HRESULT_FROM_NT(status))) {
*pulAuthPackage = ulAuthPackage;
hr = S_OK;
}
else {
hr = HRESULT_FROM_NT(status);
}
LsaDeregisterLogonProcess(hLsa);
}
else {
hr = HRESULT_FROM_NT(status);
}
return hr;
}
HRESULT MyCredential::CompleteAuthentication(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
PWSTR* ppwszOptionalStatusText,
CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) {
HRESULT hr;
pcpcs->clsidCredentialProvider = CLSID_MyProvider;
MSV1_0_INTERACTIVE_LOGON mil;
mil.MessageType = MsV1_0WorkstationUnlockLogon;
hr = LsaInitStringW(&mil.LogonDomainName, L"");
if (SUCCEEDED(hr)) hr = LsaInitStringW(&mil.UserName, L"tester");
if (SUCCEEDED(hr)) hr = LsaInitStringW(&mil.Password, L"12345");
if (SUCCEEDED(hr)) {
hr = MsvLogonPack(mil, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
if (SUCCEEDED(hr)) {
ULONG ulAuthPackage;
hr = GetMsvPackage(&ulAuthPackage);
if (SUCCEEDED(hr)) {
pcpcs->ulAuthenticationPackage = ulAuthPackage;
}
}
}
return hr;
}
This keeps giving a status of INVALID_PARAMETER, with sub-status 0. I tried replacing the MsV1_0InteractiveLogon with MsV1_0WorkstationUnlockLogon, which got me a status of STATUS_LOGON_FAILURE with sub-status INTERNAL_ERROR.
What would be suggested to solve this issue?
After some research and trial, I figured out the problem. The issue was in the Unicode Strings being absolute, while they are required to be relative to the start of the structure. So I made them relative:
_UnicodeStringPackedUnicodeStringCopy(milIn.LogonDomainName, (PWSTR)pbBuffer, &milOut->LogonDomainName);
milOut->LogonDomainName.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->LogonDomainName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.UserName, (PWSTR)pbBuffer, &milOut->UserName);
milOut->UserName.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->UserName.Length;
_UnicodeStringPackedUnicodeStringCopy(milIn.Password, (PWSTR)pbBuffer, &milOut->Password);
milOut->Password.Buffer = (PWSTR)(pbBuffer - (BYTE*)milOut);
pbBuffer += milOut->Password.Length;
This behavior is documented for KERB_CERTIFICATE_LOGON structure, but not for MSV1_0_INTERACTIVE_LOGON, for some reason.

Credential provider not displayed for all users (Other user included)

I am trying to show credential provider for all local and for other user(Domain users) in signing options but I am not able to. I developed this credential provider taking reference from here. I made following changes in _EnumerateCredentials and GetCredentialCount functions of CSampleprovider.cpp file.
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;
}
}
And I have changed a private header of type CSampleCredential to
std::vector<CSampleCredential> _pCredentialVector;
// SampleV2CredentialCSampleProvider.h
in CSampleProvider.h file.
When I tested this Credential provider of mine it is working fine i.e it is displaying for all local users in sign in options when no domain is added (No other user) but when domain is added(other user enabled) then I am being stuck at the welcome wallpaper and screen keeps on flickering.
So, How do I display my Credential provider for all local and domain users (Other user) in sign in options and over come that flickering of screen. I am new to this VC++ please help me out.
While I was trying to enable the custom credential provider to all the tiles including the other user tile I made few changes in GetCredentialCount() method and _EnumerateCredentials() method in SampleProvider.cpp file in Credential provider Sample given by Microsoft. The changes I made are:
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;
}
Now as you see we are sending nullptr as one of the argument while calling Initialize() method after checking if system is connected to domain, we need to handle that nullptr in Initialize() method by checking with if condition in SampleCredential.cpp file.
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;}
Using above code you can solve flickering (which is crashing of CP) as well as enable Credential provider for all user tiles.

Getting GUID of group with IADs interface

I'm using the following C++ code to get all groups for a user:
(from https://msdn.microsoft.com/en-us/library/windows/desktop/aa746342(v=vs.85).aspx. I just added a few liens to get the group GUID as well)
HRESULT CheckUserGroups(IADsUser *pUser)
{
IADsMembers *pGroups;
HRESULT hr = S_OK;
hr = pUser->Groups(&pGroups);
pUser->Release();
if (FAILED(hr)) return hr;
IUnknown *pUnk;
hr = pGroups->get__NewEnum(&pUnk);
if (FAILED(hr)) return hr;
pGroups->Release();
IEnumVARIANT *pEnum;
hr = pUnk->QueryInterface(IID_IEnumVARIANT, (void**)&pEnum);
if (FAILED(hr)) return hr;
pUnk->Release();
// Enumerate.
BSTR bstr;
VARIANT var;
IADs *pADs;
ULONG lFetch;
IDispatch *pDisp;
VariantInit(&var);
hr = pEnum->Next(1, &var, &lFetch);
while (hr == S_OK)
{
if (lFetch == 1)
{
pDisp = V_DISPATCH(&var);
pDisp->QueryInterface(IID_IADs, (void**)&pADs);
pADs->get_Name(&bstr);
printf("Group belonged: %S\n", bstr);
SysFreeString(bstr);
pADs->get_GUID(&bstr);
printf("Group guid: %S\n", bstr);
SysFreeString(bstr);
pADs->Release();
}
VariantClear(&var);
pDisp = NULL;
hr = pEnum->Next(1, &var, &lFetch);
};
hr = pEnum->Release();
return S_OK;
}
int main()
{
CoInitialize(NULL);
CComPtr<IADsUser> pUser;
std::wstring ADpathName = std::wstring(L"WinNT://domain/user");
if( ADsGetObject(ADpathName.c_str(), IID_IADsUser, (void**)&pUser) == S_OK )
{
CheckUserGroups(pUser);
}
return 0;
}
However, the GUID for all groups is the same and doesn't reflect the real group GUID. why is that?

How to save ID2D1Bitmap to PNG file

I faced a problem when trying to save ID2D1Bitmap (that created from ID2D1HwndRenderTarget) to PNG File. The output image is just empty with white color. HRESULT returned from function call EndDraw() is -2003238894.
Thanks for any help.
Here is my code:
HRESULT CImageUtil::SaveBitmapToFile(PCWSTR uri,ID2D1Bitmap* pBitmap,ID2D1RenderTarget* pRenderTarget)
{
HRESULT hr = S_OK;
ID2D1Factory *pD2DFactory = NULL;
IWICBitmap *pWICBitmap = NULL;
ID2D1RenderTarget *pRT = NULL;
IWICBitmapEncoder *pEncoder = NULL;
IWICBitmapFrameEncode *pFrameEncode = NULL;
IWICStream *pStream = NULL;
if (SUCCEEDED(hr))
{
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
}
//
// Create IWICBitmap and RT
//
UINT sc_bitmapWidth = pBitmap->GetSize().width;
UINT sc_bitmapHeight = pBitmap->GetSize().height;
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateBitmap(
sc_bitmapWidth,
sc_bitmapHeight,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
}
if (SUCCEEDED(hr))
{
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
hr = pD2DFactory->CreateWicBitmapRenderTarget(
pWICBitmap,
rtProps,
&pRT
);
}
if (SUCCEEDED(hr))
{
//
// Render into the bitmap
//
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
pRT->DrawBitmap(pBitmap);
pRT->EndDraw();
}
if (SUCCEEDED(hr))
{
//
// Save image to file
//
hr = m_pWICFactory->CreateStream(&pStream);
}
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
if (SUCCEEDED(hr))
{
hr = pStream->InitializeFromFilename(uri, GENERIC_WRITE);
}
if (SUCCEEDED(hr))
{
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &pEncoder);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = pEncoder->CreateNewFrame(&pFrameEncode, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Initialize(NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetSize(sc_bitmapWidth, sc_bitmapHeight);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->SetPixelFormat(&format);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr))
{
hr = pFrameEncode->Commit();
}
if (SUCCEEDED(hr))
{
hr = pEncoder->Commit();
}
SafeRelease(&pD2DFactory);
SafeRelease(&pWICBitmap);
SafeRelease(&pRT);
SafeRelease(&pEncoder);
SafeRelease(&pFrameEncode);
SafeRelease(&pStream);
return hr;
}
How would you even know if you had an error, since you just swallow errors and continue instead of logging where they came from? You get a non-zero hresult, so first figure out which function it comes from by adding a printf or fprintf after every single function call. And you have a glaring omission in the block:
if (SUCCEEDED(hr))
{
//
// Render into the bitmap
//
pRT->BeginDraw();
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
pRT->DrawBitmap(pBitmap);
pRT->EndDraw();
}
if (SUCCEEDED(hr))
You don't bother to assign hr anywhere in there, so you wouldn't even know if any of them are erroring out. Obviously, Clear() and the final png writing work fine, since you get a good file, so it's DrawBitmap or one of the bitmap creation calls that are failing.

Resources