Working with Group Policy Objects Programmatically - simple C++ example illustrating how to modify a registry based policy

In my last bog post:

http://blogs.msdn.com/dsadsi/archive/2009/07/23/working-with-group-policy-objects-programmatically-determining-registry-values-to-enable-disable-set-a-specific-policy.aspx

I discussed an empirical method to determine the values one needs to write to the registry to enable/disable/set a registry based group policy.  In this post, I will provide a simple C++ function that illustrates how to write the key information into the GPO.

For now, lets concentrate on the C++ code for modifying the GPO.

The function requires 3 pieces of information:

  1. ADsPath to the GPO object to modify
  2. A mode control values, 0=Not Configured, 1=Enabled, 2=Disabled
  3. The actual value to write in the key that represents the mode control value. 

The source for the function follows.  The comments should provide insight into how the code works.

One key point is to realize that if you are modifying an existing GPO that contains multiple settings on a specific registry key tree, you cannot just delete the entire key tree, you must delete just the values that affect the settings you are working with.

Another subtle gotcha is the way you use the IGroupPolicyObject::GetRegistryKey and the IGroupPolicyObject::Save methods. The flag on in GetRegistryKey to indicate the section is an unsigned valued ( GPO_SECTION_ROOT or GPO_SECTION_USER or GPO_SECTION_MACHINE) , the flag to indicate the section on the Save method is a boolean ( TRUE to write the machine configuration, FALSE to write the user configuration). I made the mistake of using the same value in both locations, and thought I was chasing a bug in the IGroupPolicyObject interface.

Finding the path can be a challenge. The next blog post will provide details on how to locate a GPO object in the AD based on specific information using either the GPMC object model or the BrowseForGPO function.

C++ Source Code follows:

HRESULT ModifyUserPolicyForPreventAccessToCmdPrompt( BSTR bGPOPath, int iMode, DWORD lData)

{

    HRESULT hr=S_OK;

    //

    // Use IGroupPolicyObject to retrieve and modify the registry settings.

    // for the GPO represented by the gpoInfo.lpDsPath

    //

    IGroupPolicyObject* p = NULL;

    hr = CoCreateInstance(CLSID_GroupPolicyObject, NULL,

                          CLSCTX_INPROC_SERVER, IID_IGroupPolicyObject,

                         (LPVOID*)&p);

    if (SUCCEEDED(hr))

    {

        //

        // The GPO value we want to modify is the

        //

        // User Configuration

        // +> Policies

        // +>Administrative Templates

        // +->System

        // +->Prevent access to the command prompt

        //

        DWORD dwSection = GPO_SECTION_USER;

        HKEY hGPOSectionKey = NULL;

        DWORD dwData;

        HKEY hSettingKey;

        LSTATUS rStatus;

        hr = 0;

        //

        //Open the GPO and load its registy values for both: Machine and user

        //

        hr = p->OpenDSGPO( bGPOPath, GPO_OPEN_LOAD_REGISTRY);

        //

        // Request the user Registy hive for the GPO

        //

        hr = p->GetRegistryKey(dwSection, &hGPOSectionKey);

        //

        // Determine if you want to set it to Not Congigure,

        // Enabled or Disabled for the GPO itself.

        //

        // The second call, RequestSetting will provide the "Yes" or "No"

        // value for setting

        // the policy as shown by the GPO Editor

        //

        // iMode

        // 0=Not Configured, 1=Enabled, 2=Disabled

        //

        switch (iMode)

        {

        case 0:

            //

            // We do not want to configure the GPO, but we don't want to

            // affect other GPOs on the same key,

            // so just delete values associated with this

            // particular GPO setting.

            //

            rStatus = RegDeleteValue(hGPOSectionKey,

               L"Software\\Policies\\Microsoft\\Windows\\System\\DisableCMD"

                                     );

            rStatus = RegDeleteValue(hGPOSectionKey,

         L"Software\\Policies\\Microsoft\\Windows\\System\\**del.DisableCMD"

                                     );

            break;

        case 1:

            {

                //

                // To enable the policy, the DisableCMD value must

                // exist and the **del.DisableCMD value must not.

                //

                // lData:

                //

                // Check to see if the key for this policy exists.

        // If if it does, retrieve a handle

                // If not, create it.

                //

                if( RegOpenKeyEx( hGPOSectionKey,

                       L"Software\\Policies\\Microsoft\\Windows\\System", 0,

                       KEY_WRITE, &hSettingKey) != ERROR_SUCCESS )

                {

                    rStatus = RegCreateKeyEx(

                        hGPOSectionKey,

                        L"Software\\Policies\\Microsoft\\Windows\\System",

                        0,

               NULL,

                        REG_OPTION_NON_VOLATILE,

                        KEY_WRITE,

                        NULL,

                        &hSettingKey,

                        NULL );

                }

                //

                // Set the value DisableCMD and allow, disallow

                // script launching of CMD 

                //

                rStatus = RegSetValueEx(hSettingKey, L"DisableCMD",

                                        NULL, REG_DWORD, (BYTE *)(&lData),

                                       sizeof(DWORD));

                //

                // Remove the not configured value indicator from the hive.

                // It may not exist, so the RegDeleteValue may return

                // and error, this can be ignored.

                //

                rStatus = RegDeleteValue(hGPOSectionKey,

     L"Software\\Policies\\Microsoft\\Windows\\System\\**del.DisableCMD"

                                         );

                rStatus = RegCloseKey(hSettingKey);

                break;

            }

        case 2:

            {

                //

              // Disable the policy.

                // must remove the DisableCMD value and add the

                // **del.DisableCMD value.

                //

                // Same stesp as before, check to see if the key for this

                // policy exists,

                // if not, create it.

                //

                BOOL bCreate = FALSE;

                if( RegOpenKeyEx( hGPOSectionKey, L"Software\\Policies\\Microsoft\\Windows\\System", 0, KEY_WRITE, &hSettingKey) != ERROR_SUCCESS )

                {

                    rStatus = RegCreateKeyEx(

                        hGPOSectionKey,

                        L"Software\\Policies\\Microsoft\\Windows\\System",

                        0,

                        NULL,

                        REG_OPTION_NON_VOLATILE,

               KEY_WRITE,

                        NULL,

                        &hSettingKey,

                        NULL );

                    bCreate = TRUE;

                }

                DWORD dwType = 0;

                DWORD cbType = sizeof( dwData );

                if( !bCreate )

                {

                    //

                    // If we did not create the key, then our value

                    // *may* exist.

                    // try to read it. If we succeed, write that value back

                    // to **del.DisableCMD

     // if not, then set **del.DisableCMD to 0

                    //

                    rStatus = RegGetValue(hGPOSectionKey,

                                          L"Software\\Policies\\Microsoft\\Windows\\System", L"DisableCMD", RRF_RT_ANY, &dwType, (BYTE *)(&dwData), &cbType);

  if( rStatus != ERROR_SUCCESS ) dwData = 0;

                    else RegDeleteValue( hSettingKey, L"DisableCMD");

                    rStatus = RegSetValueEx(hSettingKey, L"**del.DisableCMD", NULL, REG_DWORD, (BYTE *)(&dwData), sizeof(DWORD));

                }

                else

                {

                    //

                    // The key was created, just set the **del.DisableCMD

                    // value to 0

                    //

                    dwData = 0;

                    rStatus = RegSetValueEx(hSettingKey, L"**del.DisableCMD", NULL, REG_DWORD, (BYTE *)(&dwData), sizeof(DWORD));

                }

                rStatus = RegCloseKey(hSettingKey);

            }

        }

        GUID RegistryId = REGISTRY_EXTENSION_GUID;

        GUID ThisAdminToolGuid =

            /*{ CLSID_PolicySnapinUser/* */

        {

            0x0F6B957E,

            0x509E,

            0x11D1,

            {0xA7, 0xCC, 0x00, 0x00, 0xF8, 0x75, 0x71, 0xE3}

        };

        rStatus = RegCloseKey(hGPOSectionKey);

        //

        // Write the GPO back to the directory

        //

        hr = p->Save(

            FALSE,

            TRUE,

            &RegistryId,

            &ThisAdminToolGuid );

        hr = p->Release();

    }

    return hr;

}