/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORP., 1996
*
*  TITLE:       REGHELP.C
*
*  VERSION:     2.0
*
*  AUTHOR:      ReedB
*
*  DATE:        17 Oct, 1996
*
*  DESCRIPTION:
*
*******************************************************************************/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <string.h>
#include <regstr.h>
#include <commctrl.h>

#include <ntpoapi.h>

#include "powrprofp.h"
#include "reghelp.h"

/*******************************************************************************
*
*                     G L O B A L    D A T A
*
*******************************************************************************/

extern HINSTANCE   g_hInstance;        // Global instance handle of this DLL.
extern HANDLE      g_hSemRegistry;     // Registry semaphore.
extern UINT        g_uiLastID;         // The last ID value used, per machine.

extern TCHAR c_szREGSTR_PATH_MACHINE_POWERCFG[];
extern TCHAR c_szREGSTR_VAL_LASTID[];

// Global semaphore name.
const TCHAR c_szSemRegistry[] = TEXT("PowerProfileRegistrySemaphore");


/*******************************************************************************
*
*  OpenCurrentUser
*
*  DESCRIPTION:
*   
*  PARAMETERS:
*
*******************************************************************************/

DWORD OpenCurrentUser2(PHKEY phKey)
{
#ifdef WINNT

    // Since powerprof can be called in the Winlogon context when
    // a user is being impersonated, use RegOpenCurrentUser to get HKCU.
    LONG lRet = RegOpenCurrentUser(KEY_ALL_ACCESS, phKey);
    if (lRet != ERROR_SUCCESS)
    {
        DebugPrint("RegOpenCurrentUser, failed, LastError: 0x%08X", lRet);
    }

    return lRet;

#else
    *phKey = HKEY_CURRENT_USER;
    return ERROR_SUCCESS; 
#endif
}


BOOLEAN OpenCurrentUser(PHKEY phKey)
{
    DWORD dwError = OpenCurrentUser2(phKey);
    BOOLEAN fSucceeded = TRUE;
    
    if (ERROR_SUCCESS != dwError)
    {
        fSucceeded = FALSE;
        SetLastError(dwError);
    }

    return fSucceeded;
}



/*******************************************************************************
*
*  CloseCurrentUser
*
*  DESCRIPTION:
*   
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN CloseCurrentUser(HKEY hKey)
{
#ifdef WINNT
    RegCloseKey(hKey);
#endif
    return TRUE;
}

/*******************************************************************************
*
*  OpenMachineUserKeys
*
*  DESCRIPTION:
*   
*  PARAMETERS:
*
*******************************************************************************/

DWORD OpenMachineUserKeys2(
    LPTSTR  lpszUserKeyName,
    LPTSTR  lpszMachineKeyName,
    PHKEY   phKeyUser,
    PHKEY   phKeyMachine
)
{
    HKEY hKeyCurrentUser;
    DWORD dwError = OpenCurrentUser2(&hKeyCurrentUser);

    if (ERROR_SUCCESS == dwError)  // Sets Last Error
    {
        dwError = RegOpenKey(hKeyCurrentUser, lpszUserKeyName, phKeyUser);
        if (dwError == ERROR_SUCCESS)
        {
            dwError = RegOpenKey(HKEY_LOCAL_MACHINE, lpszMachineKeyName, phKeyMachine);
            if (dwError == ERROR_SUCCESS)
            {
                CloseCurrentUser(hKeyCurrentUser);
                return dwError;
            }
            else
            {
                DebugPrint("OpenMachineUserKeys, failure opening  HKEY_LOCAL_MACHINE\\%s", lpszMachineKeyName);
            }

            RegCloseKey(*phKeyUser);
        }
        else
        {
            DebugPrint("OpenMachineUserKeys, failure opening HKEY_CURRENT_USER\\%s", lpszUserKeyName);
        }

        CloseCurrentUser(hKeyCurrentUser);
    }
    else
    {
        dwError = ERROR_FILE_NOT_FOUND;
    }

    DebugPrint("OpenMachineUserKeys, failed, LastError: 0x%08X", dwError);
    return dwError;
}


BOOLEAN OpenMachineUserKeys(
    LPTSTR  lpszUserKeyName,
    LPTSTR  lpszMachineKeyName,
    PHKEY   phKeyUser,
    PHKEY   phKeyMachine)
{
    DWORD dwError = OpenMachineUserKeys2(lpszUserKeyName, lpszMachineKeyName, phKeyUser, phKeyMachine);
    BOOLEAN fSucceeded = TRUE;
    
    if (ERROR_SUCCESS != dwError)
    {
        fSucceeded = FALSE;
        SetLastError(dwError);
    }

    return fSucceeded;
}


/*******************************************************************************
*
*  OpenPathKeys
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

DWORD OpenPathKeys(
    LPTSTR  lpszUserKeyName,
    LPTSTR  lpszMachineKeyName,
    LPTSTR  lpszSchemeName,
    PHKEY   phKeyUser,
    PHKEY   phKeyMachine,
    BOOLEAN bMustExist,
    REGSAM  sam
)
{
    HKEY     hKeyUser, hKeyMachine;

    DWORD dwError = OpenMachineUserKeys2(lpszUserKeyName, lpszMachineKeyName, &hKeyUser, &hKeyMachine);
    if (ERROR_SUCCESS == dwError)
    {
        DWORD dwDisposition;

        dwError = RegCreateKeyEx(hKeyUser, lpszSchemeName, 0, TEXT(""), REG_OPTION_NON_VOLATILE, sam, NULL, phKeyUser, &dwDisposition);
        if (dwError == ERROR_SUCCESS)
        {
            if (!bMustExist || (dwDisposition == REG_OPENED_EXISTING_KEY))
            {
                dwError = RegCreateKeyEx(hKeyMachine,
                                           lpszSchemeName,
                                           0,
                                           TEXT(""),
                                           REG_OPTION_NON_VOLATILE,
                                           sam,
                                           NULL,
                                           phKeyMachine,
                                           &dwDisposition);
                if (dwError == ERROR_SUCCESS)
                {
                    if (!bMustExist ||
                        (dwDisposition == REG_OPENED_EXISTING_KEY))
                    {
                        // This is the success case.
                    }
                    else
                    {
                        dwError = ERROR_ACCESS_DENIED;
                    }
                }
                else
                {
                   RegCloseKey(*phKeyUser);
                   DebugPrint("OpenPathKeys, unable to create machine key %s\\%s", lpszMachineKeyName, lpszSchemeName);
                }
            }
            else
            {
                dwError = ERROR_ACCESS_DENIED;
            }
        }
        else
        {
           DebugPrint("OpenPathKeys, unable to create user key %s\\%s", lpszUserKeyName, lpszSchemeName);
        }

        RegCloseKey(hKeyUser);
        RegCloseKey(hKeyMachine);

        if (ERROR_SUCCESS != dwError)
        {
            DebugPrint("OpenPathKeys, failed, LastError: 0x%08X", dwError);
        }
    }

    return dwError;
}

PACL    BuildSemaphoreACL (void)

//  2000-06-22 vtan:
//
//  This function builds an ACL which allows everybody access to the named
//  semaphore for SYNCHRONIZE | READ_CONTROL | SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE.
//  It gives full access for the local SYSTEM or members of the local administrators
//  group. If something goes wrong the return result is NULL and no security descriptor
//  is built then.

{
    static  SID_IDENTIFIER_AUTHORITY    worldSIDAuthority       =   SECURITY_WORLD_SID_AUTHORITY;
    static  SID_IDENTIFIER_AUTHORITY    securityNTAuthority     =   SECURITY_NT_AUTHORITY;

    PSID        pSIDWorld;
    PACL        pACL;

    pACL = NULL;
    if (AllocateAndInitializeSid(&worldSIDAuthority,
                                 1,
                                 SECURITY_WORLD_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 &pSIDWorld) != FALSE)
    {
        PSID    pSIDLocalSystem;

        if (AllocateAndInitializeSid(&securityNTAuthority,
                                     1,
                                     SECURITY_LOCAL_SYSTEM_RID,
                                     0, 0, 0, 0, 0, 0, 0,
                                     &pSIDLocalSystem) != FALSE)
        {
            PSID    pSIDLocalAdministrators;

            if (AllocateAndInitializeSid(&securityNTAuthority,
                                         2,
                                         SECURITY_BUILTIN_DOMAIN_RID,
                                         DOMAIN_ALIAS_RID_ADMINS,
                                         0, 0, 0, 0, 0, 0,
                                         &pSIDLocalAdministrators) != FALSE)
            {
                DWORD       dwACLSize;

                dwACLSize = sizeof(ACL) +
                            ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG)) * 3) +
                            GetLengthSid(pSIDWorld) +
                            GetLengthSid(pSIDLocalSystem) +
                            GetLengthSid(pSIDLocalAdministrators);
                pACL = (PACL)LocalAlloc(LMEM_FIXED, dwACLSize);
                if (pACL != NULL)
                {
                    if ((InitializeAcl(pACL, dwACLSize, ACL_REVISION) == FALSE) ||
                        (AddAccessAllowedAce(pACL,
                                             ACL_REVISION,
                                             SYNCHRONIZE | READ_CONTROL | SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE,
                                             pSIDWorld) == FALSE) ||
                        (AddAccessAllowedAce(pACL,
                                             ACL_REVISION,
                                             SEMAPHORE_ALL_ACCESS,
                                             pSIDLocalSystem) == FALSE) ||
                        (AddAccessAllowedAce(pACL,
                                             ACL_REVISION,
                                             SEMAPHORE_ALL_ACCESS,
                                             pSIDLocalAdministrators) == FALSE))
                    {
                        (HLOCAL)LocalFree(pACL);
                        pACL = NULL;
                    }
                }
                (PVOID)FreeSid(pSIDLocalAdministrators);
            }
            (PVOID)FreeSid(pSIDLocalSystem);
        }
        (PVOID)FreeSid(pSIDWorld);
    }
    return(pACL);
}


/*******************************************************************************
*
*  CreateRegSemaphore
*
*  DESCRIPTION: Attempts to open/create the registry semaphore. g_hSemRegistry
*               is initialized on success.
*
*  PARAMETERS:  None
*
*******************************************************************************/

BOOLEAN CreateRegSemaphore(VOID)
{
    HANDLE Semaphore=NULL;

    // First try to open the named semaphore with only required access.

    // NOTE: the named object is per terminal server session. Therefore
    // this semaphore is really bogus because it protects HKEY_LOCAL_MACHINE
    // as well as HKEY_CURRENT_USER. Making it "Global\" is very dangerous
    // and you don't know the side effects without complete retesting.
    // Not worth it.

    Semaphore = OpenSemaphore(SYNCHRONIZE | SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE,
                              FALSE,
                              c_szSemRegistry);
    if ((Semaphore == NULL) && (GetLastError() != ERROR_ACCESS_DENIED))
    {
        SECURITY_ATTRIBUTES     securityAttributes, *pSA;
        SECURITY_DESCRIPTOR     securityDescriptor;
        PACL                    pACL;

        // If this fails then create the semaphore and ACL it so that everybody
        // can get SYNCHRONIZE | SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE
        // access. This allows a service (such as UPS) running in the SYSTEM context
        // to grant limited access to anybody who needs it to synchronize against
        // this semaphore. It also prevents C2 violation by NOT putting a NULL
        // DACL on the named semaphore. If an ACL for the semaphore cannot be
        // built then no security descriptor is given and the default ACL is used.

        pSA = NULL;
        pACL = BuildSemaphoreACL();
        if (pACL != NULL)
        {
            if ((InitializeSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION) != FALSE) &&
                (SetSecurityDescriptorDacl(&securityDescriptor, TRUE, pACL, FALSE) != FALSE))
            {
                securityAttributes.nLength = sizeof(securityAttributes);
                securityAttributes.bInheritHandle = FALSE;
                securityAttributes.lpSecurityDescriptor = &securityDescriptor;
                pSA = &securityAttributes;
            }
        }

        // Create the registry semaphore.
        Semaphore = CreateSemaphore(pSA, 1, 1, c_szSemRegistry);

        if (pACL != NULL)
        {
            (HLOCAL)LocalFree(pACL);
        }
    }

    //
    // If we successfully opened a handle, update the global g_hSemRegistry now
    //
    if (Semaphore) {
        if (InterlockedCompareExchangePointer(&g_hSemRegistry, Semaphore, NULL) != NULL) {
            CloseHandle(Semaphore);
        }
        return(TRUE);
    } else {
        return(FALSE);
    }

}

/*******************************************************************************
*
*  TakeRegSemaphore
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN TakeRegSemaphore(VOID)
{
    if (g_hSemRegistry == NULL) {
        if (!CreateRegSemaphore()) {
            return FALSE;
        }
    }
    if (WaitForSingleObject(g_hSemRegistry, SEMAPHORE_TIMEOUT) != WAIT_OBJECT_0) {
        ReleaseSemaphore(g_hSemRegistry, 1, NULL);
        DebugPrint("WaitForSingleObject, failed");
        SetLastError(ERROR_INVALID_ACCESS);
        return FALSE;
    }
    return TRUE;
}

/*******************************************************************************
*
*  ReadPowerValueOptional
*
*  DESCRIPTION:
*   Value may not exist.
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN ReadPowerValueOptional(
    HKEY    hKey,
    LPTSTR  lpszPath,
    LPTSTR  lpszValueName,
    LPTSTR  lpszValue,
    LPDWORD lpdwSize
)
{
    HKEY     hKeyPath;
    BOOLEAN  bRet = FALSE;
    DWORD    dwSize;
    LONG     lRet;

    if ((lRet = RegOpenKey(hKey,
                           lpszPath,
                           &hKeyPath)) != ERROR_SUCCESS) {
        goto RPVO_exit;
    }

    if ((lRet = RegQueryValueEx(hKeyPath,
                                lpszValueName,
                                NULL,
                                NULL,
                                (PBYTE) lpszValue,
                                lpdwSize)) == ERROR_SUCCESS) {
            bRet = TRUE;
    }

    RegCloseKey(hKeyPath);

RPVO_exit:
    return bRet;
}

/*******************************************************************************
*
*  ReadPowerIntOptional
*
*  DESCRIPTION:
*   Integer value may not exist.
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN ReadPowerIntOptional(
    HKEY    hKey,
    LPTSTR  lpszPath,
    LPTSTR  lpszValueName,
    PINT    piVal
)
{
    HKEY     hKeyPath;
    BOOLEAN  bRet = FALSE;
    DWORD    dwSize;
    TCHAR    szNum[NUM_DEC_DIGITS];
    LONG     lRet;

    if ((lRet = RegOpenKey(hKey,
                           lpszPath,
                           &hKeyPath)) != ERROR_SUCCESS) {
        goto RPVO_exit;
    }

    dwSize = sizeof(szNum);
    if ((lRet = RegQueryValueEx(hKeyPath,
                                lpszValueName,
                                NULL,
                                NULL,
                                (PBYTE) szNum,
                                &dwSize)) == ERROR_SUCCESS) {
        if (MyStrToInt(szNum, piVal)) {
            bRet = TRUE;
        }
    }

    RegCloseKey(hKeyPath);

RPVO_exit:
    return bRet;
}

/*******************************************************************************
*
*  CreatePowerValue
*
*  DESCRIPTION:
*   Value may not exist.
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN CreatePowerValue(
    HKEY    hKey,
    LPCTSTR  lpszPath,
    LPCTSTR  lpszValueName,
    LPCTSTR  lpszValue
)
{
   DWORD    dwDisposition, dwDescSize;
   HKEY     hKeyPath;
   BOOLEAN  bRet = FALSE;
   DWORD    dwSize;
   LONG     lRet;
    
    // Wait on/take the registry semaphore.
    if (!TakeRegSemaphore())        // Will SetLastError
    {
        return FALSE;
    }

    if ((lRet = RegCreateKeyEx(hKey,
                               lpszPath,
                               0,
                               TEXT(""),
                               REG_OPTION_NON_VOLATILE,
                               KEY_WRITE,
                               NULL,
                               &hKeyPath,
                               &dwDisposition)) == ERROR_SUCCESS)
    {
       if (lpszValue)
       {
           dwSize = (lstrlen(lpszValue) + 1) * sizeof(TCHAR);
           if ((lRet = RegSetValueEx(hKeyPath,
                                     lpszValueName,
                                     0,
                                     REG_SZ,
                                     (PBYTE) lpszValue,
                                     dwSize)) == ERROR_SUCCESS)
           {
               bRet = TRUE;
           }
       }
       else
       {
           lRet = ERROR_INVALID_PARAMETER;
       }

       RegCloseKey(hKeyPath);
    }
    
    if (!bRet)
    {
       SetLastError(lRet);
    }

    ReleaseSemaphore(g_hSemRegistry, 1, NULL);
    return bRet;
}


/*******************************************************************************
*
*  ReadWritePowerValue
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN ReadWritePowerValue(
    HKEY    hKey,
    LPTSTR  lpszPath,
    LPTSTR  lpszValueName,
    LPTSTR  lpszValue,
    LPDWORD lpdwSize,
    BOOLEAN bWrite,
    BOOLEAN bTakeSemaphore
)
{
    // This function will set the Last Error correctly on failure
    HKEY     hKeyPath;
    BOOLEAN  bRet = FALSE;
    DWORD    dwSize;
    LONG     lRet;

    if ((lRet = RegOpenKey(hKey,
                           lpszPath,
                           &hKeyPath)) != ERROR_SUCCESS)
    {
        goto RWPV_exit;
    }

    // Wait on/take the registry semaphore.
    if (bTakeSemaphore)
    {
        if (!TakeRegSemaphore())        // Will Set last error
        {
            return FALSE;
        }
    }

    if (bWrite)
    {
        // Write current case.
        if (lpszValue)
        {
            dwSize = (lstrlen(lpszValue) + 1) * sizeof(TCHAR);
            if ((lRet = RegSetValueEx(hKeyPath,
                                      lpszValueName,
                                      0,
                                      REG_SZ,
                                      (PBYTE) lpszValue,
                                      dwSize)) == ERROR_SUCCESS)
            {
                bRet = TRUE;
            }
        }
        else
        {
            lRet = ERROR_INVALID_PARAMETER;
        }
    }
    else
    {
        // Read current case.
        if ((lRet = RegQueryValueEx(hKeyPath,
                                    lpszValueName,
                                    NULL,
                                    NULL,
                                    (PBYTE) lpszValue,
                                    lpdwSize)) == ERROR_SUCCESS)
        {
            bRet = TRUE;
        }
    }

    if (bTakeSemaphore)
    {
        ReleaseSemaphore(g_hSemRegistry, 1, NULL);
    }
    RegCloseKey(hKeyPath);

RWPV_exit:
    if (!bRet)
    {
        if (lRet == ERROR_SUCCESS)
        {
            lRet = GetLastError();
        }

        SetLastError(lRet);

        // Access denied is a valid result. 
        if (lRet != ERROR_ACCESS_DENIED)
        {
            DebugPrint("ReadWritePowerValue, failed, lpszValueName: %s, LastError: 0x%08X", lpszValueName, lRet);
        }
    }
    return bRet;
}

/*******************************************************************************
*
*  ReadPwrPolicyEx
*
*  DESCRIPTION:
*   Supports ReadPwrScheme and ReadGlobalPwrPolicy
*
*  PARAMETERS:
*   lpdwDescSize - Pointer to size of optional description buffer.
*   lpszDesc     - Optional description buffer.
*
*******************************************************************************/

DWORD ReadPwrPolicyEx2(
    LPTSTR  lpszUserKeyName,
    LPTSTR  lpszMachineKeyName,
    LPTSTR  lpszSchemeName,
    LPTSTR  lpszDesc,
    LPDWORD lpdwDescSize,
    LPVOID  lpvUser,
    DWORD   dwcbUserSize,
    LPVOID  lpvMachine,
    DWORD   dwcbMachineSize
)
{
    HKEY     hKeyUser, hKeyMachine;
    DWORD    dwType, dwSize;
    DWORD dwError = ERROR_SUCCESS;
    BOOLEAN  bRet = FALSE;

    if ((!lpszUserKeyName || !lpszMachineKeyName) ||
        (!lpszSchemeName  || !lpvUser || !lpvMachine) ||
        (!lpdwDescSize    && lpszDesc) ||
        (lpdwDescSize     && !lpszDesc))
    {
        dwError = ERROR_INVALID_PARAMETER;
    }
    else
    {
        // Wait on/take the registry semaphore.
        if (!TakeRegSemaphore())        // Will Set Last Error 
        {
            return GetLastError();
        }

        dwError = OpenPathKeys(lpszUserKeyName, lpszMachineKeyName, lpszSchemeName, &hKeyUser, &hKeyMachine, TRUE, KEY_READ);
        if (ERROR_SUCCESS != dwError)
        {
            ReleaseSemaphore(g_hSemRegistry, 1, NULL);
            return dwError;
        }

        dwSize = dwcbUserSize;
        dwError = RegQueryValueEx(hKeyUser,
                               TEXT("Policies"),
                               NULL,
                               &dwType,
                               (PBYTE) lpvUser,
                               &dwSize);

        if (dwError == ERROR_SUCCESS)
        {
            if (dwType == REG_BINARY)
            {
                dwSize = dwcbMachineSize;
                dwError = RegQueryValueEx(hKeyMachine,
                                       TEXT("Policies"),
                                       NULL,
                                       &dwType,
                                       (PBYTE) lpvMachine,
                                       &dwSize);
            }
            else
            {
                dwError = ERROR_INVALID_DATATYPE;
            }
        }

        if (dwError == ERROR_SUCCESS)
        {
            if (dwType == REG_BINARY)
            {
                if (lpdwDescSize)
                {
                    dwError = RegQueryValueEx(hKeyUser, TEXT("Description"), NULL, &dwType, (PBYTE) lpszDesc, lpdwDescSize);
                }
            }
            else
            {
                dwError = ERROR_INVALID_DATATYPE;
            }
        }

        RegCloseKey(hKeyUser);
        RegCloseKey(hKeyMachine);
        ReleaseSemaphore(g_hSemRegistry, 1, NULL);
    }

    if (ERROR_SUCCESS != dwError)
    {
        DebugPrint("ReadPwrPolicyEx, failed, LastError: 0x%08X", dwError);
        DebugPrint("  lpszUserKeyName: %s, lpszSchemeName: %s", lpszUserKeyName, lpszSchemeName);
        SetLastError(dwError);
    }

    return dwError;
}

DWORD 
ReadProcessorPwrPolicy(
    LPTSTR lpszMachineKeyName, 
    LPTSTR lpszSchemeName, 
    LPVOID lpvMachineProcessor, 
    DWORD dwcbMachineProcessorSize
    )
{
    HKEY    hKeyMachine = NULL;
    HKEY    hKeyPolicy = NULL;
    DWORD   dwError = ERROR_SUCCESS;
    DWORD   dwDisposition, dwSize, dwType;

    if (!lpszMachineKeyName || !lpvMachineProcessor) {
        dwError = ERROR_INVALID_PARAMETER;
        goto ReadProcessorPwrPolicyEnd;
    }

    // Wait on/take the registry semaphore.
    if (!TakeRegSemaphore())        // Will Set Last Error 
    {
        return GetLastError();
    }

    dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpszMachineKeyName, 0, KEY_READ, &hKeyMachine);
    if (ERROR_SUCCESS != dwError) goto ReadProcessorPwrPolicyExit;

    dwError = RegCreateKeyEx(hKeyMachine, lpszSchemeName, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKeyPolicy, &dwDisposition);
    if (ERROR_SUCCESS != dwError) goto ReadProcessorPwrPolicyExit;

    dwSize = dwcbMachineProcessorSize;
    dwError = RegQueryValueEx(hKeyPolicy,
                           TEXT("Policies"),
                           NULL,
                           &dwType,
                           (PBYTE) lpvMachineProcessor,
                           &dwSize);

    if (REG_BINARY != dwType) {
        dwError = ERROR_INVALID_DATATYPE;
    }
    
ReadProcessorPwrPolicyExit:

    if (hKeyPolicy) RegCloseKey(hKeyPolicy);
    if (hKeyMachine) RegCloseKey(hKeyMachine);
    ReleaseSemaphore(g_hSemRegistry, 1, NULL);

ReadProcessorPwrPolicyEnd:

    if (ERROR_SUCCESS != dwError)
    {
        DebugPrint("ReadProcessorPwrPolicy, failed, LastError: 0x%08X", dwError);
        DebugPrint("  lpszMachineKeyName: %s, lpszSchemeName: %s", lpszMachineKeyName, lpszSchemeName);
        SetLastError(dwError);
    }

    return dwError;
}

DWORD 
WriteProcessorPwrPolicy(
    LPTSTR lpszMachineKeyName, 
    LPTSTR lpszSchemeName, 
    LPVOID lpvMachineProcessor, 
    DWORD dwcbMachineProcessorSize
    )
{
    HKEY    hKeyMachine = NULL;
    HKEY    hKeyPolicy = NULL;
    DWORD   dwError = ERROR_SUCCESS;
    DWORD   dwDisposition;

    if (!lpszMachineKeyName || !lpvMachineProcessor) {
        dwError = ERROR_INVALID_PARAMETER;
        goto WriteProcessorPwrPolicyEnd;
    }

    // Wait on/take the registry semaphore.
    if (!TakeRegSemaphore())        // Will Set Last Error 
    {
        return GetLastError();
    }

    dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpszMachineKeyName, 0, KEY_WRITE, &hKeyMachine);
    if (ERROR_SUCCESS != dwError) goto WriteProcessorPwrPolicyExit;

    dwError = RegCreateKeyEx(hKeyMachine, lpszSchemeName, 0, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyPolicy, &dwDisposition);
    if (ERROR_SUCCESS != dwError) goto WriteProcessorPwrPolicyExit;

    dwError = RegSetValueEx(hKeyPolicy,
                           TEXT("Policies"),
                           0,
                           REG_BINARY,
                           (PBYTE) lpvMachineProcessor,
                           dwcbMachineProcessorSize);

WriteProcessorPwrPolicyExit:

    if (hKeyPolicy) RegCloseKey(hKeyPolicy);
    if (hKeyMachine) RegCloseKey(hKeyMachine);
    ReleaseSemaphore(g_hSemRegistry, 1, NULL);

WriteProcessorPwrPolicyEnd:

    if (ERROR_SUCCESS != dwError)
    {
        DebugPrint("WriteProcessorPwrPolicy, failed, LastError: 0x%08X", dwError);
        DebugPrint("  lpszMachineKeyName: %s, lpszSchemeName: %s", lpszMachineKeyName, lpszSchemeName);
        SetLastError(dwError);
    }

    return dwError;
}

/*******************************************************************************
*
*  WritePwrPolicyEx
*
*  DESCRIPTION:
*   Supports WritePwrScheme and
*   WriteGlobalPwrPolicy
*
*  PARAMETERS:
*
*******************************************************************************/

BOOLEAN WritePwrPolicyEx(
    LPTSTR  lpszUserKeyName,
    LPTSTR  lpszMachineKeyName,
    PUINT   puiID,
    LPTSTR  lpszName,
    LPTSTR  lpszDescription,
    LPVOID  lpvUser,
    DWORD   dwcbUserSize,
    LPVOID  lpvMachine,
    DWORD   dwcbMachineSize
)
{
    // The function will set the last error if it fails.
    HKEY     hKeyUser, hKeyMachine;
    LONG     lRet = ERROR_SUCCESS;
    DWORD    dwDisposition, dwSize;
    TCHAR    szNum[NUM_DEC_DIGITS];
    LPTSTR   lpszKeyName;

    if ((!lpszUserKeyName || !lpszMachineKeyName || !lpvUser || !lpvMachine) || (!puiID && !lpszName))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        lRet = ERROR_INVALID_PARAMETER;
    }
    else
    {
        // If a scheme ID was passed
        if (puiID)
        {
            if (*puiID == NEWSCHEME)
            {
                *puiID = ++g_uiLastID;
                wsprintf(szNum, TEXT("%d"), *puiID);

                // This ReadWritePowerValue will SetLastError
                if (!ReadWritePowerValue(HKEY_LOCAL_MACHINE,
                                         c_szREGSTR_PATH_MACHINE_POWERCFG,
                                         c_szREGSTR_VAL_LASTID,
                                         szNum, &dwSize, TRUE, TRUE))
                {
                    return FALSE;
                }
            }
            else
            {
                wsprintf(szNum, TEXT("%d"), *puiID);
            }
            lpszKeyName = szNum;
        }
        else
        {
            lpszKeyName = lpszName;
        }

        // Wait on/take the registry semaphore.
        if (!TakeRegSemaphore())    // Will set last error
        {
            return FALSE;
        }

        lRet = OpenPathKeys(lpszUserKeyName, lpszMachineKeyName, lpszKeyName, &hKeyUser, &hKeyMachine, FALSE, KEY_WRITE);
        if (ERROR_SUCCESS != lRet)
        {
            ReleaseSemaphore(g_hSemRegistry, 1, FALSE);
            SetLastError(lRet);
            return FALSE;
        }

        // Write the binary policies data
        if ((lRet = RegSetValueEx(hKeyUser,
                                  TEXT("Policies"),
                                  0,
                                  REG_BINARY,
                                  (PBYTE) lpvUser,
                                  dwcbUserSize)) == ERROR_SUCCESS)
        {
            // Write the binary policies data
            if ((lRet = RegSetValueEx(hKeyMachine,
                                      TEXT("Policies"),
                                      0,
                                      REG_BINARY,
                                      (PBYTE) lpvMachine,
                                      dwcbMachineSize)) == ERROR_SUCCESS)
            {
                // Write the name text if an ID was provided.
                if (lpszName && puiID)
                {
                    dwSize = (lstrlen(lpszName) + 1) * sizeof(TCHAR);
                    lRet = RegSetValueEx(hKeyUser, TEXT("Name"), 0, REG_SZ, (PBYTE) lpszName, dwSize);
                }

                // Write the description text.
                if (lpszDescription && (lRet == ERROR_SUCCESS))
                {
                    dwSize = (lstrlen(lpszDescription) + 1) * sizeof(TCHAR);
                    lRet = RegSetValueEx(hKeyUser, TEXT("Description"), 0, REG_SZ, (PBYTE) lpszDescription, dwSize);
                }
            }
        }
        RegCloseKey(hKeyUser);
        RegCloseKey(hKeyMachine);
        ReleaseSemaphore(g_hSemRegistry, 1, NULL);
    }

    if (lRet != ERROR_SUCCESS)
    {
        DebugPrint("WritePwrPolicyEx, failed, LastError: 0x%08X", lRet);
        DebugPrint("  lpszUserKeyName: %s, lpszKeyName: %s", lpszUserKeyName, lpszKeyName);

        SetLastError(lRet);
        return FALSE;
    }
    return TRUE;
}