/*++ Copyright (c) 1991 Microsoft Corporation Module Name: lodctr.c Abstract: Program to read the contents of the file specified in the command line and update the registry accordingly Author: Bob Watson (a-robw) 10 Feb 93 Revision History: a-robw 25-Feb-93 revised calls to make it compile as a UNICODE or an ANSI app. --*/ #define UNICODE 1 #define _UNICODE 1 // // "C" Include files // #include #include #include #include // // Windows Include files // #include #include #include // #define _INITIALIZE_GLOBALS_ 1 #include "common.h" #undef _INITIALIZE_GLOBALS_ #define TYPE_HELP 1 #define TYPE_NAME 2 #include "nwcfg.hxx" #define OLD_VERSION 0x010000 DWORD dwSystemVersion; BOOL GetDriverName ( IN LPTSTR lpIniFile, OUT LPTSTR *lpDevName ) /*++ GetDriverName looks up driver name in the .ini file and returns it in lpDevName Arguments lpIniFile Filename of ini file lpDevName pointer to pointer to reciev buffer w/dev name in it Return Value TRUE if found FALSE if not found in .ini file --*/ { DWORD dwRetSize; if (lpDevName) { dwRetSize = GetPrivateProfileString ( TEXT("info"), // info section TEXT("drivername"), // driver name value TEXT("drivernameNotFound"), // default value *lpDevName, DISP_BUFF_SIZE, lpIniFile); if ((lstrcmpi(*lpDevName, TEXT("drivernameNotFound"))) != 0) { // name found return TRUE; } else { // name not found, default returned so return NULL string lstrcpy(*lpDevName,TEXT("\0")); return FALSE; } } else { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } } BOOL BuildLanguageTables ( IN LPTSTR lpIniFile, IN OUT PLANGUAGE_LIST_ELEMENT pFirstElem ) /*++ BuildLanguageTables Creates a list of structures that will hold the text for each supported language Arguments lpIniFile Filename with data pFirstElem pointer to first list entry ReturnValue TRUE if all OK FALSE if not --*/ { LPTSTR lpEnumeratedLangs; LPTSTR lpThisLang; PLANGUAGE_LIST_ELEMENT pThisElem; DWORD dwSize; lpEnumeratedLangs = malloc(SMALL_BUFFER_SIZE * sizeof(TCHAR)); if (!lpEnumeratedLangs) { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } dwSize = GetPrivateProfileString ( TEXT("languages"), NULL, // return all values in multi-sz string TEXT("009"), // english as the default lpEnumeratedLangs, SMALL_BUFFER_SIZE, lpIniFile); // do first language lpThisLang = lpEnumeratedLangs; pThisElem = pFirstElem; while (*lpThisLang) { pThisElem->pNextLang = NULL; pThisElem->LangId = (LPTSTR) malloc ((lstrlen(lpThisLang) + 1) * sizeof(TCHAR)); if (pThisElem->LangId == NULL) { free(lpEnumeratedLangs); SetLastError (ERROR_OUTOFMEMORY); return FALSE; } lstrcpy (pThisElem->LangId, lpThisLang); pThisElem->pFirstName = NULL; pThisElem->pThisName = NULL; pThisElem->dwNumElements=0; pThisElem->NameBuffer = NULL; pThisElem->HelpBuffer = NULL; // go to next string lpThisLang += lstrlen(lpThisLang) + 1; if (*lpThisLang) { // there's another so allocate a new element pThisElem->pNextLang = malloc (sizeof(LANGUAGE_LIST_ELEMENT)); if (!pThisElem) { free(pThisElem->LangId); free(lpEnumeratedLangs); SetLastError (ERROR_OUTOFMEMORY); return FALSE; } pThisElem = pThisElem->pNextLang; // point to new one } } free(lpEnumeratedLangs); return TRUE; } BOOL LoadIncludeFile ( IN LPTSTR lpIniFile, OUT PSYMBOL_TABLE_ENTRY *pTable ) /*++ LoadIncludeFile Reads the include file that contains symbolic name definitions and loads a table with the values defined Arguments lpIniFile Ini file with include file name pTable address of pointer to table structure created Return Value TRUE if table read or if no table defined FALSE if error encountere reading table --*/ { INT iNumArgs; DWORD dwSize; BOOL bReUse; BOOL bReturn = TRUE; PSYMBOL_TABLE_ENTRY pThisSymbol; LPTSTR lpIncludeFileName = NULL; LPSTR lpIncludeFile = NULL; LPSTR lpLineBuffer = NULL; LPSTR lpAnsiSymbol = NULL; FILE *fIncludeFile; HFILE hIncludeFile; OFSTRUCT ofIncludeFile; lpIncludeFileName = malloc (MAX_PATH * sizeof (TCHAR)); lpIncludeFile = malloc (MAX_PATH); lpLineBuffer = malloc (DISP_BUFF_SIZE); lpAnsiSymbol = malloc (DISP_BUFF_SIZE); if (!lpIncludeFileName || !lpIncludeFile || !lpLineBuffer || !lpAnsiSymbol) { if (lpIncludeFileName) { free(lpIncludeFileName); } if (lpIncludeFile) { free(lpIncludeFile); } if (lpLineBuffer) { free(lpLineBuffer); } if (lpAnsiSymbol) { free(lpAnsiSymbol); } SetLastError (ERROR_OUTOFMEMORY); return FALSE; } // get name of include file (if present) dwSize = GetPrivateProfileString ( TEXT("info"), TEXT("symbolfile"), TEXT("SymbolFileNotFound"), lpIncludeFileName, _msize(lpIncludeFileName), lpIniFile); if ((lstrcmpi(lpIncludeFileName, TEXT("SymbolFileNotFound"))) == 0) { // no symbol file defined *pTable = NULL; goto CleanUp2; } // if here, then a symbol file was defined and is now stored in // lpIncludeFileName CharToOem (lpIncludeFileName, lpIncludeFile); hIncludeFile = OpenFile ( lpIncludeFile, &ofIncludeFile, OF_PARSE); if (hIncludeFile == HFILE_ERROR) { // unable to read include filename // error is already in GetLastError *pTable = NULL; bReturn = FALSE; goto CleanUp2; } else { // open a stream fIncludeFile = fopen (ofIncludeFile.szPathName, "rt"); if (!fIncludeFile) { *pTable = NULL; bReturn = FALSE; goto CleanUp2; } } // // read ANSI Characters from include file // bReUse = FALSE; while (fgets(lpLineBuffer, DISP_BUFF_SIZE, fIncludeFile) != NULL) { if (strlen(lpLineBuffer) > 8) { if (!bReUse) { if (*pTable) { // then add to list pThisSymbol->pNext = malloc (sizeof (SYMBOL_TABLE_ENTRY)); pThisSymbol = pThisSymbol->pNext; } else { // allocate first element *pTable = malloc (sizeof (SYMBOL_TABLE_ENTRY)); pThisSymbol = *pTable; } if (!pThisSymbol) { SetLastError (ERROR_OUTOFMEMORY); bReturn = FALSE; goto CleanUp; } // allocate room for the symbol name by using the line length // - the size of "#define " // pThisSymbol->SymbolName = malloc ((strlen(lpLineBuffer) - 8) * sizeof (TCHAR)); pThisSymbol->SymbolName = malloc (DISP_BUFF_SIZE * sizeof (TCHAR)); if (!pThisSymbol->SymbolName) { SetLastError (ERROR_OUTOFMEMORY); bReturn = FALSE; goto CleanUp; } } // all the memory is allocated so load the fields pThisSymbol->pNext = NULL; iNumArgs = sscanf (lpLineBuffer, "#define %s %d", lpAnsiSymbol, &pThisSymbol->Value); if (iNumArgs != 2) { *(pThisSymbol->SymbolName) = TEXT('\0'); pThisSymbol->Value = (DWORD)-1L; bReUse = TRUE; } else { OemToChar (lpAnsiSymbol, pThisSymbol->SymbolName); bReUse = FALSE; } } } CleanUp: fclose (fIncludeFile); CleanUp2: if (lpIncludeFileName) free (lpIncludeFileName); if (lpIncludeFile) free (lpIncludeFile); if (lpLineBuffer) free (lpLineBuffer); if (lpAnsiSymbol) free (lpAnsiSymbol); return bReturn; } BOOL ParseTextId ( IN LPTSTR lpTextId, IN PSYMBOL_TABLE_ENTRY pFirstSymbol, OUT PDWORD pdwOffset, OUT LPTSTR *lpLangId, OUT PDWORD pdwType ) /*++ ParseTextId decodes Text Id key from .INI file syntax for this process is: {} {"NAME"} {}__{"HELP"} e.g. 0_009_NAME OBJECT_1_009_HELP Arguments lpTextId string to decode pFirstSymbol pointer to first entry in symbol table (NULL if no table) pdwOffset address of DWORD to recive offest value lpLangId address of pointer to Language Id string (NOTE: this will point into the string lpTextID which will be modified by this routine) pdwType pointer to dword that will recieve the type of string i.e. HELP or NAME Return Value TRUE text Id decoded successfully FALSE unable to decode string NOTE: the string in lpTextID will be modified by this procedure --*/ { LPTSTR lpThisChar; PSYMBOL_TABLE_ENTRY pThisSymbol; // check for valid return arguments if (!(pdwOffset) || !(lpLangId) || !(pdwType)) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } // search string from right to left in order to identify the // components of the string. lpThisChar = lpTextId + lstrlen(lpTextId); // point to end of string while (*lpThisChar != TEXT('_')) { lpThisChar--; if (lpThisChar <= lpTextId) { // underscore not found in string SetLastError (ERROR_INVALID_DATA); return FALSE; } } // first underscore found if ((lstrcmpi(lpThisChar, TEXT("_NAME"))) == 0) { // name found, so set type *pdwType = TYPE_NAME; } else if ((lstrcmpi(lpThisChar, TEXT("_HELP"))) == 0) { // help text found, so set type *pdwType = TYPE_HELP; } else { // bad format SetLastError (ERROR_INVALID_DATA); return FALSE; } // set the current underscore to \0 and look for language ID *lpThisChar-- = TEXT('\0'); while (*lpThisChar != TEXT('_')) { lpThisChar--; if (lpThisChar <= lpTextId) { // underscore not found in string SetLastError (ERROR_INVALID_DATA); return FALSE; } } // set lang ID string pointer to current char ('_') + 1 *lpLangId = lpThisChar + 1; // set this underscore to a NULL and try to decode the remaining text *lpThisChar = TEXT('\0'); // see if the first part of the string is a decimal digit if ((_stscanf (lpTextId, TEXT(" %d"), pdwOffset)) != 1) { // it's not a digit, so try to decode it as a symbol in the // loaded symbol table for (pThisSymbol=pFirstSymbol; pThisSymbol && *(pThisSymbol->SymbolName); pThisSymbol = pThisSymbol->pNext) { if ((lstrcmpi(lpTextId, pThisSymbol->SymbolName)) == 0) { // a matching symbol was found, so insert it's value // and return (that's all that needs to be done *pdwOffset = pThisSymbol->Value; return TRUE; } } // if here, then no matching symbol was found, and it's not // a number, so return an error SetLastError (ERROR_BAD_TOKEN_TYPE); return FALSE; } else { // symbol was prefixed with a decimal number return TRUE; } } PLANGUAGE_LIST_ELEMENT FindLanguage ( IN PLANGUAGE_LIST_ELEMENT pFirstLang, IN LPTSTR pLangId ) /*++ FindLanguage searchs the list of languages and returns a pointer to the language list entry that matches the pLangId string argument Arguments pFirstLang pointer to first language list element pLangId pointer to text string with language ID to look up Return Value Pointer to matching language list entry or NULL if no match --*/ { PLANGUAGE_LIST_ELEMENT pThisLang; for (pThisLang = pFirstLang; pThisLang; pThisLang = pThisLang->pNextLang) { if ((lstrcmpi(pLangId, pThisLang->LangId)) == 0) { // match found so return pointer return pThisLang; } } return NULL; // no match found } BOOL AddEntryToLanguage ( PLANGUAGE_LIST_ELEMENT pLang, LPTSTR lpValueKey, DWORD dwType, DWORD dwOffset, LPTSTR lpIniFile ) /*++ AddEntryToLanguage Add a text entry to the list of text entries for the specified language Arguments pLang pointer to language structure to update lpValueKey value key to look up in .ini file dwOffset numeric offset of name in registry lpIniFile ini file Return Value TRUE if added successfully FALSE if error (see GetLastError for status) --*/ { LPTSTR lpLocalStringBuff; DWORD dwSize; lpLocalStringBuff = malloc (SMALL_BUFFER_SIZE * sizeof(TCHAR)); if (!lpLocalStringBuff) { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } dwSize = GetPrivateProfileString ( TEXT("text"), // section lpValueKey, // key TEXT("DefaultValue"), // default value lpLocalStringBuff, SMALL_BUFFER_SIZE, lpIniFile); if ((lstrcmpi(lpLocalStringBuff, TEXT("DefaultValue")))== 0) { SetLastError (ERROR_BADKEY); if (lpLocalStringBuff) free (lpLocalStringBuff); return FALSE; } // key found, so load structure if (!pLang->pThisName) { // this is the first pLang->pThisName = malloc (sizeof (NAME_ENTRY) + (lstrlen(lpLocalStringBuff) + 1) * sizeof (TCHAR)); if (!pLang->pThisName) { SetLastError (ERROR_OUTOFMEMORY); if (lpLocalStringBuff) free (lpLocalStringBuff); return FALSE; } else { pLang->pFirstName = pLang->pThisName; } } else { pLang->pThisName->pNext = malloc (sizeof (NAME_ENTRY) + (lstrlen(lpLocalStringBuff) + 1) * sizeof (TCHAR)); if (!pLang->pThisName->pNext) { SetLastError (ERROR_OUTOFMEMORY); if (lpLocalStringBuff) free (lpLocalStringBuff); return FALSE; } else { pLang->pThisName = pLang->pThisName->pNext; } } // pLang->pThisName now points to an uninitialized structre pLang->pThisName->pNext = NULL; pLang->pThisName->dwOffset = dwOffset; pLang->pThisName->dwType = dwType; pLang->pThisName->lpText = (LPTSTR)&(pLang->pThisName[1]); // string follows lstrcpy (pLang->pThisName->lpText, lpLocalStringBuff); if (lpLocalStringBuff) free (lpLocalStringBuff); SetLastError (ERROR_SUCCESS); return (TRUE); } BOOL LoadLanguageLists ( IN LPTSTR lpIniFile, IN DWORD dwFirstCounter, IN DWORD dwFirstHelp, IN PSYMBOL_TABLE_ENTRY pFirstSymbol, IN PLANGUAGE_LIST_ELEMENT pFirstLang ) /*++ LoadLanguageLists Reads in the name and explain text definitions from the ini file and builds a list of these items for each of the supported languages and then combines all the entries into a sorted MULTI_SZ string buffer. Arguments lpIniFile file containing the definitions to add to the registry dwFirstCounter starting counter name index number dwFirstHelp starting help text index number pFirstLang pointer to first element in list of language elements Return Value TRUE if all is well FALSE if not error is returned in GetLastError --*/ { LPTSTR lpTextIdArray; LPTSTR lpLocalKey; LPTSTR lpThisKey; DWORD dwSize; LPTSTR lpLang; DWORD dwOffset; DWORD dwType; PLANGUAGE_LIST_ELEMENT pThisLang; if (!(lpTextIdArray = malloc (SMALL_BUFFER_SIZE * sizeof(TCHAR)))) { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } if (!(lpLocalKey = malloc (MAX_PATH))) { SetLastError (ERROR_OUTOFMEMORY); if (lpTextIdArray) free (lpTextIdArray); return FALSE; } // get list of text keys to look up dwSize = GetPrivateProfileString ( TEXT("text"), // [text] section of .INI file NULL, // return all keys TEXT("DefaultKeyValue"), // default lpTextIdArray, // return buffer SMALL_BUFFER_SIZE, // buffer size lpIniFile); // .INI file name if ((lstrcmpi(lpTextIdArray, TEXT("DefaultKeyValue"))) == 0) { // key not found, default returned SetLastError (ERROR_NO_SUCH_GROUP); if (lpTextIdArray) free (lpTextIdArray); if (lpLocalKey) free (lpLocalKey); return FALSE; } // do each key returned for (lpThisKey=lpTextIdArray; *lpThisKey; lpThisKey += (lstrlen(lpThisKey) + 1)) { lstrcpy (lpLocalKey, lpThisKey); // make a copy of the key // parse key to see if it's in the correct format if (ParseTextId(lpLocalKey, pFirstSymbol, &dwOffset, &lpLang, &dwType)) { // so get pointer to language entry structure pThisLang = FindLanguage (pFirstLang, lpLang); if (pThisLang) { if (!AddEntryToLanguage(pThisLang, lpThisKey, dwType, (dwOffset + ((dwType == TYPE_NAME) ? dwFirstCounter : dwFirstHelp)), lpIniFile)) { } } else { // language not in list } } else { // unable to parse ID string } } if (lpTextIdArray) free (lpTextIdArray); if (lpLocalKey) free (lpLocalKey); return TRUE; } BOOL SortLanguageTables ( PLANGUAGE_LIST_ELEMENT pFirstLang, PDWORD pdwLastName, PDWORD pdwLastHelp ) /*++ SortLangageTables walks list of languages loaded, allocates and loads a sorted multi_SZ buffer containing new entries to be added to current names/help text Arguments pFirstLang pointer to first element in list of languages ReturnValue TRUE everything done as expected FALSE error occurred, status in GetLastError --*/ { PLANGUAGE_LIST_ELEMENT pThisLang; BOOL bSorted; LPTSTR pNameBufPos, pHelpBufPos; PNAME_ENTRY pThisName, pPrevName; DWORD dwHelpSize, dwNameSize, dwSize; if (!pdwLastName || !pdwLastHelp) { SetLastError (ERROR_BAD_ARGUMENTS); return FALSE; } for (pThisLang = pFirstLang; pThisLang; pThisLang = pThisLang->pNextLang) { // do each language in list // sort elements in list by value (offset) so that lowest is first bSorted = FALSE; while (!bSorted ) { // point to start of list pPrevName = pThisLang->pFirstName; if (pPrevName) { pThisName = pPrevName->pNext; } else { break; // no elements in this list } if (!pThisName) { break; // only one element in the list } bSorted = TRUE; // assume that it's sorted // go until end of list while (pThisName->pNext) { if (pThisName->dwOffset > pThisName->pNext->dwOffset) { // switch 'em pPrevName->pNext = pThisName->pNext; pThisName->pNext = pThisName->pNext->pNext; pThisName->pNext->pNext = pThisName; bSorted = FALSE; } //move to next entry pPrevName = pThisName; pThisName = pThisName->pNext; } // if bSorted = TRUE , then we walked all the way down // the list without changing anything so that's the end. } // with the list sorted, build the MULTI_SZ strings for the // help and name text strings // compute buffer size dwNameSize = dwHelpSize = 0; *pdwLastName = *pdwLastHelp = 0; for (pThisName = pThisLang->pFirstName; pThisName; pThisName = pThisName->pNext) { // compute buffer requirements for this entry dwSize = SIZE_OF_OFFSET_STRING; dwSize += lstrlen (pThisName->lpText); dwSize += 1; // null dwSize *= sizeof (TCHAR); // adjust for character size // add to appropriate size register if (pThisName->dwType == TYPE_NAME) { dwNameSize += dwSize; if (pThisName->dwOffset > *pdwLastName) { *pdwLastName = pThisName->dwOffset; } } else if (pThisName->dwType == TYPE_HELP) { dwHelpSize += dwSize; if (pThisName->dwOffset > *pdwLastHelp) { *pdwLastHelp = pThisName->dwOffset; } } } // allocate buffers for the Multi_SZ strings pThisLang->NameBuffer = malloc (dwNameSize); pThisLang->HelpBuffer = malloc (dwHelpSize); if (!pThisLang->NameBuffer || !pThisLang->HelpBuffer) { SetLastError (ERROR_OUTOFMEMORY); return FALSE; } // fill in buffers with sorted strings pNameBufPos = (LPTSTR)pThisLang->NameBuffer; pHelpBufPos = (LPTSTR)pThisLang->HelpBuffer; for (pThisName = pThisLang->pFirstName; pThisName; pThisName = pThisName->pNext) { if (pThisName->dwType == TYPE_NAME) { // load number as first 0-term. string dwSize = _stprintf (pNameBufPos, TEXT("%d"), pThisName->dwOffset); pNameBufPos += dwSize + 1; // save NULL term. // load the text to match lstrcpy (pNameBufPos, pThisName->lpText); pNameBufPos += lstrlen(pNameBufPos) + 1; } else if (pThisName->dwType == TYPE_HELP) { // load number as first 0-term. string dwSize = _stprintf (pHelpBufPos, TEXT("%d"), pThisName->dwOffset); pHelpBufPos += dwSize + 1; // save NULL term. // load the text to match lstrcpy (pHelpBufPos, pThisName->lpText); pHelpBufPos += lstrlen(pHelpBufPos) + 1; } } // add additional NULL at end of string to terminate MULTI_SZ *pHelpBufPos = TEXT('\0'); *pNameBufPos = TEXT('\0'); // compute size of MULTI_SZ strings pThisLang->dwNameBuffSize = (DWORD)((PBYTE)pNameBufPos - (PBYTE)pThisLang->NameBuffer) + sizeof(TCHAR); pThisLang->dwHelpBuffSize = (DWORD)((PBYTE)pHelpBufPos - (PBYTE)pThisLang->HelpBuffer) + sizeof(TCHAR); } return TRUE; } BOOL UpdateEachLanguage ( HKEY hPerflibRoot, PLANGUAGE_LIST_ELEMENT pFirstLang ) /*++ UpdateEachLanguage Goes through list of languages and adds the sorted MULTI_SZ strings to the existing counter and explain text in the registry. Also updates the "Last Counter and Last Help" values Arguments hPerflibRoot handle to Perflib key in the registry pFirstLanguage pointer to first language entry Return Value TRUE all went as planned FALSE an error occured, use GetLastError to find out what it was. --*/ { PLANGUAGE_LIST_ELEMENT pThisLang; LPTSTR pHelpBuffer = NULL; LPTSTR pNameBuffer = NULL; LPTSTR pNewName; LPTSTR pNewHelp; DWORD dwBufferSize; DWORD dwValueType; DWORD dwCounterSize; DWORD dwHelpSize; HKEY hKeyThisLang; LONG lStatus; for (pThisLang = pFirstLang; pThisLang; pThisLang = pThisLang->pNextLang) { lStatus = RegOpenKeyEx( hPerflibRoot, pThisLang->LangId, RESERVED, KEY_READ | KEY_WRITE, &hKeyThisLang); if (lStatus == ERROR_SUCCESS) { // get size of counter names dwBufferSize = 0; lStatus = RegQueryValueEx ( hKeyThisLang, Counters, RESERVED, &dwValueType, NULL, &dwBufferSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); return FALSE; } dwCounterSize = dwBufferSize; // get size of help text dwBufferSize = 0; lStatus = RegQueryValueEx ( hKeyThisLang, Help, RESERVED, &dwValueType, NULL, &dwBufferSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); return FALSE; } dwHelpSize = dwBufferSize; // allocate new buffers dwCounterSize += pThisLang->dwNameBuffSize; pNameBuffer = malloc (dwCounterSize); dwHelpSize += pThisLang->dwHelpBuffSize; pHelpBuffer = malloc (dwHelpSize); if (!pNameBuffer || !pHelpBuffer) { if (pNameBuffer) { free(pNameBuffer); } if (pHelpBuffer) { free(pHelpBuffer); } SetLastError (ERROR_OUTOFMEMORY); return (FALSE); } // load current buffers into memory // read counter names into buffer. Counter names will be stored as // a MULTI_SZ string in the format of "###" "Name" dwBufferSize = dwCounterSize; lStatus = RegQueryValueEx ( hKeyThisLang, Counters, RESERVED, &dwValueType, (LPVOID)pNameBuffer, &dwBufferSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto ErrorExit; } // set pointer to location in buffer where new string should be // appended: end of buffer - 1 (second null at end of MULTI_SZ pNewName = (LPTSTR)((PBYTE)pNameBuffer + dwBufferSize - sizeof(TCHAR)); // adjust buffer length to take into account 2nd null from 1st // buffer that has been overwritten dwCounterSize -= sizeof(TCHAR); // read explain text into buffer. Counter names will be stored as // a MULTI_SZ string in the format of "###" "Text..." dwBufferSize = dwHelpSize; lStatus = RegQueryValueEx ( hKeyThisLang, Help, RESERVED, &dwValueType, (LPVOID)pHelpBuffer, &dwBufferSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto ErrorExit; } // set pointer to location in buffer where new string should be // appended: end of buffer - 1 (second null at end of MULTI_SZ pNewHelp = (LPTSTR)((PBYTE)pHelpBuffer + dwBufferSize - sizeof(TCHAR)); // adjust buffer length to take into account 2nd null from 1st // buffer that has been overwritten dwHelpSize -= sizeof(TCHAR); // append new strings to end of current strings memcpy (pNewHelp, pThisLang->HelpBuffer, pThisLang->dwHelpBuffSize); memcpy (pNewName, pThisLang->NameBuffer, pThisLang->dwNameBuffSize); lStatus = RegSetValueEx ( hKeyThisLang, Counters, RESERVED, REG_MULTI_SZ, (LPBYTE)pNameBuffer, dwCounterSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto ErrorExit; } lStatus = RegSetValueEx ( hKeyThisLang, Help, RESERVED, REG_MULTI_SZ, (LPBYTE)pHelpBuffer, dwHelpSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto ErrorExit; } ErrorExit: free (pNameBuffer); free (pHelpBuffer); CloseHandle (hKeyThisLang); if (lStatus != ERROR_SUCCESS) return FALSE; } else { } } return TRUE; } BOOL UpdateRegistry ( LPTSTR lpIniFile, HKEY hKeyMachine, LPTSTR lpDriverName, PLANGUAGE_LIST_ELEMENT pFirstLang, PSYMBOL_TABLE_ENTRY pFirstSymbol ) /*++ UpdateRegistry - checks, and if not busy, sets the "busy" key in the registry - Reads in the text and help definitions from the .ini file - Reads in the current contents of the HELP and COUNTER names - Builds a sorted MULTI_SZ struct containing the new definitions - Appends the new MULTI_SZ to the current as read from the registry - loads the new MULTI_SZ string into the registry - updates the keys in the driver's entry and Perflib's entry in the registry (e.g. first, last, etc) - clears the "busy" key Arguments lpIniFile pathname to .ini file conatining definitions hKeyMachine handle to HKEY_LOCAL_MACHINE in registry on system to update counters for. lpDriverName Name of device driver to load counters for pFirstLang pointer to first element in language structure list pFirstSymbol pointer to first element in symbol definition list Return Value TRUE if registry updated successfully FALSE if registry not updated (This routine will print an error message to stdout if an error is encountered). --*/ { HKEY hDriverPerf = NULL; HKEY hPerflib = NULL; LPTSTR lpDriverKeyPath; DWORD dwType; DWORD dwSize; DWORD dwFirstDriverCounter; DWORD dwFirstDriverHelp; DWORD dwLastDriverCounter; DWORD dwLastPerflibCounter; DWORD dwLastPerflibHelp; BOOL bStatus; LONG lStatus; bStatus = FALSE; // allocate temporary buffers lpDriverKeyPath = malloc (MAX_PATH * sizeof(TCHAR)); if (!lpDriverKeyPath) { SetLastError (ERROR_OUTOFMEMORY); goto UpdateRegExit; } // build driver key path string lstrcpy (lpDriverKeyPath, DriverPathRoot); lstrcat (lpDriverKeyPath, Slash); lstrcat (lpDriverKeyPath, lpDriverName); lstrcat (lpDriverKeyPath, Slash); lstrcat (lpDriverKeyPath, Performance); // open keys to registry // open key to driver's performance key lStatus = RegOpenKeyEx ( hKeyMachine, lpDriverKeyPath, RESERVED, KEY_WRITE | KEY_READ, &hDriverPerf); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto UpdateRegExit; } // open key to perflib's "root" key lStatus = RegOpenKeyEx ( hKeyMachine, NamesKey, RESERVED, KEY_WRITE | KEY_READ, &hPerflib); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto UpdateRegExit; } // get "last" values from PERFLIB dwType = 0; dwLastPerflibCounter = 0; dwSize = sizeof (dwLastPerflibCounter); lStatus = RegQueryValueEx ( hPerflib, LastCounter, RESERVED, &dwType, (LPBYTE)&dwLastPerflibCounter, &dwSize); if (lStatus != ERROR_SUCCESS) { // this request should always succeed, if not then worse things // will happen later on, so quit now and avoid the trouble. SetLastError (lStatus); goto UpdateRegExit; } // get last help value now dwType = 0; dwLastPerflibHelp = 0; dwSize = sizeof (dwLastPerflibHelp); lStatus = RegQueryValueEx ( hPerflib, LastHelp, RESERVED, &dwType, (LPBYTE)&dwLastPerflibHelp, &dwSize); if (lStatus != ERROR_SUCCESS) { // this request should always succeed, if not then worse things // will happen later on, so quit now and avoid the trouble. SetLastError (lStatus); goto UpdateRegExit; } // get last help value now dwType = 0; dwSize = sizeof (dwSystemVersion); lStatus = RegQueryValueEx ( hPerflib, VersionStr, RESERVED, &dwType, (LPBYTE)&dwSystemVersion, &dwSize); if (lStatus != ERROR_SUCCESS) { dwSystemVersion = OLD_VERSION; } if ( dwSystemVersion != OLD_VERSION ) { // ERROR. The caller does not check the version. It is the caller // fault goto UpdateRegExit; } // see if this driver's counter names have already been installed // by checking to see if LastCounter's value is less than Perflib's // Last Counter dwType = 0; dwLastDriverCounter = 0; dwSize = sizeof (dwLastDriverCounter); lStatus = RegQueryValueEx ( hDriverPerf, LastCounter, RESERVED, &dwType, (LPBYTE)&dwLastDriverCounter, &dwSize); if (lStatus == ERROR_SUCCESS) { // if key found, then compare with perflib value and exit this // procedure if the driver's last counter is <= to perflib's last // // if key not found, then continue with installation // on the assumption that the counters have not been installed if (dwLastDriverCounter <= dwLastPerflibCounter) { SetLastError (ERROR_SUCCESS); goto UpdateRegExit; } } // everything looks like it's ready to go so first check the // busy indicator lStatus = RegQueryValueEx ( hPerflib, Busy, RESERVED, &dwType, NULL, &dwSize); if (lStatus == ERROR_SUCCESS) { // perflib is in use at the moment return ERROR_BUSY; } // set the "busy" indicator under the PERFLIB key dwSize = lstrlen(lpDriverName) * sizeof (TCHAR); lStatus = RegSetValueEx ( hPerflib, Busy, RESERVED, REG_SZ, (LPBYTE)lpDriverName, dwSize); if (lStatus != ERROR_SUCCESS) { SetLastError (lStatus); goto UpdateRegExit; } // increment (by 2) the last counters so they point to the first // unused index after the existing names and then // set the first driver counters dwFirstDriverCounter = dwLastPerflibCounter += 2; dwFirstDriverHelp = dwLastPerflibHelp += 2; // load .INI file definitions into language tables if (!LoadLanguageLists (lpIniFile, dwLastPerflibCounter, dwLastPerflibHelp, pFirstSymbol, pFirstLang)) { // error message is displayed by LoadLanguageLists so just abort // error is in GetLastError already goto UpdateRegExit; } // all the symbols and definitions have been loaded into internal // tables. so now they need to be sorted and merged into a multiSZ string // this routine also updates the "last" counters if (!SortLanguageTables (pFirstLang, &dwLastPerflibCounter, &dwLastPerflibHelp)) { goto UpdateRegExit; } if (!UpdateEachLanguage (hPerflib, pFirstLang)) { goto UpdateRegExit; } // update last counters for driver and perflib // perflib... lStatus = RegSetValueEx( hPerflib, LastCounter, RESERVED, REG_DWORD, (LPBYTE)&dwLastPerflibCounter, sizeof(DWORD)); lStatus = RegSetValueEx( hPerflib, LastHelp, RESERVED, REG_DWORD, (LPBYTE)&dwLastPerflibHelp, sizeof(DWORD)); // and the driver lStatus = RegSetValueEx( hDriverPerf, LastCounter, RESERVED, REG_DWORD, (LPBYTE)&dwLastPerflibCounter, sizeof(DWORD)); lStatus = RegSetValueEx( hDriverPerf, LastHelp, RESERVED, REG_DWORD, (LPBYTE)&dwLastPerflibHelp, sizeof(DWORD)); lStatus = RegSetValueEx( hDriverPerf, FirstCounter, RESERVED, REG_DWORD, (LPBYTE)&dwFirstDriverCounter, sizeof(DWORD)); lStatus = RegSetValueEx( hDriverPerf, FirstHelp, RESERVED, REG_DWORD, (LPBYTE)&dwFirstDriverHelp, sizeof(DWORD)); bStatus = TRUE; // free temporary buffers UpdateRegExit: // clear busy flag if (hPerflib) { lStatus = RegDeleteValue ( hPerflib, Busy); } // free temporary buffers if (lpDriverKeyPath) free (lpDriverKeyPath); if (hDriverPerf) CloseHandle (hDriverPerf); if (hPerflib) CloseHandle (hPerflib); return bStatus; } BOOL FAR PASCAL lodctr(DWORD argc,LPSTR argv[], LPSTR *ppszResult ) /*++ main Arguments ReturnValue 0 (ERROR_SUCCESS) if command was processed Non-Zero if command error was detected. --*/ { LPTSTR lpIniFile; LPTSTR lpDriverName; LANGUAGE_LIST_ELEMENT LangList; PSYMBOL_TABLE_ENTRY SymbolTable = NULL; PSYMBOL_TABLE_ENTRY pThisSymbol = NULL; BOOL fReturn = TRUE; lpIniFile = malloc(MAX_PATH * sizeof(TCHAR)); lpDriverName = malloc(MAX_PATH * sizeof(TCHAR)); if ((lpIniFile == NULL) || (lpDriverName == NULL)) { if (lpIniFile) { free(lpIniFile); } if (lpDriverName) { free(lpDriverName); } SetLastError (ERROR_OUTOFMEMORY); return FALSE; } wsprintfA( achBuff, "{\"NO_ERROR\"}"); if ( argc == 1) { MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, argv[0], -1, lpIniFile, MAX_PATH); if (!GetDriverName (lpIniFile, &lpDriverName)) { wsprintfA(achBuff,"{\"ERROR\"}"); fReturn = FALSE; goto EndOfMain; } if (!BuildLanguageTables(lpIniFile, &LangList)) { wsprintfA (achBuff, "{\"ERROR\"}"); fReturn = FALSE; goto EndOfMain; } if (!LoadIncludeFile(lpIniFile, &SymbolTable)) { // open errors displayed in routine fReturn = FALSE; goto EndOfMain; } if (!UpdateRegistry(lpIniFile, HKEY_LOCAL_MACHINE, lpDriverName, &LangList, SymbolTable)) { wsprintfA (achBuff, "{\"ERROR\"}"); fReturn = FALSE; } } EndOfMain: if (lpIniFile) free (lpIniFile); if (lpDriverName) free (lpDriverName); *ppszResult = achBuff; return (fReturn); // success }