IDispatch null pointer exception while creating Active Directory user

Issue

I am writing a C++ native method to create an Active Directory user. I am getting a null pointer exception . This code is exactly the same as the code in the official Microsoft documentation.

I have mentioned in a comment on which line I get the error:

HRESULT CreateUserFromADs(
    LPCWSTR pwszName,
    LPCWSTR pwszSAMAccountName,
    LPCWSTR pwszInitialPassword)
{
    HRESULT hr;


    CoInitialize(NULL);
    IADsContainer* pUsers = NULL;
    hr = ADsOpenObject(L"LDAP://WIN-F94H2MP3UJR.Test.local/CN=Users,DC=Test,DC=local", L"Administrator", L"[email protected]",
        ADS_SECURE_AUTHENTICATION, // For secure authentication
        IID_IADs,
        (void**)&pUsers);
    if (SUCCEEDED(hr))
    {
        IDispatch* pDisp = NULL;

        CComBSTR sbstrName = "CN=";
        sbstrName += pwszName;

        // Create the new object in the User folder.
        hr = pUsers->Create(CComBSTR("user"), sbstrName, &pDisp);

        if (SUCCEEDED(hr))
        {
            IADsUser* padsUser = NULL;

            // Get the IADs interface.

        // Am getting null pointer exception here.
            hr = pDisp->QueryInterface(IID_IADsUser,
                (void**)&padsUser);

            if (SUCCEEDED(hr))
            {
                CComBSTR sbstrProp;
                /*
                The sAMAccountName property is required on operating system
                versions prior to Windows Server 2003.
                The Windows Server 2003 operating system will create a
                sAMAccountName value if one is not specified.
                */
                CComVariant svar;
                svar = pwszSAMAccountName;
                sbstrProp = "sAMAccountName";
                hr = padsUser->Put(sbstrProp, svar);

                /*
                Commit the new user to persistent memory.
                The user does not exist until this is called.
                */
                hr = padsUser->SetInfo();

                /*
                Set the initial password. This must be done after
                SetInfo is called because the user object must
                already exist on the server.
                */
                hr = padsUser->SetPassword(CComBSTR(pwszInitialPassword));

                /*
                Set the pwdLastSet property to zero, which forces the
                user to change the password the next time they log on.
                */
                sbstrProp = "pwdLastSet";
                svar = 0;
                hr = padsUser->Put(sbstrProp, svar);

                /*
                Enable the user account by removing the
                ADS_UF_ACCOUNTDISABLE flag from the userAccountControl
                property. Also, remove the ADS_UF_PASSWD_NOTREQD and
                ADS_UF_DONT_EXPIRE_PASSWD flags from the
                userAccountControl property.
                */
                svar.Clear();
                sbstrProp = "userAccountControl";
                hr = padsUser->Get(sbstrProp, &svar);
                if (SUCCEEDED(hr))
                {
                    svar = svar.lVal & ~(ADS_UF_ACCOUNTDISABLE |
                        ADS_UF_PASSWD_NOTREQD |
                        ADS_UF_DONT_EXPIRE_PASSWD);

                    hr = padsUser->Put(sbstrProp, svar);
                    hr = padsUser->SetInfo();
                }

                hr = padsUser->put_AccountDisabled(VARIANT_FALSE);
                hr = padsUser->SetInfo();

                padsUser->Release();
            }

            pDisp->Release();
        }

        pUsers->Release();
    }
    CoUninitialize();

    return hr;
}

Error message is

Exception thrown: read access violation.
pDisp was nullptr.

Solution

You are asking ADsOpenObject() for an IADs* interface pointer, but you are storing it in an IADsContainer* variable. That is a type mismatch, IADs and IADsContainer are unrelated interfaces. So, when you call pUsers->Create(), you are not actually calling IADsContainer::Create() at all, you are actually calling IADs::SetInfo() instead, which does not assign anything to pDisp, which is why it remains NULL.

The Microsoft doc you linked to does not make that mistake, it asks ADsOpenObject() for IADsContainer instead of IADs.

The IID you request must match the variable type you receive into. So, change IID_IADs to IID_IADsContainer:

IADsContainer* pUsers = NULL;
hr = ADsOpenObject(...,
        IID_IADsContainer,
        (void**)&pUsers);

You should consider using the IID_PPV_ARGS() macro to avoid making this mistake again:

IADsContainer* pUsers = NULL;
hr = ADsOpenObject(...,
        IID_PPV_ARGS(&pUsers));

On a side note, your CreateUserFromADs() function has no business calling CoInitialize() directly, so you should remove that call. It is the responsibility of the calling thread to call CoInitialize/Ex() to establish its concurrency model before performing any COM-related activities. It is not up to functions to decide what concurrency model to use on behalf of the calling thread.

Answered By – Remy Lebeau

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published