Can I create a CommandLink that works the same as the submit button?
Asked Answered
R

2

2

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). enter image description here 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;
             // ...
         }
     }
 }
Rhumb answered 28/6, 2018 at 2:45 Comment(0)
A
4

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.

Arterialize answered 19/7, 2018 at 7:47 Comment(1)
Your friendly explanation has helped me a lot.Rhumb
T
2

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:

  1. 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;

}
  1. 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";
    }

    . . .
}
  1. 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

Timi answered 10/11, 2021 at 5:44 Comment(2)
This works fine with single user. How do we achieve this with multiple users?Tirado
@Tirado If you are trying to do this in multiple users, please learn about writing & reading files on C:/System32 (or else with System privilege). It's been a while since I quit the company that works on this one. But that's what I've done back then. So I wrote the username, password, and domain combination and store it there.Timi

© 2022 - 2024 — McMap. All rights reserved.