Supporting RSoP Logging Mode

Each client-side extension that plans to support the logging of RSoP data must export an implementation of the application-defined ProcessGroupPolicyEx callback function. When the Winlogon.exe/Userenv.dll process calls the function as part of applying Group Policy, the client-side extension should apply Group Policy and log RSoP data.

To register this callback function, create a subkey under the following registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\ClientExtensionGuid

with the registry value [**ProcessGroupPolicyEx**](/windows/desktop/api/Userenv/nc-userenv-pfnprocessgrouppolicyex) **REG\_SZ** "Function name that processes policy and logs RSoP data".

The subkey should be a GUID, so that it is unique. For more information, see ProcessGroupPolicyEx and Registering a Policy Callback Function.

The following code example can be used by a registry extension to log one instance of the RSOP_RegistryPolicySetting MOF class.

//*************************************************************
//
//  Function:   LogRegistryPolicyInstance()
//
//  Purpose:    Logs an instance of registry policy. Will be called from deep
//              within ProcessGroupPolicyEx process to log RsoP data for a registry client-side extension.
//
//  Parameters: pwszGPO – GPO ID obtained from PGROUP_POLICY_OBJECT->lpDSPath
//              pwszSOM – SOM ID obtained from PGROUP_POLICY_OBJECT->lpLink
//              pWbemServices – Obtained from last parameter in ProcessGroupPolicyEx
//              dwPrecedence – Precedence order for this policy instance
//              pwszKeyName, pwszValueName, bDeleted  - Registry client-side extension-specific properties
//
//  Returns:    HRESULT
//
//*************************************************************

HRESULT LogRegistryPolicyInstance( WCHAR *pwszGPO, WCHAR *pwszSOM,
                                   IWbemServices *pWbemServices,
                                   WCHAR *pwszKeyName, WCHAR *pwszValueName,
                                   BOOL bDeleted, DWORD dwPrecedence )
{
   IWbemClassObject*   pClass;
   HRESULT hr = pWbemServices->GetObject( SysAllocString(L"RSOP_RegistryPolicySetting"),
                                          0L,
                                          NULL,
                                          &pClass,
                                          NULL );
    if ( FAILED(hr) ) 
    {   
       // Be aware that error-checking has been omitted. 
       //   Also be aware that pClass must be assigned to a smart pointer 
       //     so that it can be released eventually by calling 
       //     pClass->Release().
       //   Also be aware that if you call the SysAllocString function
       //     to allocate resources, you must call the SysFreeString
       //     function to deallocate them. 
    }
    
    IWbemClassObject *pInstance = NULL;
    HRESULT hr = pClass->SpawnInstance( 0, &pInstance );
        
    //
    // First, log client-side extension-specific properties.
    //    
    VARIANT var;

    var.vt = VT_BOOL;
    var.boolVal = bDeleted ? VARIANT_TRUE : VARIANT_FALSE;
    hr = pInstance->Put( L"deleted", 0, &var, 0 );

    var.vt = VT_BSTR;
    var.bstrVal = SysAllocString( pwszValueName;
    hr = pInstance->Put( SysAllocString( L"name", 0, &var, 0 );
    
    //
    // Set other registry client-side extension 
    // properties such as valueType and registryKey.
    //

    // Log properties of the RSOP_PolicySetting parent class.
    //
    //  For the GPOID property, use the data in the 
    //  PGROUP_POLICY_OBJECT->lpDSPath member. For the
    //  SOMID property, use the PGROUP_POLICY_OBJECT->lpLink member.
    //
    // Be aware that the LDAP://CN=Machine or LDAP:// must be removed 
    //  from the prefix of the lpDSPath and lpLink members to
    //  get the canonical values. See the code for the StripPrefix 
    //  and StripLinkPrefix functions.
    //

    // Precedence is determined by the client-side extension to 
    //  indicate "winning" and "losing" policies.
    // 

    var.vt = VT_I4;
    var.lVal = dwPrecedence;
    hr = pInstance->Put( SysAllocString( L"precedence', 0, &var, 0 );

    WCHAR *pwszStrippedGPO = StripGPOPrefix( pwszGPO );

    var.bstrVal = SysAllocString( pwszStrippedGPO );
    hr = pInstance->Put( SysAllocString( L"GPOID", 0, &var, 0 );
    
    WCHAR *pwszStrippedSOM = StripSOMPrefix( pwszSOM );

    var.bstrVal = SysAllocString( pwszStrippedSOM );
    hr = pInstance->Put( SysAllocString(L"SOMID), 0, &var, 0 );
    
    //
    // Create a GUID for the id property (a key).
    //  Client-side extensions must create their own key. The
    //  key must be unique for every instance of the policy instance.
    //

    WCHAR wszId[MAX_GUID_LENGTH];
    GUID guid;
    
    hr = CoCreateGuid( &guid );
    GuidToString( &guid, wszId );

    var.bstrVal = SysAllocString( wszId );
    hr = pInstance->Put( L"id", 0, &var, 0 );
     
    //
    // Commit all above properties by calling the PutInstance method.
    //

    hr = pWbemServices->PutInstance( pInstance, WBEM_FLAG_CREATE_OR_UPDATE, NULL, NULL );
    
    //
    // You must free all allocated resources to avoid memory leaks.
    //

    return S_OK;
}


//*************************************************************
//
//  Function:   StripGPOPrefix()
//
//  Purpose:    Strips out the prefix to get the canonical path to a GPO.
//
//  Parameters: pwszPath      DS path to GPO
//
//  Returns:    Pointer to suffix
//
//*************************************************************

WCHAR *StripGPOPrefix( WCHAR *pwszPath )
{
    WCHAR wszMachPrefix[] = TEXT("LDAP://CN=Machine,");
    INT iMachPrefixLen = lstrlen( wszMachPrefix );
    WCHAR wszUserPrefix[] = TEXT("LDAP://CN=User,");
    INT iUserPrefixLen = lstrlen( wszUserPrefix );
    WCHAR *pwszPathSuffix;

    //
    // Remove the prefix to get the canonical path to the GPO.
    //

    if ( CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE,
                        pwszPath, iUserPrefixLen, wszUserPrefix, 
                        iUserPrefixLen ) == CSTR_EQUAL ) {
        pwszPathSuffix = pwszPath + iUserPrefixLen;
    } else if ( CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE,
                        pwszPath, iMachPrefixLen, wszMachPrefix, 
                        iMachPrefixLen ) == CSTR_EQUAL ) {
        pwszPathSuffix = pwszPath + iMachPrefixLen;
    } else
        pwszPathSuffix = pwszPath;

    return pwszPathSuffix;
}

//*************************************************************
//
//  Function:   StripSOMPrefix()
//
//  Purpose:    Removes the prefix to get canonical path to the SOM
//              object.
//
//  Parameters: pwszPath    path to SOM 
//
//  Returns:    Pointer to suffix
//
//*************************************************************

WCHAR *StripSOMPrefix( WCHAR *pwszPath )
{
    WCHAR wszPrefix[] = TEXT("LDAP://");
    INT iPrefixLen = lstrlen( wszPrefix );
    WCHAR *pwszPathSuffix;

    //
    // Remove the prefix to get the canonical path to the SOM.
    //

    if ( wcslen(pwszPath) <= (DWORD) iPrefixLen ) {
        return pwszPath;
    }

    if ( CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE,
                        pwszPath, iPrefixLen, wszPrefix, iPrefixLen ) == CSTR_EQUAL ) {
        pwszPathSuffix = pwszPath + iPrefixLen;
    } else
        pwszPathSuffix = pwszPath;

    return pwszPathSuffix;
}