/*++

Copyright (c) 1991-1999  Microsoft Corporation

Module Name:

    dumpload.c

Abstract:

    functions to dump and load the contents of the performance related registry
	entries

Author:

    Bob Watson (bobw) 13 Jun 99

Revision History:



--*/
#ifndef     UNICODE
#define     UNICODE     1
#endif

#ifndef     _UNICODE
#define     _UNICODE    1
#endif
//
//  "C" Include files
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
//
//  Windows Include files
//
#include <windows.h>
#include <winperf.h>
#include <loadperf.h>
#include "wmistr.h"
#include "evntrace.h"
//
//  application include files
//
#include "winperfp.h"
#include "common.h"
#include "ldprfmsg.h"

static const WCHAR  cszServiceKeyName[] = {L"SYSTEM\\CurrentControlSet\\Services"};
static const WCHAR  cszPerflibKeyName[] = {L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"};
static const WCHAR  cszLastCounter[] = {L"Last Counter"};
static const WCHAR  cszFirstHelp[] = {L"First Help"};
static const WCHAR  cszLastHelp[] = {L"Last Help"};
static const WCHAR  cszBaseIndex[] = {L"Base Index"};
static const WCHAR  cszPerformance[] = {L"\\Performance"};
static const WCHAR  cszDisablePerformanceCounters[] = {L"Disable Performance Counters"};

// headings in save file
static const WCHAR  cszFmtSectionHeader[] = {L"\r\n\r\n[%s]"};
static const WCHAR  cszFmtServiceSectionHeader[] = {L"\r\n\r\n[PERF_%s]"};
static const WCHAR  cszFmtServiceSectionName[] = {L"PERF_%s"};
static const WCHAR  cszFmtStringSectionHeader[] = {L"\r\n\r\n[PerfStrings_%s]"};
static const WCHAR  cszFmtExtCtrString[] = {L"\r\n%d=%s"};
static const WCHAR  cszFmtDecimalParam[] = {L"\r\n%s=%d"};
static const WCHAR  cszFmtNoParam[] = {L"\r\n%s="};

static const WCHAR  cszExtensiblePerfStrings[] = {L"Strings"};
static const WCHAR  cszPerfCounterServices[] = {L"PerfCounterServices"};
static const WCHAR  cszNoPerfCounterServices[] = {L"NoPerfCounterServices"};
static const WCHAR  cszPerflib[] = {L"Perflib"};

// external forward definitions
LPWSTR
*BuildNameTable(
    HKEY    hKeyRegistry,   // handle to registry db with counter names
    LPWSTR  lpszLangId,     // unicode value of Language subkey
    PDWORD  pdwLastItem     // size of array in elements
);

DWORD
UpdatePerfNameFilesX (
	IN  LPCWSTR     szNewCtrFilePath,   // data file with new base counter strings
	IN  LPCWSTR     szNewHlpFilePath,   // data file with new base counter strings
    IN 	LPWSTR      szLanguageID,       // Lang ID to update
    IN  ULONG_PTR   dwFlags             // flags
);

DWORD
static
DumpNameTable (
    IN  HANDLE  hOutputFile,
    IN  LPCWSTR szLangId,
    IN  LPCWSTR *pszNameTable,
    IN  DWORD   dwStartIndex,
    IN  DWORD   dwLastIndex
)
{
    DWORD   ndx;
    WCHAR   szOutputBuffer[4096];
    DWORD   dwSize, dwSizeWritten;

    dwSize = swprintf (szOutputBuffer, cszFmtStringSectionHeader, szLangId);
    dwSize *= sizeof (WCHAR);

    WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);

    for (ndx = dwStartIndex; ndx <= dwLastIndex; ndx++) {
        if (pszNameTable[ndx] != NULL) {
            dwSize = swprintf (szOutputBuffer, cszFmtExtCtrString, ndx, pszNameTable[ndx] );
            dwSize *= sizeof (WCHAR);
            WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
        }
    }

    return ERROR_SUCCESS;
}

DWORD
static
DumpPerfServiceEntries (
    IN  HANDLE  hOutputFile,
    IN  LPCWSTR szServiceName
)
{
    LONG    lStatus = ERROR_SUCCESS;
    WCHAR   szPerfSubKeyName[MAX_PATH+20];
    HKEY    hKeyPerformance;
    HKEY    hKeyServices = NULL;
    DWORD   dwItemSize, dwType, dwValue;
    DWORD   dwRegAccessMask;
    DWORD   dwRetStatus = ERROR_SUCCESS;

    DWORD   dwSize, dwSizeWritten;
    WCHAR   szOutputBuffer[4096];

    // try read-only then
    dwRegAccessMask = KEY_READ;
    lStatus = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
        cszServiceKeyName,
        0L,
        dwRegAccessMask,
        &hKeyServices);

    if (lStatus == ERROR_SUCCESS) {
        //try to open the perfkey under this key.
        lstrcpy (szPerfSubKeyName, szServiceName);
        lstrcat (szPerfSubKeyName, cszPerformance);

        lStatus = RegOpenKeyExW (
            hKeyServices,
            szPerfSubKeyName,
            0L,
            dwRegAccessMask,
            &hKeyPerformance);

        if (lStatus == ERROR_SUCCESS) {
            // key found so service has perf data
            dwSize = swprintf (szOutputBuffer, cszFmtServiceSectionHeader, szServiceName);
            dwSize *= sizeof (WCHAR);
            WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);

            // now check to see if the strings have been loaded
            dwType = dwValue = 0;
            dwItemSize = sizeof (dwValue);
            lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszFirstCounter,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   TRACE_WSTR(cszFirstCounter),
                   TRACE_DWORD(dwValue),
                   NULL));
            if ((lStatus == ERROR_SUCCESS) &&
                ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszFirstCounter, dwValue);
                dwSize *= sizeof (WCHAR);
                WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            }

            dwType = dwValue = 0;
            dwItemSize = sizeof (dwValue);
            lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszFirstHelp,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   TRACE_WSTR(cszFirstHelp),
                   TRACE_DWORD(dwValue),
                   NULL));
            if ((lStatus == ERROR_SUCCESS) &&
                ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszFirstHelp, dwValue);
                dwSize *= sizeof (WCHAR);
                WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            }

            dwType = dwValue = 0;
            dwItemSize = sizeof (dwValue);
            lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszLastCounter,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   TRACE_WSTR(cszLastCounter),
                   TRACE_DWORD(dwValue),
                   NULL));
            if ((lStatus == ERROR_SUCCESS) &&
                ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszLastCounter, dwValue);
                dwSize *= sizeof (WCHAR);
                WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            }

            dwType = dwValue = 0;
            dwItemSize = sizeof (dwValue);
            lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszLastHelp,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   TRACE_WSTR(cszLastHelp),
                   TRACE_DWORD(dwValue),
                   NULL));
            if ((lStatus == ERROR_SUCCESS) &&
                ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszLastHelp, dwValue);
                dwSize *= sizeof (WCHAR);
                WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            }

            dwType = dwValue = 0;
            dwItemSize = sizeof (dwValue);
            lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszDisablePerformanceCounters,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   TRACE_WSTR(cszDisablePerformanceCounters),
                   TRACE_DWORD(dwValue),
                   NULL));
            if ((lStatus == ERROR_SUCCESS) &&
                ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszDisablePerformanceCounters, dwValue);
                dwSize *= sizeof (WCHAR);
                WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            }

            RegCloseKey (hKeyPerformance);
        } else {
            dwRetStatus = lStatus;
            TRACE((WINPERF_DBG_TRACE_ERROR),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_DUMPPERFSERVICEENTRIES,
                   ARG_DEF(ARG_TYPE_WSTR, 1),
                   lStatus,
                   TRACE_WSTR(szServiceName),
                   NULL));
        }

        RegCloseKey (hKeyServices);
    }
    else {
        dwRetStatus = lStatus;
        TRACE((WINPERF_DBG_TRACE_ERROR),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_DUMPPERFSERVICEENTRIES,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               lStatus,
               TRACE_WSTR(cszServiceKeyName),
               NULL));
    }

    return dwRetStatus;
}

DWORD
static
DumpPerflibEntries (
    IN  HANDLE  hOutputFile,
    IN  LPDWORD pdwFirstExtCtrIndex

)
{
    HKEY    hKeyPerflib = NULL;
    DWORD   dwStatus;
    DWORD   dwItemSize, dwType, dwValue;

    DWORD   dwSize, dwSizeWritten;
    WCHAR   szOutputBuffer[4096];

    dwStatus = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
        cszPerflibKeyName,
        0L,
        KEY_READ,
        &hKeyPerflib);

    if (dwStatus == ERROR_SUCCESS) {
        dwSize = swprintf (szOutputBuffer, cszFmtSectionHeader, cszPerflib);
        dwSize *= sizeof (WCHAR);
        WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
    }
    else {
        TRACE((WINPERF_DBG_TRACE_ERROR),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_DUMPPERFLIBENTRIES,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               dwStatus,
               TRACE_WSTR(cszPerflibKeyName),
               NULL));
    }

    if (dwStatus == ERROR_SUCCESS) {
        dwType = dwValue = 0;
        dwItemSize = sizeof (dwValue);
        dwStatus = RegQueryValueEx (
                hKeyPerflib,
                cszBaseIndex,
                NULL,
                &dwType,
                (LPBYTE)&dwValue,
                &dwItemSize);
        TRACE((WINPERF_DBG_TRACE_INFO),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_DUMPPERFLIBENTRIES,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               dwStatus,
               TRACE_WSTR(cszBaseIndex),
               TRACE_DWORD(dwValue),
               NULL));
        if ((dwStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszBaseIndex, dwValue);
            dwSize *= sizeof (WCHAR);
            WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
            *pdwFirstExtCtrIndex = dwValue + 1;
        }
    }

    if (dwStatus == ERROR_SUCCESS) {
        dwType = dwValue = 0;
        dwItemSize = sizeof (dwValue);
        dwStatus = RegQueryValueEx (
                hKeyPerflib,
                cszLastCounter,
                NULL,
                &dwType,
                (LPBYTE)&dwValue,
                &dwItemSize);
        TRACE((WINPERF_DBG_TRACE_INFO),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_DUMPPERFLIBENTRIES,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               dwStatus,
               TRACE_WSTR(cszLastCounter),
               TRACE_DWORD(dwValue),
               NULL));
        if ((dwStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszLastCounter, dwValue);
            dwSize *= sizeof (WCHAR);
            WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
        }
    }

    if (dwStatus == ERROR_SUCCESS) {
        dwType = dwValue = 0;
        dwItemSize = sizeof (dwValue);
        dwStatus = RegQueryValueEx (
                hKeyPerflib,
                cszLastHelp,
                NULL,
                &dwType,
                (LPBYTE)&dwValue,
                &dwItemSize);
        TRACE((WINPERF_DBG_TRACE_INFO),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_DUMPPERFLIBENTRIES,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               dwStatus,
               TRACE_WSTR(cszLastHelp),
               TRACE_DWORD(dwValue),
               NULL));
        if ((dwStatus == ERROR_SUCCESS) && (dwType == REG_DWORD)) {
            dwSize = swprintf (szOutputBuffer, cszFmtDecimalParam, cszLastHelp, dwValue);
            dwSize *= sizeof (WCHAR);
            WriteFile (hOutputFile, szOutputBuffer, dwSize, &dwSizeWritten, NULL);
        }
    }

    if (hKeyPerflib != NULL) RegCloseKey (hKeyPerflib);

    return dwStatus;
}


DWORD
static
BuildServiceLists (
    IN  LPWSTR  mszPerfServiceList,
    IN  LPDWORD pcchPerfServiceListSize,
    IN  LPWSTR  mszNoPerfServiceList,
    IN  LPDWORD pcchNoPerfServiceListSize
)
{
    LONG    lStatus = ERROR_SUCCESS;
    LONG    lEnumStatus = ERROR_SUCCESS;
    DWORD   dwServiceIndex = 0;
    WCHAR   szServiceSubKeyName[MAX_PATH];
    WCHAR   szPerfSubKeyName[MAX_PATH+20];
    DWORD   dwNameSize = MAX_PATH;
    HKEY    hKeyPerformance;
    HKEY    hKeyServices = NULL;
    DWORD   dwItemSize, dwType, dwValue;
    DWORD   dwRegAccessMask;
    DWORD   bServiceHasPerfCounters;
    DWORD   dwRetStatus = ERROR_SUCCESS;

    LPWSTR  szNextNoPerfChar, szNextPerfChar;
    DWORD   dwNoPerfSizeRem, dwPerfSizeRem;
    DWORD   dwPerfSizeUsed = 0, dwNoPerfSizeUsed = 0;

    // try read-only then
    dwRegAccessMask = KEY_READ;
    lStatus = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
        cszServiceKeyName,
        0L,
        dwRegAccessMask,
        &hKeyServices);

    if (lStatus == ERROR_SUCCESS) {
        szNextNoPerfChar = mszNoPerfServiceList;
        szNextPerfChar = mszPerfServiceList;
        dwNoPerfSizeRem = *pcchPerfServiceListSize;
        dwPerfSizeRem = *pcchNoPerfServiceListSize;
        dwPerfSizeUsed = 0;
        dwNoPerfSizeUsed = 0;

        while ((lEnumStatus = RegEnumKeyExW (
            hKeyServices,
            dwServiceIndex,
            szServiceSubKeyName,
            &dwNameSize,
            NULL,
            NULL,
            NULL,
            NULL)) == ERROR_SUCCESS) {

            //try to open the perfkey under this key.
            lstrcpy (szPerfSubKeyName, szServiceSubKeyName);
            lstrcat (szPerfSubKeyName, cszPerformance);

            lStatus = RegOpenKeyExW (
                hKeyServices,
                szPerfSubKeyName,
                0L,
                dwRegAccessMask,
                &hKeyPerformance);

            if (lStatus == ERROR_SUCCESS) {
                // key found so service has perf data
                // now check to see if the strings have been loaded
                dwType = dwValue = 0;
                dwItemSize = sizeof (dwValue);
                lStatus = RegQueryValueExW (
                    hKeyPerformance,
                    cszFirstCounter,
                    NULL,
                    &dwType,
                    (LPBYTE)&dwValue,
                    &dwItemSize);
                if ((lStatus == ERROR_SUCCESS) &&
                    ((dwType == REG_DWORD) || dwType == REG_BINARY)) {
                    bServiceHasPerfCounters = TRUE;
                } else {
                    bServiceHasPerfCounters = FALSE;
                }

                RegCloseKey (hKeyPerformance);
            } else {
                // key not found so service doesn't have perfdata
                bServiceHasPerfCounters = FALSE;
            }

            TRACE((WINPERF_DBG_TRACE_INFO),
                  (& LoadPerfGuid,
                   __LINE__,
                   LOADPERF_BUILDSERVICELISTS,
                   ARG_DEF(ARG_TYPE_WSTR, 1),
                   lStatus,
                   TRACE_WSTR(szServiceSubKeyName),
                   TRACE_DWORD(bServiceHasPerfCounters),
                   NULL));

            if (bServiceHasPerfCounters != FALSE) {
                // add to the perf service list
                if ((dwNameSize + 1)< dwPerfSizeRem) {
                    // add to list
                    lstrcpyW (szNextPerfChar, szServiceSubKeyName);
                    szNextPerfChar += dwNameSize;
                    *szNextPerfChar = 0;
                    szNextPerfChar++;
                    dwPerfSizeRem -= dwNameSize + 1;
                } else {
                    dwRetStatus = ERROR_MORE_DATA;
                }
                dwPerfSizeUsed += dwNameSize + 1;
            } else {
                // add to the no perf list
                if ((dwNameSize + 1) < dwNoPerfSizeRem) {
                    // add to list
                    lstrcpyW (szNextNoPerfChar, szServiceSubKeyName);
                    szNextNoPerfChar += dwNameSize;
                    *szNextNoPerfChar = 0;
                    szNextNoPerfChar++;
                    dwNoPerfSizeRem -= dwNameSize + 1;
                } else {
                    dwRetStatus = ERROR_MORE_DATA;
                }
                dwNoPerfSizeUsed += dwNameSize + 1;
            }
            // reset for next loop
            dwServiceIndex++;
            dwNameSize = MAX_PATH;
        }

        // zero term the MSZ
        if (1 < dwPerfSizeRem) {
            *szNextPerfChar = 0;
            szNextPerfChar++;
            dwPerfSizeRem -= 1;
        } else {
            dwRetStatus = ERROR_MORE_DATA;
        }
        dwPerfSizeUsed += 1;

        // zero term the no perf list
        if (1 < dwNoPerfSizeRem) {
            // add to list
            *szNextNoPerfChar = 0;
            szNextNoPerfChar++;
            dwNoPerfSizeRem -= 1;
        } else {
            dwRetStatus = ERROR_MORE_DATA;
        }
        dwNoPerfSizeUsed += 1;
    }
    else {
        TRACE((WINPERF_DBG_TRACE_ERROR),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_BUILDSERVICELISTS,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               lStatus,
               TRACE_WSTR(cszServiceKeyName),
               NULL));
    }

    if (hKeyServices != NULL) RegCloseKey (hKeyServices);

    *pcchPerfServiceListSize = dwPerfSizeUsed;
    *pcchNoPerfServiceListSize = dwNoPerfSizeUsed;

    return dwRetStatus;
}


DWORD
BackupPerfRegistryToFileW (
    IN  LPCWSTR szFileName,
    IN  LPCWSTR szCommentString
)
{
    HANDLE  hOutFile;
    DWORD   dwStatus = ERROR_SUCCESS;
    LPWSTR  szNewFileName = NULL;
    DWORD   dwNewFileNameLen;
    DWORD   dwOrigFileNameLen;
    DWORD   dwFileNameSN;

    LPWSTR  mszPerfServiceList = NULL;
    DWORD   dwPerfServiceListSize = 0;
    LPWSTR  mszNoPerfServiceList = NULL;
    DWORD   dwNoPerfServiceListSize = 0;

    LPWSTR  *lpCounterText = NULL;
    DWORD   dwLastElement = 0;
    DWORD   dwFirstExtCtrIndex = 0;

    LPWSTR  szThisServiceName;

    LPWSTR  szLangId = (LPWSTR)L"009";

    DBG_UNREFERENCED_PARAMETER (szCommentString);

    WinPerfStartTrace(NULL);

    // open output file
    hOutFile = CreateFileW (
        szFileName,
        GENERIC_WRITE,
        0,  // no sharing
        NULL,   // default security
        CREATE_NEW,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    // if the file open failed
    if (hOutFile == INVALID_HANDLE_VALUE) {
        // see if it's because the file already exists
        dwStatus = GetLastError();
        if (dwStatus == ERROR_FILE_EXISTS) {
            // then try appending a serial number to the name
            dwOrigFileNameLen = lstrlenW (szFileName);
            dwNewFileNameLen = dwOrigFileNameLen + 4;
            szNewFileName = HeapAlloc (
                GetProcessHeap(),
                HEAP_ZERO_MEMORY,
                (dwNewFileNameLen +1) * sizeof(WCHAR));
            if (szNewFileName != NULL) {
                lstrcpyW (szNewFileName, szFileName);
                for (dwFileNameSN = 1; dwFileNameSN < 1000; dwFileNameSN++) {
                    swprintf (&szNewFileName[dwOrigFileNameLen],
                        (LPCWSTR)L"_%3.3d", dwFileNameSN);
                    hOutFile = CreateFileW (
                        szNewFileName,
                        GENERIC_WRITE,
                        0,  // no sharing
                        NULL,   // default security
                        CREATE_NEW,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);

                    // if the file open failed
                    if (hOutFile == INVALID_HANDLE_VALUE) {
                        dwStatus = GetLastError();
                        if (dwStatus != ERROR_FILE_EXISTS) {
                            // some other error occurred so bail out
                            break;
                        } else {
                            continue; // with the next try
                        }
                    } else {
                        // found one not in use so continue on
                        dwStatus = ERROR_SUCCESS;
                        break;
                    }
                }
            } else {
                dwStatus = ERROR_OUTOFMEMORY;
            }
        }
    } else {
        // file opened so continue
        dwStatus = ERROR_SUCCESS;
    }

    if (dwStatus == ERROR_SUCCESS) {
        // dump perflib key entires
        dwStatus = DumpPerflibEntries (hOutFile, &dwFirstExtCtrIndex);
    }

    if (dwStatus == ERROR_SUCCESS) {
        do {
            if (mszPerfServiceList != NULL) {
                HeapFree (GetProcessHeap(), 0, mszPerfServiceList);
                mszPerfServiceList = NULL;
            }

            if (mszNoPerfServiceList != NULL) {
                HeapFree (GetProcessHeap(), 0, mszNoPerfServiceList);
                mszNoPerfServiceList = NULL;
            }

            // build service lists
            dwPerfServiceListSize += 32768;
            dwNoPerfServiceListSize += 65536;
            mszPerfServiceList = HeapAlloc (
                GetProcessHeap(),
                HEAP_ZERO_MEMORY,
                (dwPerfServiceListSize) * sizeof(WCHAR));

            mszNoPerfServiceList = HeapAlloc (
                GetProcessHeap(),
                HEAP_ZERO_MEMORY,
                (dwNoPerfServiceListSize) * sizeof(WCHAR));

            if ((mszNoPerfServiceList == NULL) || (mszPerfServiceList  == NULL)) {
                dwStatus = ERROR_OUTOFMEMORY;
                break;
            }

            if (dwStatus == ERROR_SUCCESS) {
                dwStatus = BuildServiceLists (
                    mszPerfServiceList,
                    &dwPerfServiceListSize,
                    mszNoPerfServiceList,
                    &dwNoPerfServiceListSize);
                if (dwStatus == ERROR_SUCCESS) break; // and continue on
            }
        } while (dwPerfServiceListSize < 4194304);
    }

    // dump service entries for those services with perf counters
    if (dwStatus == ERROR_SUCCESS) {
        for (szThisServiceName = mszPerfServiceList;
            *szThisServiceName != 0;
            szThisServiceName += lstrlenW(szThisServiceName)+1) {

            dwStatus = DumpPerfServiceEntries (
                hOutFile,
                szThisServiceName);

            if (dwStatus != ERROR_SUCCESS) break;
        }
    }

    // dump perf string entries
    if (dwStatus == ERROR_SUCCESS) {
        // build Name table
        // get ENGLISH string list
        lpCounterText = BuildNameTable (
            HKEY_LOCAL_MACHINE, // use only local machine
            (LPWSTR)szLangId,		
            &dwLastElement);

        if (lpCounterText != NULL) {
            dwStatus = DumpNameTable (
                hOutFile,
                szLangId,
                lpCounterText,
                0,              // dump the entire table for complete restore
                dwLastElement
                );
        } else {
            dwStatus = GetLastError();
        }
    }

    // free buffers
    if (lpCounterText != NULL) {
        HeapFree (GetProcessHeap(), 0, lpCounterText);
        lpCounterText = NULL;
    }

    if (mszNoPerfServiceList != NULL) {
        HeapFree (GetProcessHeap(), 0, mszNoPerfServiceList);
        mszNoPerfServiceList = NULL;
    }

    if (mszPerfServiceList != NULL) {
        HeapFree (GetProcessHeap(), 0, mszPerfServiceList);
        mszPerfServiceList = NULL;
    }

    if (szNewFileName  != NULL) {
        HeapFree (GetProcessHeap(), 0, szNewFileName);
        szNewFileName = NULL;
    }

    // close file handles
    if (hOutFile != INVALID_HANDLE_VALUE) {
        CloseHandle (hOutFile);
    }

    return dwStatus;
}

DWORD
RestorePerfRegistryFromFileW (
    IN  LPCWSTR szFileName,
    IN  LPCWSTR szLangId
)
{
    LONG    lStatus = ERROR_SUCCESS;
    LONG    lEnumStatus = ERROR_SUCCESS;
    DWORD   dwServiceIndex = 0;
    WCHAR   szServiceSubKeyName[MAX_PATH];
    WCHAR   szPerfSubKeyName[MAX_PATH+20];
    DWORD   dwNameSize = MAX_PATH;
    HKEY    hKeyPerformance;
    HKEY    hKeyServices = NULL;
    HKEY    hKeyPerflib = NULL;
    DWORD   dwItemSize;
    DWORD   dwRegAccessMask;
    DWORD   dwRetStatus = ERROR_SUCCESS;
    UINT    nValue;
    DWORD   dwnValue;
    BOOL    bServiceRegistryOk = TRUE;

    WCHAR   wPerfSection[MAX_PATH * 2];

    LPWSTR  szLocalLangId;

    WinPerfStartTrace(NULL);

    if (szLangId == NULL) {
        szLocalLangId = (LPWSTR)L"009";
    } else {
        szLocalLangId = (LPWSTR)szLangId;
    }

    lStatus = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
        cszServiceKeyName,
        0L,
        KEY_READ,
        &hKeyServices);

    if (lStatus == ERROR_SUCCESS) {
        // enum service list
        while ((lEnumStatus = RegEnumKeyExW (
            hKeyServices,
            dwServiceIndex,
            szServiceSubKeyName,
            &dwNameSize,
            NULL,
            NULL,
            NULL,
            NULL)) == ERROR_SUCCESS) {

            //try to open the perfkey under this key.
            lstrcpy (szPerfSubKeyName, szServiceSubKeyName);
            lstrcat (szPerfSubKeyName, cszPerformance);

            bServiceRegistryOk = TRUE;
            dwRegAccessMask = KEY_READ | KEY_WRITE;
            // look for a performance subkey
            lStatus = RegOpenKeyExW (
                hKeyServices,
                szPerfSubKeyName,
                0L,
                dwRegAccessMask,
                &hKeyPerformance);

            if (lStatus == ERROR_SUCCESS) {
                // key found so service has perf data
                // if performance subkey then
                dwItemSize = swprintf (wPerfSection,
                    cszFmtServiceSectionName, szServiceSubKeyName);
                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                        wPerfSection,
                        cszFirstCounter,
                        -1,
                        szFileName);

                if (nValue != (UINT) -1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerformance,
                            cszFirstCounter,
                            0L,
                            REG_DWORD,
                            (const BYTE *) & nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                           lStatus,
                           TRACE_WSTR(szServiceSubKeyName),
                           TRACE_WSTR(cszFirstCounter),
                           TRACE_DWORD(dwnValue),
                           NULL));
                    // now read the other values
                } else {
                    // there's one or more missing entries so
                    // remove the whole entry
                    bServiceRegistryOk = FALSE;
                }

                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                        wPerfSection,
                        cszFirstHelp,
                        -1,
                        szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerformance,
                            cszFirstHelp,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                           lStatus,
                           TRACE_WSTR(szServiceSubKeyName),
                           TRACE_WSTR(cszFirstHelp),
                           TRACE_DWORD(dwnValue),
                           NULL));
                    // now read the other values
                } else {
                    // there's one or more missing entries so
                    // remove the whole entry
                    bServiceRegistryOk = FALSE;
                }

                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                    wPerfSection,
                    cszLastCounter,
                    -1,
                    szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerformance,
                            cszLastCounter,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                           lStatus,
                           TRACE_WSTR(szServiceSubKeyName),
                           TRACE_WSTR(cszLastCounter),
                           TRACE_DWORD(dwnValue),
                           NULL));
                    // now read the other values
                } else {
                    // there's one or more missing entries so
                    // remove the whole entry
                    bServiceRegistryOk = FALSE;
                }

                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                    wPerfSection,
                    cszLastHelp,
                    -1,
                    szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerformance,
                            cszLastHelp,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_WSTR, 2),
                           lStatus,
                           TRACE_WSTR(szServiceSubKeyName),
                           TRACE_WSTR(cszLastHelp),
                           TRACE_DWORD(dwnValue),
                           NULL));
                    // now read the other values
                } else {
                    // there's one or more missing entries so
                    // remove the whole entry
                    bServiceRegistryOk = FALSE;
                }

                if (!bServiceRegistryOk) {
                    // an error occurred so delete the first/last counter/help values
                    RegDeleteValue (hKeyPerformance, cszFirstCounter);
                    RegDeleteValue (hKeyPerformance, cszFirstHelp);
                    RegDeleteValue (hKeyPerformance, cszLastCounter);
                    RegDeleteValue (hKeyPerformance, cszLastHelp);
                } // else continiue

                RegCloseKey (hKeyPerformance);
            } // else this service has no perf data so skip
            else {
                TRACE((WINPERF_DBG_TRACE_ERROR),
                      (& LoadPerfGuid,
                       __LINE__,
                       LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                       ARG_DEF(ARG_TYPE_WSTR, 1),
                       lStatus,
                       TRACE_WSTR(szServiceSubKeyName),
                       NULL));
            }

            // reset for next loop
            dwServiceIndex++;
            dwNameSize = MAX_PATH;
        } // end enum service list
    }
    else {
        TRACE((WINPERF_DBG_TRACE_ERROR),
              (& LoadPerfGuid,
               __LINE__,
               LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
               ARG_DEF(ARG_TYPE_WSTR, 1),
               lStatus,
               TRACE_WSTR(cszServiceKeyName),
               NULL));
    }

    if (hKeyServices != NULL) RegCloseKey (hKeyServices);

    if (dwRetStatus == ERROR_SUCCESS) {
        // merge registry string values:
        dwRetStatus = UpdatePerfNameFilesX (
            szFileName, NULL,
            szLocalLangId,
            LODCTR_UPNF_RESTORE);

        if (dwRetStatus == ERROR_SUCCESS) {
            // update the keys in the registry

            lStatus = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
                    cszPerflibKeyName,
                    0L,
                    KEY_ALL_ACCESS,
                    &hKeyPerflib);
            if (lStatus != ERROR_SUCCESS) {
                TRACE((WINPERF_DBG_TRACE_ERROR),
                      (& LoadPerfGuid,
                       __LINE__,
                       LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                       ARG_DEF(ARG_TYPE_WSTR, 1),
                       lStatus,
                       TRACE_WSTR(cszPerflibKeyName),
                       NULL));
            }

            if (lStatus == ERROR_SUCCESS) {
               nValue = GetPrivateProfileIntW (
                    cszPerflib,
                    cszLastCounter,
                    -1,
                    szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerflib,
                            cszLastCounter,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1),
                           lStatus,
                           TRACE_WSTR(cszLastCounter),
                           TRACE_DWORD(dwnValue),
                           NULL));
                }
            }

            if (lStatus == ERROR_SUCCESS) {
                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                    cszPerflib,
                    cszLastHelp,
                    -1,
                    szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerflib,
                            cszLastHelp,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1),
                           lStatus,
                           TRACE_WSTR(cszLastHelp),
                           TRACE_DWORD(dwnValue),
                           NULL));
                }
            }

            if (lStatus == ERROR_SUCCESS) {
                // look into the file for a perf entry for this service
                nValue = GetPrivateProfileIntW (
                    cszPerflib,
                    cszBaseIndex,
                    -1,
                    szFileName);

                if (nValue != (UINT)-1) {
                    // if found in file then update registry with values from file
                    lStatus = RegSetValueExW (hKeyPerflib,
                            cszBaseIndex,
                            0L,
                            REG_DWORD,
                            (const BYTE *)&nValue,
                            sizeof(nValue));
                    dwnValue = nValue;
                    TRACE((WINPERF_DBG_TRACE_INFO),
                          (& LoadPerfGuid,
                           __LINE__,
                           LOADPERF_RESTOREPERFREGISTRYFROMFILEW,
                           ARG_DEF(ARG_TYPE_WSTR, 1),
                           lStatus,
                           TRACE_WSTR(cszBaseIndex),
                           TRACE_DWORD(dwnValue),
                           NULL));
                }
            }

            if (hKeyPerflib != NULL) RegCloseKey (hKeyPerflib);
        }
        dwRetStatus = lStatus;
    }

    return dwRetStatus;
}
