I use sample code of "Credential Providers in Windows 10". I develop my code and change it very good.
But I have some problem with it:
It is not enable (Show tile) for all users in my windows. (Show for one user only)
I See this questions:
https://stackoverflow.com/a/31247811/3477587
Credential provider not displayed for all users (Other user included)
So I change my code but when use from my DLL win-login don't load and I must delete in in safe-mode to I can enter to my windows without Credential Providers.
I change my code as follow:
std::vector<CSamanCredential*> _pCredential; // SampleV2Credential
HRESULT CSamanProvider::GetCredentialAt(
DWORD dwIndex,
_Outptr_result_nullonfailure_ ICredentialProviderCredential** ppcpc)
{
HRESULT hr = E_INVALIDARG;
*ppcpc = nullptr;
hr = _pCredential[dwIndex]->QueryInterface(IID_PPV_ARGS(ppcpc));
return hr;
}
HRESULT CSamanProvider::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 = 0;
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;
}
void CSamanProvider::_ReleaseEnumeratedCredentials()
{
DWORD dwUserCount;
_pCredProviderUserArray->GetCount(&dwUserCount);
for (DWORD i = 0; i < dwUserCount; i++) {
if (_pCredential[i] != nullptr)
{
_pCredential[i]->Release();
_pCredential[i] = nullptr;
}
}
}
So I change other place in my code but I don't get result.
You must follow instructions in the posts you mentioned.
As a indirect response to ICredentialProviderSetUserArray::SetUserArray you must prepare tile for each user in the supplied list.
Later your tiles will be asked for ICredentialProviderCredential2::GetUserSid.
There your tile must return one of SIDs from the list of previous step.
When I was implementing this logic had to redesign a huge part of my code to support this feature.
I read this code. This is very helpful for knowing instruction and other details.
https://github.com/multiOTP/multiOTPCredentialProvider
This code (C++) active CP for all user on computer.
Thank you
Related
I'm new to this Credential Provider stuff. And I decided to start with Microsoft's V2 Credential Provider Sample.
The sample only adds the custom Credential Provider for the local administrator account. But I'd like to have it for every other local account.
I have looked into these questions:
Credential provider not displayed for all users (Other user included)
Credential Providers V2 Active for All users(Tiles)
How to show ICredentialProviderCredentialv2 on more than one User tile on the other user tile
And I also make the following changes to my code in reference to this GitHub repo.
In CSampleProvider.h:
// CSampleCredential *_pCredential; -> From this
std::vector<CSampleCredential*> _pCredential; -> To this
In CSampleProvider.cpp:
CSampleProvider::~CSampleProvider()
{
// if (_pCredential != nullptr)
// {
// _pCredential->Release();
// _pCredential = nullptr;
// }
_ReleaseEnumerateCredentials();
...
}
HRESULT CSampleProvider::GetCredentialCount(...)
{
...
// *pdwCount = 1;
DWORD dwUserCount;
_pCredProviderUserArray->GetCount(&dwUserCount);
*pdwCount = dwUserCount;
...
}
HRESULT CSampleProvider::GetCredentialAt(...) {
...
// if ((dwIndex == 0) && ppcpc)
// {
// hr = _pCredential->QueryInterface(IID_PPV_ARGS(ppcpc));
// }
if ((dwIndex == 0) && ppcpc)
{
hr = _pCredential[dwIndex]->QueryInterface(IID_PPV_ARGS(ppcpc));
}
...
}
void CSampleProvider::_ReleaseEnumeratedCredentials()
{
DWORD dwUserCount;
_pCredProviderUserArray->GetCount(&dwUserCount);
if (!_pCredential.empty())
{
for (DWORD i = 0; i < dwUserCount; i++)
{
_pCredential[0]->Release();
_pCredential.erase(_pCredential.begin());
}
}
}
HRESULT CSampleProvider::_EnumerateCredentials()
{
HRESULT hr = E_UNEXPECTED;
if (_pCredProviderUserArray != nullptr)
{
DWORD dwUserCount;
_pCredProviderUserArray->GetCount(&dwUserCount);
if (dwUserCount > 0)
{
for (DWORD i = 0; i < dwUserCount; i++) {
ICredentialProviderUser *pCredUser;
hr = _pCredProviderUserArray->GetAt(0, &pCredUser);
if (SUCCEEDED(hr))
{
_pCredential.push_back(new(std::nothrow) CSampleCredential());
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();
}
}
}
}
return hr;
}
I saw that I must return different SID in response to getUserSID() but I can't figure out how can I do it properly.
I've crashed some of my environment (fortunately I'm using VMs). And can't really figure out how to do it for multiple local user.
By the way, is there any way to debug the program?
First of all you must absorb the concept of Credential Provider V2.
It is significant extend the old concept of Credential Provider
Your CP is supporting the ICredentialProviderSetUserArray interface an supplied with the list of available users. You can use this list to math with your internal storage and check that is it possible to logon into the system using your provider or no.
Based on this information you need to return the number of user's tiles.
Each tile is associated with the user's SID and is displaying next to the user with the same SID.
For debugging the Credential Provider you can use the Credential Invoker.
I am writing custom windows credential provider using c++ with the help of sample provided by windows CredentialProvider2
I modified CredentialCount and CredentialAt in provider class to return 3 users. Those 3 users appear below sign-in options link. I expect them to display left bottom of the screen.
HRESULT CSampleProvider::GetCredentialCount(
_Out_ DWORD *pdwCount,
_Out_ DWORD *pdwDefault,
_Out_ BOOL *pbAutoLogonWithDefault)
{
*pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
*pbAutoLogonWithDefault = FALSE;
if (_fRecreateEnumeratedCredentials)
{
_fRecreateEnumeratedCredentials = false;
_ReleaseEnumeratedCredentials();
_CreateEnumeratedCredentials();
}
*pdwCount = 3;
return S_OK;
}
HRESULT CSampleProvider::GetCredentialAt(
DWORD dwIndex,
_Outptr_result_nullonfailure_ ICredentialProviderCredential **ppcpc)
{
HRESULT hr = E_INVALIDARG;
*ppcpc = nullptr;
if(ppcpc){
hr = _pCredential->QueryInterface(IID_PPV_ARGS(ppcpc));
}
return hr;
}
This is what I get
But I want to display the list of users in the left bottom of the screen. I will be a great help if you suggest me the changes are required to make it display left bottom on the screen?
Please understand my lack of writing skills.
I am testing to make a custom credential provider.
I want to create a CommandLink that does the same thing with the submit button.
I want to log on through the CommandLink separately from the Submit button.
Currently, only the custom credential provider is exposed through the providerFilter::Filter(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD dwFlags, GUID* rgclsidProviders, BOOL* rgbAllow, DWORD cProviders).
Click [anathor longon button] to log on.
This is my sample code:
HRESULT CSampleCredential::CommandLinkClicked(DWORD dwFieldID)
{
HRESULT hr = S_OK;
DWORD dwResult = 0;
if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) &&
(CPFT_COMMAND_LINK == _rgCredProvFieldDescriptors[dwFieldID].cpft))
{
HWND hwndOwner = nullptr;
switch (dwFieldID)
{
case SFI_ANATHOR_SUBMIT_LINK:
dwResult = function_foo();
if(dwResult == 1) {
Call GetSerialization()...?
Run the logon.
}
break;
// ...
}
}
}
Because you are writing credential provider, than you are already implementing ICredentialProvider interface and its Advise method:
virtual HRESULT STDMETHODCALLTYPE Advise(
/* [annotation][in] */
_In_ ICredentialProviderEvents *pcpe,
/* [annotation][in] */
_In_ UINT_PTR upAdviseContext) = 0;
The first argument is pointer to events interface ICredentialProviderEvents which have only one method: CredentialsChanged.
Your task is to grab credentials from user (login/password) store them inside your internals and call this method.
On the next turn your provider will be called this method:
virtual HRESULT STDMETHODCALLTYPE GetCredentialCount(
/* [annotation][out] */
_Out_ DWORD *pdwCount,
/* [annotation][out] */
_Out_ DWORD *pdwDefault,
/* [annotation][out] */
_Out_ BOOL *pbAutoLogonWithDefault) = 0;
Your task is to return the correct values in pdwDefault and pbAutoLogonWithDefault parameters (my suggest is 0 and TRUE).
Than your class that implementing ICredentialProviderCredential interface will be immediately called for GetSerialization method.
Here you can return already stored credentials.
Newbie here,
For the people who don't understand #Alexander 's explanation (like me several days ago), I recommend to read the Sample Harware Event Credential Provider. You need the 3rd class that is not inherited from ICredentialProvider or ICredentialProviderCredential. This is an independent class which is not related to both of them. I use this class to call the CredentialsChanged function.
So, TL;DR this is how I implement it:
Make a condition inside the ICredentialProvider's GetCredentialCount function like this
HRESULT CSampleCredentialProvider::GetCredentialCount(
DWORD* pdwCount,
DWORD* pdwDefault,
BOOL* pbAutoLogonWithDefault
)
{
//SavedCredentialInfo is just a simple Bool variable
if (_pCredential->SavedCredentialInfo == true)
{
*pdwCount = 1;
*pdwDefault = 0;
*pbAutoLogonWithDefault = TRUE;
}else{
*pdwCount = 1;
*pdwDefault = 0;
*pbAutoLogonWithDefault = FALSE;
}
return S_OK;
}
Save the credential (I use username & password) as a variable (string) inside my custom class that inherit ICredentialProviderCredential
HRESULT CGuardianCredential::CommandLinkClicked(DWORD dwFieldID)
{
if (dwFieldID < ARRAYSIZE(_rgCredProvFieldDescriptors) &&
(CPFT_COMMAND_LINK == _rgCredProvFieldDescriptors[dwFieldID].cpft)) {
this._username = "Ramon";
this._domain = "DESKTOP-937SDJ";
this._password = "MyPassword";
}
. . .
}
Call the 3rd class's function that call the ICredentialProvider's CredentialsChanged function
HRESULT CGuardianCredential::CommandLinkClicked(DWORD dwFieldID)
{
. . .
_commandWindow->callOnChange();
}
Then inside the commandWindow's class
BOOL CCommandWindow::callOnChange()
{
_pProvider->OnConnectStatusChanged();
return true;
}
Then you have it
Im working on an application that uses a CHtmlView. New requirements mean I would like to be able to get the HTML source from the class to parse for a specific tag (or if possible just get the information in the tag). This would be fine if we were using a newer system and I could use CHtmlView::GetSource but it doesn't exist.
I've had a pretty extensive search online but am pretty new to most of Windows programming and haven't been able to achieve anything useful yet.
So if anyone has an example of how to extract the HTML from a CHtmlView without using GetSource I would appreciate seeing it. I've tried
BSTR bstr;
_bstr_t * bstrContainer;
HRESULT hr;
IHTMLDocument2 * pDoc;
IDispatch * pDocDisp = NULL;
pDocDisp = this->GetHtmlDocument();
if (pDocDisp != NULL) {
hr = pDocDisp->QueryInterface (IID_IHTMLDocument2, (void**)&pDoc);
if (SUCCEEDED(hr)) {
if (pDoc->toString(&bstr) != S_OK) {
//error...
} else {
bstrContainer = new _bstr_t(bstr);
size = (bstrContainer->length()+1)*2;
realString = new char[size];
strncpy(realString, (char*)(*bstrContainer), size);
}
} else {
//error
}
pDocDisp->Release();
}
but it mostly just gives me "[object]" in realString. Like I said, new to Windows.
Any help appreciated.
Add this helper function into your CHtmlView-derived class to retrieve the html source. Remember to check the returned boolean state from this function as com-interface can be quite unreliable when system resources are low.
/* ============================================== */
BOOL CTest1View::GetHtmlText(CString &strHtmlText)
{
BOOL bState = FALSE;
// get IDispatch interface of the active document object
IDispatch *pDisp = this->GetHtmlDocument();
if (pDisp != NULL)
{ // get the IHTMLDocument3 interface
IHTMLDocument3 *pDoc = NULL;
HRESULT hr = pDisp->QueryInterface(IID_IHTMLDocument3, (void**) &pDoc);
if (SUCCEEDED(hr))
{ // get root element
IHTMLElement *pRootElement = NULL;
hr = pDoc->get_documentElement(&pRootElement);
if (SUCCEEDED(hr))
{ // get html text into bstr
BSTR bstrHtmlText;
hr = pRootElement->get_outerHTML(&bstrHtmlText);
if (SUCCEEDED(hr))
{ // convert bstr to CString
strHtmlText = bstrHtmlText;
bState = TRUE;
SysFreeString(bstrHtmlText);
}
pRootElement->Release();
}
pDoc->Release();
}
pDisp->Release();
}
return bState;
}
Edit: Are there any tutorials on how to use WIA or TWAIN in c++, that explain how to scan pages, adjust settings (DPI, using automatic feeder etc.) and save them as PNG files?
I'd like to use WIA to scan pages and store them as png files. If the scanner supports automatic feeding I'd also like to use that feature. Currently I am following the steps of this tutorial and am stuck at the section Transferring Image Data in WIA 2.0.
So far my scanner has been found and I am able to create the device, and an IWiaItem2* has been created. How can use it to scan at 300dpi and store the result as png file?
The tutorial is not clear about how to start the scan process or how to set dpi for scanning, so I hope someone can help me with the code.
This is essentially the code for getting all local devices:
bool init(IWiaDevMgr2* devMgr)
{
//creating the device manager
*devMgr = 0;
CoCreateInstance( CLSID_WiaDevMgr2, 0, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr2, (void**)&devMgr);
//enumerating wia devices
IEnumWIA_DEV_INFO* enumDevInfo = 0;
HRESULT hr = devMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &enumDevInfo);
if(SUCCEEDED(hr))
{
//loop until an error occurs or end of list
while(hr == S_OK)
{
IWiaPropertyStorage* storage = 0;
hr = enumDevInfo->Next( 1, &storage, 0);
if(hr == S_OK)
{
readProperties(storage);
storage->Release();
storage = 0;
}
}
//set hr to ok, so no error code is returned
if(hr == S_FALSE) hr = S_OK;
enumDevInfo->Release();
enumDevInfo = 0;
}
return SUCCEEDED(hr);
}
void readProperties(IWiaPropertyStorage* storage)
{
PROPSPEC propSpec[2] = {0};
PROPVARIANT propVar[2] = {0};
const ULONG propCount = sizeof(propSpec) / sizeof(propSpec[0]);
propSpec[0].ulKind = PRSPEC_PROPID;
propSpec[0].propid = WIA_DIP_DEV_ID;
propSpec[1].ulKind = PRSPEC_PROPID;
propSpec[1].propid = WIA_DIP_DEV_NAME;
HRESULT hr = storage->ReadMultiple(propCount, propSpec, propVar);
if(SUCCEEDED(hr))
{
Device* dev = new Device(propVar[0].bstrVal, propVar[1].bstrVal);
devices.push_back( dev );
FreePropVariantArray( propCount, propVar );
}
}
Afterwards a device is initialized like this:
bool createDevice(BSTR id, IWiaItem2** item)
{
*item = 0;
HRESULT hr = devMgr->CreateDevice( 0, deviceId, item);
return SUCCEEDED(hr);
}
Then the items are enumerated:
bool enumerateItems(IWiaItem2* item)
{
LONG itemType = 0;
HRESULT hr = item->GetItemType(&itemType);
if(SUCCEEDED(hr))
{
if(itemType & WiaItemTypeFolder || itemType & WiaItemTypeHasAttachments)
{
IEnumWiaItem2* enumItem = 0;
hr = item->EnumChildItems(0, &enumItem );
while(hr == S_OK)
{
IWiaItem2* child = 0;
hr = enumItem->Next( 1, &child, 0 );
if(hr == S_OK)
{
hr = enumerateItems( child );
child->Release();
child = 0;
}
}
if(hr == S_FALSE) hr = S_OK;
enumItem->Release();
enumItem = 0;
}
}
return SUCCEEDED(hr);
}
Now that everything has been initialized I'd like to implement a scan function. However, the code provided at the tutorial is for transferring files and folders and not for scanning images.
void scanAndSaveAsPNG(IWiaItem2* item, unsigned int dpi, std::string targetPath)
{
}
EDIT:
I installed the latest version available of the scanner driver (WIA and TWAIN) and after checking the supported commands using this code
void printCommands(IWiaItem2* i)
{
IEnumWIA_DEV_CAPS* caps = 0;
HRESULT h = item->EnumDeviceCapabilities(WIA_DEVICE_COMMANDS, &caps);
if(SUCCEEDED(h))
{
ULONG count = 0;
caps->GetCount(&count);
if(count > 0)
{
WIA_DEV_CAP* cap = new WIA_DEV_CAP[ count ];
ULONG fetched;
caps->Next(count, cap, &fetched);
for(int i = 0; i < fetched; i++)
{
std::cout << bstr_t( cap[i].bstrName ) << "\n";
}
}
caps->Release();
}
}
I noticed it only lists WIA Synchronize command. I am not sure if I didn't initialize the device correctly, or if the device doesn't support all WIA commands although the driver is installed.
So unless this problem is solved I am alternatively also looking for the same code based on TWAIN.
You want to use IWiaItem2::DeviceCommand which sends a command to the image capture device. The list of commands you can send are listed here.