/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: strtab.c Abstract: String table functions for Windows NT Setup API dll A string table is a block of memory that contains a bunch of strings. Hashing is used, and each hash table entry points to a linked list of strings within the string table. Strings within each linked list are sorted in ascending order. A node in the linked list consists of a pointer to the next node, followed by the string itself. Nodes are manually aligned to start on DWORD boundaries so we don't have to resort to using unaligned pointers. Author: Ted Miller (tedm) Jan-11-1995 Revision History: Jamie Hunter (JamieHun) Jan-15-1997 fixed minor bug regarding use of STRTAB_NEW_EXTRADATA Jamie Hunter (JamieHun) Feb-8-2000 improved string table growth algorithm Jamie Hunter (JamieHun) Jun-27-2000 moved to sputils static library --*/ #include "precomp.h" #pragma hdrstop // // Values used for the initial and growth size // of the string table data area // // We start out with 6K, but remember that this includes the hash buckets. // After you subtract their part of the buffer, you're left with ~4K bytes. // STRING_TABLE_NEW_SIZE_ADJUST - determines approximate increase // STRING_TABLE_NEW_SIZE - will increase oldsize by at least STRING_TABLE_GROWTH_SIZE // and a multiple of STRING_TABLE_GROWTH_SIZE // if string table gets very big, we limit growth to STRING_TABLE_GROWTH_CAP bytes. // #define STRING_TABLE_INITIAL_SIZE 6144 #define STRING_TABLE_GROWTH_SIZE 2048 #define STRING_TABLE_GROWTH_CAP 0x100000 #define STRING_TABLE_NEW_SIZE_ADJUST(oldsize) ((oldsize)/3*2) #define STRING_TABLE_NEW_SIZE(oldsize) \ (oldsize+min((((DWORD)(STRING_TABLE_NEW_SIZE_ADJUST(oldsize)/STRING_TABLE_GROWTH_SIZE)+1)*STRING_TABLE_GROWTH_SIZE),STRING_TABLE_GROWTH_CAP)) // // WARNING: // // Don't change this structure, various file formats depend upon it // #include "pshpack1.h" #ifdef SPUTILSW // // name mangling so the names don't conflict with any in sputilsa.lib // #define _STRING_NODE _STRING_NODE_W #define STRING_NODE STRING_NODE_W #define PSTRING_NODE PSTRING_NODE_W #define _STRING_TABLE _STRING_TABLE_W #define STRING_TABLE STRING_TABLE_W #define PSTRING_TABLE PSTRING_TABLE_W #endif // SPUTILSW typedef struct _STRING_NODE { // // This is stored as an offset instead of a pointer // because the table can move as it's built // The offset is from the beginning of the table // LONG NextOffset; // // This field must be last // TCHAR String[ANYSIZE_ARRAY]; } STRING_NODE, *PSTRING_NODE; #include "poppack.h" // // in-memory details about the string table // typedef struct _STRING_TABLE { PUCHAR Data; // First HASH_BUCKET_COUNT DWORDS are StringNodeOffset array. DWORD DataSize; DWORD BufferSize; MYLOCK Lock; UINT ExtraDataSize; LCID Locale; } STRING_TABLE, *PSTRING_TABLE; #define LockTable(table) BeginSynchronizedAccess(&((table)->Lock)) #define UnlockTable(table) EndSynchronizedAccess(&((table)->Lock)) #ifdef UNICODE #define FixedCompareString CompareString #else #include #include static INT FixedCompareString ( IN LCID Locale, IN DWORD Flags, IN PCSTR FirstString, IN INT Count1, IN PCSTR SecondString, IN INT Count2 ) { LCID OldLocale; INT Result = 0; // // This routine uses the C runtime to compare the strings, because // the Win32 APIs are broken on some versions of Win95 // OldLocale = GetThreadLocale(); if (OldLocale != Locale) { SetThreadLocale (Locale); setlocale(LC_ALL,""); } __try { if (Count1 == -1) { Count1 = strlen (FirstString); } if (Count2 == -1) { Count2 = strlen (SecondString); } // // The C runtime compares strings differently than the CompareString // API. Most importantly, the C runtime considers uppercase to be // less than lowercase; the CompareString API is the opposite. // if (Flags & NORM_IGNORECASE) { Result = _mbsnbicmp (FirstString, SecondString, min (Count1, Count2)); } else { Result = _mbsnbcmp (FirstString, SecondString, min (Count1, Count2)); } // // We now convert the C runtime result into the CompareString result. // This means making the comparison a Z to A ordering, with lowercase // coming before uppercase. The length comparison does not get reversed. // if(Result == _NLSCMPERROR) { Result = 0; // zero returned if _mbsnbicmp could not compare } else if (Result < 0) { Result = CSTR_GREATER_THAN; } else if (Result == 0) { if (Count1 < Count2) { Result = CSTR_LESS_THAN; // first string shorter than second } else if (Count1 > Count2) { Result = CSTR_GREATER_THAN; // first string longer than second } else { Result = CSTR_EQUAL; } } else { Result = CSTR_LESS_THAN; } } __except (TRUE) { Result = 0; } if (OldLocale != Locale) { SetThreadLocale (OldLocale); setlocale(LC_ALL,""); } return Result; } #endif static DWORD _StringTableCheckFlags( IN DWORD FlagsIn ) /*++ Routine Description: Pre-process flags, called by exported routines we want to handle the combination of CASE_INSENSITIVE, CASE_SENSITIVE and BUFFER_WRITEABLE and keep all other flags as is. Arguments: FlagsIn - flags as supplied Return Value: Flags out --*/ { DWORD FlagsOut; DWORD FlagsSpecial; // // we're just interested in these flags for the switch // FlagsSpecial = FlagsIn & (STRTAB_CASE_SENSITIVE | STRTAB_BUFFER_WRITEABLE); // // strip these off FlagsIn to create initial FlagsOut // FlagsOut = FlagsIn ^ FlagsSpecial; switch (FlagsSpecial) { case STRTAB_CASE_INSENSITIVE : case STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE : // // these cases ok // FlagsOut |= FlagsSpecial; break; default : // // any other combination is treated as STRTAB_CASE_SENSITIVE (and so // WRITEABLE doesn't matter) // FlagsOut |= STRTAB_CASE_SENSITIVE; } return FlagsOut; } static VOID _ComputeHashValue( IN PTSTR String, OUT PDWORD StringLength, IN DWORD Flags, OUT PDWORD HashValue ) /*++ Routine Description: Compute a hash value for a given string. The algorithm simply adds up the unicode values for each character in the string and then takes the result mod the number of hash buckets. Arguments: String - supplies the string for which a hash value is desired. StringLength - receives the number of characters in the string, not including the terminating nul. Flags - supplies flags controlling how the hashing is to be done. May be a combination of the following values (all other bits ignored): STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string look-up. Specifying this flag improves the performance of this API for case-insensitive string additions. This flag is ignored for case-sensitive string additions. STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. HashValue - receives the hash value. Return Value: None. --*/ { DWORD Length; DWORD Value = 0; PCTSTR p, q; DWORD Char; try { if((Flags & (STRTAB_BUFFER_WRITEABLE | STRTAB_ALREADY_LOWERCASE)) == STRTAB_BUFFER_WRITEABLE) { // // Then the buffer is writeable, but isn't yet lower-case. Take care of that right now. // #ifndef UNICODE _mbslwr (String); #else CharLower(String); #endif Flags |= STRTAB_ALREADY_LOWERCASE; } // // Define a macro to ensure we don't get sign-extension when adding up character values. // #ifdef UNICODE #define DWORD_FROM_TCHAR(x) ((DWORD)((WCHAR)(x))) #else #define DWORD_FROM_TCHAR(x) ((DWORD)((UCHAR)(x))) #endif p = String; if(Flags & STRTAB_ALREADY_LOWERCASE) { while (*p) { Value += DWORD_FROM_TCHAR (*p); p++; } } else { // // Make sure we don't get sign-extension on extended chars // in String -- otherwise we get values like 0xffffffe4 passed // to CharLower(), which thinks it's a pointer and faults. // #ifdef UNICODE // // The WCHAR case is trivial // while (*p) { Value += DWORD_FROM_TCHAR(CharLower((PWSTR)(WORD) (*p))); p++; } #else // // The DBCS case is a mess because of the possibility of CharLower // altering a two-byte character // Standardize to use _mbslwr as that is used elsewhere // ie, if we did _mbslwr, & called this function with // flag set to say "already lower", vs we called function // with buffer writable, vs calling with neither // we should ensure we get same hash in each case // it may fail, but at least it will fail *universally* and // generate the same hash // PTSTR copy = pSetupDuplicateString(String); if(copy) { // // do conversion on copied string // _mbslwr(copy); p = copy; while (*p) { Value += DWORD_FROM_TCHAR (*p); p++; } pSetupFree(copy); p = String+lstrlen(String); } else { // // we had a memory failure // *HashValue = 0; *StringLength = 0; leave; } #endif } *HashValue = Value % HASH_BUCKET_COUNT; *StringLength = (DWORD)(p - String); } except(EXCEPTION_EXECUTE_HANDLER) { // // Inbound string was bogus // *HashValue = 0; *StringLength = 0; MYASSERT(FALSE); } } BOOL _pSpUtilsStringTableLock( IN PVOID StringTable ) /*++ Routine Description: This routine acquires the lock for the specified string table (it's implemented as a function call for uses in setupapi where locking needs to be explicitly controlled). Arguments: StringTable - supplies handle to string table to be locked. Return Value: If the lock was successfully acquired, the return value is TRUE. Otherwise, the return value is FALSE. --*/ { return LockTable((PSTRING_TABLE)StringTable); } VOID _pSpUtilsStringTableUnlock( IN PVOID StringTable ) /*++ Routine Description: This routine releases the lock (previously acquired via _pSpUtilsStringTableLock) for the specified string table. Arguments: StringTable - supplies handle to string table to be unlocked. Return Value: None. --*/ { UnlockTable((PSTRING_TABLE)StringTable); } LONG _pSpUtilsStringTableLookUpString( IN PVOID StringTable, IN OUT PTSTR String, OUT PDWORD StringLength, OUT PDWORD HashValue, OPTIONAL OUT PVOID *FindContext, OPTIONAL IN DWORD Flags, OUT PVOID ExtraData, OPTIONAL IN UINT ExtraDataBufferSize OPTIONAL ) /*++ Routine Description: Locates a string in the string table, if present. If the string is not present, this routine may optionally tell its caller where the search stopped. This is useful for maintaining a sorted order for the strings. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be looked up StringLength - receives number of characters in the string, not including the terminating nul. HashValue - Optionally, receives hash value for the string. FindContext - Optionally, receives the context at which the search was terminated. (NOTE: This is actually a PSTRING_NODE pointer, that is used during new string addition. Since this routine has wider exposure than just internal string table usage, this parameter is made into a PVOID, so no one else has to have access to string table-internal structures. On return, this variable receives a pointer to the string node of the node where the search stopped. If the string was found, then this is a pointer to the string's node. If the string was not found, then this is a pointer to the last string node whose string is 'less' (based on lstrcmpi) than the string we're looking for. Note that this value may be NULL.) Flags - supplies flags controlling how the string is to be located. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string look-up. Specifying this flag improves the performance of this API for case-insensitive string additions. This flag is ignored for case-sensitive string additions. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. ExtraData - if specified, receives extra data associated with the string if the string is found. ExtraDataBufferSize - if ExtraData is specified, then this parameter specifies the size of the buffer, in bytes. As much extra data as will fit is stored here. Return Value: The return value is a value that uniquely identifies the string within the string table, namely the offset of the string node within the string table. If the string could not be found the value is -1. --*/ { PSTRING_NODE node,prev; int i; PSTRING_TABLE stringTable = StringTable; DWORD hashValue; PSTRING_NODE FinalNode; LONG rc = -1; LCID Locale; DWORD CompareFlags; BOOL CollateEnded = FALSE; // // If this is a case-sensitive lookup, then we want to reset the STRTAB_BUFFER_WRITEABLE // flag, if present, since otherwise the string will get replaced with its all-lowercase // counterpart. // if(Flags & STRTAB_CASE_SENSITIVE) { Flags &= ~STRTAB_BUFFER_WRITEABLE; } // // Compute hash value // _ComputeHashValue(String,StringLength,Flags,&hashValue); if(((PLONG)(stringTable->Data))[hashValue] == -1) { // // The string table contains no strings at the computed hash value. // FinalNode = NULL; goto clean0; } // // We know there's at least one string in the table with the computed // hash value, so go find it. There's no previous node yet. // node = (PSTRING_NODE)(stringTable->Data + ((PLONG)(stringTable->Data))[hashValue]); prev = NULL; // // Go looking through the string nodes for that hash value, // looking through the string. // Locale = stringTable->Locale; CompareFlags = (Flags & STRTAB_CASE_SENSITIVE) ? 0 : NORM_IGNORECASE; while(1) { if(i = FixedCompareString(Locale,CompareFlags,String,-1,node->String,-1)) { i -= 2; } else { // // Failure, try system default locale // if(i = FixedCompareString(LOCALE_SYSTEM_DEFAULT,CompareFlags,String,-1,node->String,-1)) { i -= 2; } else { // // Failure, just use CRTs // // This could give wrong collation?? If it does, we're stuck with it now. // i = (Flags & STRTAB_CASE_SENSITIVE) ? _tcscmp(String,node->String) : _tcsicmp(String,node->String); } } if(i == 0) { FinalNode = node; rc = (LONG)((PUCHAR)node - stringTable->Data); break; } // // If the string we are looking for is 'less' than the current // string, mark it's position so we can insert a new string before here // (ANSI) but keep searching (UNICODE) we can abort - old behaviour // if((i < 0) && !CollateEnded) { CollateEnded = TRUE; FinalNode = prev; #if UNICODE break; #endif } // // The string we are looking for is 'greater' than the current string. // Keep looking, unless we've reached the end of the table. // if(node->NextOffset == -1) { if(!CollateEnded) { // // unless we found a more ideal position // return the end of the list // FinalNode = node; } break; } else { prev = node; node = (PSTRING_NODE)(stringTable->Data + node->NextOffset); } } clean0: if((rc != -1) && ExtraData) { // // Extra data is stored immediately following the string. // CopyMemory( ExtraData, FinalNode->String + *StringLength + 1, min(ExtraDataBufferSize,stringTable->ExtraDataSize) ); } if(HashValue) { *HashValue = hashValue; } if(FindContext) { *FindContext = FinalNode; } return rc; } LONG pSetupStringTableLookUpString( IN PVOID StringTable, IN OUT PTSTR String, IN DWORD Flags ) /*++ Routine Description: Locates a string in the string table, if present. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is specified and a case-insensitive lookup is requested, then this buffer will be all lower-case upon return. Flags - supplies flags controlling how the string is to be located. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string look-up. Specifying this flag improves the performance of this API for case-insensitive string additions. This flag is ignored for case-sensitive string additions. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. Return Value: The return value is a value that uniquely identifies the string within the string table. If the string could not be found the value is -1. --*/ { DWORD StringLength, PrivateFlags, AlreadyLcFlag; LONG rc = -1; BOOL locked = FALSE; try { if (!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; PrivateFlags = _StringTableCheckFlags(Flags); rc = _pSpUtilsStringTableLookUpString( StringTable, String, &StringLength, NULL, NULL, PrivateFlags, NULL, 0 ); } except (EXCEPTION_EXECUTE_HANDLER) { rc = -1; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return (rc); } LONG pSetupStringTableLookUpStringEx( IN PVOID StringTable, IN OUT PTSTR String, IN DWORD Flags, OUT PVOID ExtraData, OPTIONAL IN UINT ExtraDataBufferSize OPTIONAL ) /*++ Routine Description: Locates a string in the string table, if present. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be looked up. If STRTAB_BUFFER_WRITEABLE is specified and a case-insensitive lookup is requested, then this buffer will be all lower-case upon return. Flags - supplies flags controlling how the string is to be located. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Search for the string case-insensitively. STRTAB_CASE_SENSITIVE - Search for the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string look-up. Specifying this flag improves the performance of this API for case-insensitive string additions. This flag is ignored for case-sensitive string additions. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. ExtraData - if specified, receives extra data associated with the string if the string is found. ExtraDataBufferSize - if ExtraData is specified, then this parameter specifies the size of the buffer, in bytes. As much extra data as will fit is stored here. Return Value: The return value is a value that uniquely identifies the string within the string table. If the string could not be found the value is -1. --*/ { DWORD StringLength, PrivateFlags, AlreadyLcFlag; LONG rc = -1; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; PrivateFlags = _StringTableCheckFlags(Flags); rc = _pSpUtilsStringTableLookUpString( StringTable, String, &StringLength, NULL, NULL, PrivateFlags, ExtraData, ExtraDataBufferSize ); } except (EXCEPTION_EXECUTE_HANDLER) { rc = -1; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return (rc); } BOOL pSetupStringTableGetExtraData( IN PVOID StringTable, IN LONG StringId, OUT PVOID ExtraData, IN UINT ExtraDataBufferSize ) /*++ Routine Description: Get arbitrary data associated with a string table entry. Arguments: StringTable - supplies handle to string table containing the string whose associated data is to be returned. String - supplies the id of the string whose associated data is to be returned. ExtraData - receives the data associated with the string. Data is truncated to fit, if necessary. ExtraDataBufferSize - supplies the size in bytes of the buffer specified by ExtraData. If this value is smaller than the extra data size for the string table, data is truncated to fit. Return Value: Boolean value indicating outcome. --*/ { BOOL b = FALSE; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; b = _pSpUtilsStringTableGetExtraData(StringTable,StringId,ExtraData,ExtraDataBufferSize); } except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return(b); } BOOL _pSpUtilsStringTableGetExtraData( IN PVOID StringTable, IN LONG StringId, OUT PVOID ExtraData, IN UINT ExtraDataBufferSize ) /*++ Routine Description: Get arbitrary data associated with a string table entry. THIS ROUTINE DOES NOT DO LOCKING and IT DOES NOT HANDLE EXCEPTIONS! Arguments: StringTable - supplies handle to string table containing the string whose associated data is to be returned. String - supplies the id of the string whose associated data is to be returned. ExtraData - receives the data associated with the string. Data is truncated to fit, if necessary. ExtraDataBufferSize - supplies the size in bytes of the buffer specified by ExtraData. If this value is smaller than the extra data size for the string table, data is truncated to fit. Return Value: Boolean value indicating outcome. --*/ { PSTRING_TABLE stringTable = StringTable; PSTRING_NODE stringNode; PVOID p; stringNode = (PSTRING_NODE)(stringTable->Data + StringId); p = stringNode->String + lstrlen(stringNode->String) + 1; CopyMemory(ExtraData,p,min(ExtraDataBufferSize,stringTable->ExtraDataSize)); return(TRUE); } BOOL pSetupStringTableSetExtraData( IN PVOID StringTable, IN LONG StringId, IN PVOID ExtraData, IN UINT ExtraDataSize ) /*++ Routine Description: Associate arbitrary data with a string table entry. Arguments: StringTable - supplies handle to string table containing the string with which the data is to be associated. String - supplies the id of the string with which the data is to be associated. ExtraData - supplies the data to be associated with the string. ExtraDataSize - specifies the size in bytes of the data. If the data is larger than the extra data size for this string table, then the routine fails. Return Value: Boolean value indicating outcome. --*/ { BOOL b = FALSE; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; b = _pSpUtilsStringTableSetExtraData(StringTable,StringId,ExtraData,ExtraDataSize); } except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return(b); } BOOL _pSpUtilsStringTableSetExtraData( IN PVOID StringTable, IN LONG StringId, IN PVOID ExtraData, IN UINT ExtraDataSize ) /*++ Routine Description: Associate arbitrary data with a string table entry. Arguments: StringTable - supplies handle to string table containing the string with which the data is to be associated. String - supplies the id of the string with which the data is to be associated. ExtraData - supplies the data to be associated with the string. ExtraDataSize - specifies the size in bytes of the data. If the data is larger than the extra data size for this string table, then the routine fails. Return Value: Boolean value indicating outcome. --*/ { PSTRING_TABLE stringTable = StringTable; PSTRING_NODE stringNode; BOOL b; PVOID p; if(ExtraDataSize <= stringTable->ExtraDataSize) { stringNode = (PSTRING_NODE)(stringTable->Data + StringId); p = stringNode->String + lstrlen(stringNode->String) + 1; ZeroMemory(p,stringTable->ExtraDataSize); CopyMemory(p,ExtraData,ExtraDataSize); b = TRUE; } else { b = FALSE; } return(b); } LONG _pSpUtilsStringTableAddString( IN PVOID StringTable, IN OUT PTSTR String, IN DWORD Flags, IN PVOID ExtraData, OPTIONAL IN UINT ExtraDataSize OPTIONAL ) /*++ Routine Description: Adds a string to the string table if the string is not already in the string table. (Does not do locking!) If the string is to be added case-insensitively, then it is lower-cased, and added case-sensitively. Since lower-case characters are 'less than' lower case ones (according to lstrcmp), this ensures that a case-insensitive string will always appear in front of any of its case-sensitive counterparts. This ensures that we always find the correct string ID for things like section names. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be added Flags - supplies flags controlling how the string is to be added, and whether the caller-supplied buffer may be modified. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The specified string will be added to the string table as all lower-case. This flag is overridden if STRTAB_CASE_SENSITIVE is specified. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string-addition process. Specifying this flag improves the performance of this API for case- insensitive string additions. This flag is ignored for case-sensitive string additions. STRTAB_NEW_EXTRADATA - if the string already exists in the table and ExtraData is specified (see below) then the new ExtraData overwrites any existing extra data. Otherwise any existing extra data is left alone. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. ExtraData - if supplied, specifies extra data to be associated with the string in the string table. If the string already exists in the table, the Flags field controls whether the new data overwrites existing data already associated with the string. ExtraDataSize - if ExtraData is supplied, then this value supplies the size in bytes of the buffer pointed to by ExtraData. If the data is larger than the extra data size for the string table, the routine fails. Return Value: The return value uniquely identifes the string within the string table. It is -1 if the string was not in the string table but could not be added (out of memory). --*/ { LONG rc; PSTRING_TABLE stringTable = StringTable; DWORD StringLength; DWORD HashValue; PSTRING_NODE PreviousNode,NewNode; DWORD SpaceRequired; PTSTR TempString = String; BOOL FreeTempString = FALSE; PVOID p; DWORD sz; if (!(Flags & STRTAB_CASE_SENSITIVE)) { // // not case sensitive ( = insensitive) // if (!(Flags & STRTAB_ALREADY_LOWERCASE)) { // // not already lowercase // if (!(Flags & STRTAB_BUFFER_WRITEABLE)) { // // not writable // // // Then the string is to be added case-insensitively, but the caller // doesn't want us to write to their buffer. Allocate one of our own. // if (TempString = pSetupDuplicateString(String)) { FreeTempString = TRUE; } else { // // We couldn't allocate space for our duplicated string. Since we'll // only consider exact matches (where the strings are all lower-case), // we're stuck, since we can't lower-case the buffer in place. // return -1; } } // // Lower-case the buffer. // #ifndef UNICODE _mbslwr (TempString); #else CharLower(TempString); #endif } // // we know that the string is now lower-case // we no longer need "Writable" flag // searches will be case sensitive // Flags &= ~ (STRTAB_BUFFER_WRITEABLE | STRTAB_CASE_INSENSITIVE); Flags |= STRTAB_CASE_SENSITIVE | STRTAB_ALREADY_LOWERCASE; } try { if (ExtraData && (ExtraDataSize > stringTable->ExtraDataSize)) { // // Force us into the exception handler -- sort of a non-local goto. // RaiseException(0,0,0,NULL); } // // The string might already be in there. // rc = _pSpUtilsStringTableLookUpString( StringTable, TempString, &StringLength, &HashValue, &PreviousNode, Flags, NULL, 0 ); if (rc != -1) { if (ExtraData && (Flags & STRTAB_NEW_EXTRADATA)) { // // Overwrite extra data. We know the data is small enough to fit // because we checked for this above. // p = PreviousNode->String + StringLength + 1; ZeroMemory(p,stringTable->ExtraDataSize); CopyMemory(p,ExtraData,ExtraDataSize); } if (FreeTempString) { pSetupFree(TempString); } return (rc); } // // Figure out how much space is required to hold this entry. // This is the size of a STRING_NODE plus the length of the string // plus space for extra per-element data. // SpaceRequired = offsetof(STRING_NODE,String) + ((StringLength+1)*sizeof(TCHAR)) + stringTable->ExtraDataSize; // // Make sure things stay aligned within the table // if (SpaceRequired % sizeof(DWORD)) { SpaceRequired += sizeof(DWORD) - (SpaceRequired % sizeof(DWORD)); } while(stringTable->DataSize + SpaceRequired > stringTable->BufferSize) { // // Grow the string table. // do this exponentially so that tables with a lot of items // added won't cause lots of reallocs // sz = STRING_TABLE_NEW_SIZE(stringTable->BufferSize); if (sz < stringTable->BufferSize) { sz = stringTable->DataSize + SpaceRequired; } p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA); if (!p) { // // we've run out of room, this could be because we asked // for too big of a re-alloc // if we're in this state, we're probably going to // have problems later anyway, but for now, let's // try and proceed with exactly what we need // sz = stringTable->DataSize + SpaceRequired; p = pSetupReallocWithTag(stringTable->Data,sz,MEMTAG_STRINGDATA); if (!p) { // // nope, this didn't help // if (FreeTempString) { pSetupFree(TempString); } return (-1); } } // // Adjust previous node pointer. // if (PreviousNode) { PreviousNode = (PSTRING_NODE)((PUCHAR)p + ((PUCHAR)PreviousNode-(PUCHAR)stringTable->Data)); } stringTable->Data = p; stringTable->BufferSize = sz; } // // Stick the string and extra data, if any, in the string table buffer. // NewNode = (PSTRING_NODE)(stringTable->Data + stringTable->DataSize); if (PreviousNode) { NewNode->NextOffset = PreviousNode->NextOffset; PreviousNode->NextOffset = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data); } else { NewNode->NextOffset = ((PLONG)(stringTable->Data))[HashValue]; ((PLONG)(stringTable->Data))[HashValue] = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data); } lstrcpy(NewNode->String,TempString); p = NewNode->String + StringLength + 1; ZeroMemory(p,stringTable->ExtraDataSize); if (ExtraData) { CopyMemory(p,ExtraData,ExtraDataSize); } stringTable->DataSize += SpaceRequired; rc = (LONG)((LONG_PTR)NewNode - (LONG_PTR)stringTable->Data); }except(EXCEPTION_EXECUTE_HANDLER) { rc = -1; } if (FreeTempString) { pSetupFree(TempString); } return rc; } LONG pSetupStringTableAddString( IN PVOID StringTable, IN PTSTR String, IN DWORD Flags ) /*++ Routine Description: Adds a string to the string table if the string is not already in the string table. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be added Flags - supplies flags controlling how the string is to be added, and whether the caller-supplied buffer may be modified. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The specified string will be added to the string table as all lower-case. This flag is overridden if STRTAB_CASE_SENSITIVE is specified. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string-addition process. Specifying this flag improves the performance of this API for case- insensitive string additions. This flag is ignored for case-sensitive string additions. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. Return Value: The return value uniquely identifes the string within the string table. It is -1 if the string was not in the string table but could not be added (out of memory). --*/ { LONG rc = -1; BOOL locked = FALSE; DWORD PrivateFlags; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; PrivateFlags = _StringTableCheckFlags(Flags); rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, NULL, 0); } except (EXCEPTION_EXECUTE_HANDLER) { rc = -1; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return(rc); } LONG pSetupStringTableAddStringEx( IN PVOID StringTable, IN PTSTR String, IN DWORD Flags, IN PVOID ExtraData, OPTIONAL IN UINT ExtraDataSize OPTIONAL ) /*++ Routine Description: Adds a string to the string table if the string is not already in the string table. Arguments: StringTable - supplies handle to string table to be searched for the string String - supplies the string to be added Flags - supplies flags controlling how the string is to be added, and whether the caller-supplied buffer may be modified. May be a combination of the following values: STRTAB_CASE_INSENSITIVE - Add the string case-insensitively. The specified string will be added to the string table as all lower-case. This flag is overridden if STRTAB_CASE_SENSITIVE is specified. STRTAB_CASE_SENSITIVE - Add the string case-sensitively. This flag overrides the STRTAB_CASE_INSENSITIVE flag. STRTAB_BUFFER_WRITEABLE - The caller-supplied buffer may be written to during the string-addition process. Specifying this flag improves the performance of this API for case- insensitive string additions. This flag is ignored for case-sensitive string additions. STRTAB_NEW_EXTRADATA - If the string already exists in the table and ExtraData is specified (see below) then the new ExtraData overwrites any existing extra data. Otherwise any existing extra data is left alone. In addition to the above public flags, the following private flag is also allowed: STRTAB_ALREADY_LOWERCASE - The supplied string has already been converted to all lower-case (e.g., by calling CharLower), and therefore doesn't need to be lower-cased in the hashing routine. If this flag is supplied, then STRTAB_BUFFER_WRITEABLE is ignored, since modifying the caller's buffer is not required. ExtraData - if supplied, specifies extra data to be associated with the string in the string table. If the string already exists in the table, the Flags field controls whether the new data overwrites existing data already associated with the string. ExtraDataSize - if ExtraData is supplied, then this value supplies the size in bytes of the buffer pointed to by ExtraData. If the data is larger than the extra data size for the string table, the routine fails. Return Value: The return value uniquely identifes the string within the string table. It is -1 if the string was not in the string table but could not be added (out of memory). --*/ { LONG rc = -1; BOOL locked = FALSE; DWORD PrivateFlags; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; PrivateFlags = _StringTableCheckFlags(Flags); rc = _pSpUtilsStringTableAddString(StringTable, String, PrivateFlags, ExtraData, ExtraDataSize); } except (EXCEPTION_EXECUTE_HANDLER) { rc = -1; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return (rc); } BOOL pSetupStringTableEnum( IN PVOID StringTable, OUT PVOID ExtraDataBuffer, OPTIONAL IN UINT ExtraDataBufferSize, OPTIONAL IN PSTRTAB_ENUM_ROUTINE Callback, IN LPARAM lParam OPTIONAL ) /*++ Routine Description: For every string in a string table, inform a callback routine of the stirng's id, it's value, and any associated data. Arguments: StringTable - supplies a pointer to the string table to be enumerated. ExtraDataBuffer - supplies the address of a buffer to be passed to the callback routine for each string, which will be filled in with the associated data of each string. ExtraDataBufferSize - if ExtraDataBuffer is specified then this supplies the size of that buffer in bytes. If this value is smaller than the size of the extra data for the string table, the enumeration fails. Callback - supplies the routine to be notified of each string. lParam - supplies an optional parameter meaningful to the caller which is passed on to the callback unchanged. Return Value: Boolean value indicating outcome. TRUE unless ExtraDataBufferSize is too small. --*/ { BOOL b = FALSE; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; b = _pSpUtilsStringTableEnum(StringTable,ExtraDataBuffer,ExtraDataBufferSize,Callback,lParam); } except (EXCEPTION_EXECUTE_HANDLER) { b = FALSE; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return(b); } BOOL _pSpUtilsStringTableEnum( IN PVOID StringTable, OUT PVOID ExtraDataBuffer, OPTIONAL IN UINT ExtraDataBufferSize, OPTIONAL IN PSTRTAB_ENUM_ROUTINE Callback, IN LPARAM lParam OPTIONAL ) /*++ Routine Description: For every string in a string table, inform a callback routine of the stirng's id, its value, and any associated data. THIS ROUTINE DOES NOT DO LOCKING. Arguments: StringTable - supplies a pointer to the string table to be enumerated. ExtraDataBuffer - supplies the address of a buffer to be passed to the callback routine for each string, which will be filled in with the associated data of each string. ExtraDataBufferSize - if ExtraDataBuffer is specified then this supplies the size of that buffer in bytes. If this value is smaller than the size of the extra data for the string table, the enumeration fails. Callback - supplies the routine to be notified of each string. lParam - supplies an optional parameter meaningful to the caller which is passed on to the callback unchanged. Return Value: Boolean value indicating outcome. TRUE unless ExtraDataBufferSize is too small. --*/ { UINT u; PSTRING_TABLE stringTable = StringTable; PSTRING_NODE stringNode; LONG FirstOffset; BOOL b; // // Validate buffer size. // if(ExtraDataBuffer && (ExtraDataBufferSize < stringTable->ExtraDataSize)) { return(FALSE); } for(b=TRUE,u=0; b && (uData)[u]; if(FirstOffset == -1) { continue; } stringNode = (PSTRING_NODE)(stringTable->Data + FirstOffset); do { if(ExtraDataBuffer) { CopyMemory( ExtraDataBuffer, stringNode->String + lstrlen(stringNode->String) + 1, stringTable->ExtraDataSize ); } b = Callback( StringTable, (LONG)((PUCHAR)stringNode - stringTable->Data), stringNode->String, ExtraDataBuffer, ExtraDataBuffer ? stringTable->ExtraDataSize : 0, lParam ); stringNode = (stringNode->NextOffset == -1) ? NULL : (PSTRING_NODE)(stringTable->Data + stringNode->NextOffset); } while(b && stringNode); } return(TRUE); } PTSTR _pSpUtilsStringTableStringFromId( IN PVOID StringTable, IN LONG StringId ) /*++ Routine Description: Given a string ID returned when a string was added or looked up, return a pointer to the actual string. (This is exactly the same as pSetupStringTableStringFromId, except that it doesn't do locking.) Arguments: StringTable - supplies a pointer to the string table containing the string to be retrieved. StringId - supplies a string id returned from pSetupStringTableAddString or pSetupStringTableLookUpString. Return Value: Pointer to string data. The caller must not write into or otherwise alter the string. --*/ { return ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String; } PTSTR pSetupStringTableStringFromId( IN PVOID StringTable, IN LONG StringId ) /*++ Routine Description: Given a string ID returned when a string was added or looked up, return a pointer to the actual string. Arguments: StringTable - supplies a pointer to the string table containing the string to be retrieved. StringId - supplies a string id returned from pSetupStringTableAddString or pSetupStringTableLookUpString. Return Value: Pointer to string data. The caller must not write into or otherwise alter the string. This function is fundamentally not thread-safe since the ptr we return could be modified by another thread if string table is accessed by more than one thread Hence new API below pSetupStringTableStringFromIdEx --*/ { PTSTR p = NULL; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; p = ((PSTRING_NODE)(((PSTRING_TABLE)StringTable)->Data + StringId))->String; } except (EXCEPTION_EXECUTE_HANDLER) { p = NULL; // // Reference the following variable so the compiler will respect // statement ordering w.r.t. its assignment. // locked = locked; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return(p); } BOOL pSetupStringTableStringFromIdEx( IN PVOID StringTable, IN LONG StringId, IN OUT PTSTR pBuffer, IN OUT PULONG pBufSize ) /*++ Routine Description: Given a string ID returned when a string was added or looked up, return a pointer to the actual string. Arguments: StringTable - supplies a pointer to the string table containing the string to be retrieved. StringId - supplies a string id returned from pSetupStringTableAddString or pSetupStringTableLookUpString. pBuffer - points to a buffer that will be filled out with the string to be retrieved pBufSize - supplies a pointer to an input/output parameter that contains the size of the buffer on entry, and the number of chars. written on exit. Return Value: TRUE if the string was written. pBufSize contains the length of the string FALSE if the buffer was invalid, or the string ID was invalid, or the buffer wasn't big enough. If pBufSize non-zero, then it is the required bufsize. currently caller can tell the difference between invalid param and buffer size by checking pBufSize --*/ { PTSTR p; ULONG len; PSTRING_TABLE stringTable = (PSTRING_TABLE)StringTable; DWORD status = ERROR_INVALID_DATA; BOOL locked = FALSE; try { if (!pBufSize) { status = ERROR_INVALID_PARAMETER; leave; } if(!LockTable(stringTable)) { if (pBuffer != NULL && *pBufSize > 0) { pBuffer[0]=0; } *pBufSize = 0; status = ERROR_INVALID_HANDLE; leave; } locked = TRUE; // // CFGMGR calls this with an ID passed by it's caller // we have to check bounds here (while table is locked) // the check has to do: // // (1) StringId must be > 0 (0 is hash-bucket 0) // (2) StringId must be < size of string table // This should catch common errors but isn't perfect. // // the check is here since Id validity requires access to Opaque pointer // if(StringId <= 0 || StringId >= (LONG)(stringTable->DataSize)) { if (pBuffer != NULL && *pBufSize > 0) { pBuffer[0]=0; } *pBufSize = 0; status = ERROR_INVALID_PARAMETER; leave; } len = lstrlen( ((PSTRING_NODE)(stringTable->Data + StringId))->String); len ++; // account for terminating NULL if (len > *pBufSize || pBuffer == NULL) { if (pBuffer != NULL && *pBufSize > 0) { pBuffer[0]=0; } *pBufSize = len; status = ERROR_INSUFFICIENT_BUFFER; leave; } lstrcpy (pBuffer,((PSTRING_NODE)(stringTable->Data + StringId))->String); *pBufSize = len; status = NO_ERROR; } except (EXCEPTION_EXECUTE_HANDLER) { status = ERROR_INVALID_DATA; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } if(status == NO_ERROR) { // // if success, return TRUE without modifying error code // return TRUE; } // // if error, we may be interested in cause // // SetLastError(status); // left disabled till I know this is safe to do return FALSE; } VOID _pSpUtilsStringTableTrim( IN PVOID StringTable ) /*++ Routine Description: Free any memory currently allocated for the string table but not currently used. This is useful after all strings have been added to a string table because the string table grows by a fixed block size as it's being built. THIS ROUTINE DOES NOT DO LOCKING! Arguments: StringTable - supplies a string table handle returned from a call to pSetupStringTableInitialize(). Return Value: None. --*/ { PSTRING_TABLE stringTable = StringTable; PVOID p; // // If the realloc failed the original block is not freed, // so we don't really care. // if(p = pSetupReallocWithTag(stringTable->Data, stringTable->DataSize, MEMTAG_STRINGDATA)) { stringTable->Data = p; stringTable->BufferSize = stringTable->DataSize; } } PVOID pSetupStringTableInitialize( VOID ) /*++ Routine Description: Create and initialize a string table. Arguments: None. Return Value: NULL if the string table could not be created (out of memory). Otherwise returns an opaque value that references the string table in other StringTable calls. Remarks: This routine returns a string table with synchronization locks required by all public StringTable APIs. If the string table is to be enclosed in a structure that has its own locking (e.g., HINF, HDEVINFO), then the private version of this API may be called, which will not create locks for the string table. --*/ { PSTRING_TABLE StringTable; if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(0)) { if(InitializeSynchronizedAccess(&StringTable->Lock)) { return StringTable; } _pSpUtilsStringTableDestroy(StringTable); } return NULL; } PVOID pSetupStringTableInitializeEx( IN UINT ExtraDataSize, OPTIONAL IN UINT Reserved ) /*++ Routine Description: Create and initialize a string table, where each string can have some arbitrary data associated with it. Arguments: ExtraDataSize - supplies maximum size of arbitrary data that can be associated with strings in the string table that will be created. Reserved - unused, must be 0. Return Value: NULL if the string table could not be created (out of memory). Otherwise returns an opaque value that references the string table in other StringTable calls. Remarks: This routine returns a string table with synchronization locks required by all public StringTable APIs. If the string table is to be enclosed in a structure that has its own locking (e.g., HINF, HDEVINFO), then the private version of this API may be called, which will not create locks for the string table. --*/ { PSTRING_TABLE StringTable; if(Reserved) { return(NULL); } if(StringTable = (PSTRING_TABLE)_pSpUtilsStringTableInitialize(ExtraDataSize)) { if(InitializeSynchronizedAccess(&StringTable->Lock)) { return StringTable; } _pSpUtilsStringTableDestroy(StringTable); } return NULL; } PVOID _pSpUtilsStringTableInitialize( IN UINT ExtraDataSize OPTIONAL ) /*++ Routine Description: Create and initialize a string table. Each string can optionally have some arbitrary data associated with it. THIS ROUTINE DOES NOT INITIALIZE STRING TABLE SYNCHRONIZATION LOCKS! Arguments: ExtraDataSize - supplies maximum size of arbitrary data that can be associated with strings in the string table that will be created. Return Value: NULL if the string table could not be created (out of memory). Otherwise returns an opaque value that references the string table in other StringTable calls. Remarks: The string table returned from this API may not be used as-is with the public StringTable APIs--it must have its synchronization locks initialized by the public form of this API. --*/ { UINT u; PSTRING_TABLE stringTable; LCID locale; // // Allocate a string table // if(stringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) { ZeroMemory(stringTable,sizeof(STRING_TABLE)); stringTable->ExtraDataSize = ExtraDataSize; locale = GetThreadLocale(); // // changes here may need to be reflected in _pSpUtilsStringTableInitializeFromMemoryMappedFile // if(PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_TURKISH) { // // Turkish has a problem with i and dotted i's. // Do comparison in English (default sort) // stringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); } else { // // string tables always use default sorting algorithm // stringTable->Locale = MAKELCID(LANGIDFROMLCID(locale),SORT_DEFAULT); } // // Allocate space for the string table data. // if(stringTable->Data = pSetupMallocWithTag(STRING_TABLE_INITIAL_SIZE,MEMTAG_STRINGDATA)) { stringTable->BufferSize = STRING_TABLE_INITIAL_SIZE; // // Initialize the hash table // for(u=0; uData))[u] = -1; } // // Set the DataSize to the size of the StringNodeOffset list, so // we'll start adding new strings after it. // stringTable->DataSize = HASH_BUCKET_COUNT * sizeof(LONG); return(stringTable); } pSetupFreeWithTag(stringTable,MEMTAG_STRINGTABLE); } return(NULL); } VOID pSetupStringTableDestroy( IN PVOID StringTable ) /*++ Routine Description: Destroy a string table, freeing all resources it uses. Arguments: StringTable - supplies a string table handle returned from a call to pSetupStringTableInitialize(). Return Value: None. --*/ { try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } DestroySynchronizedAccess(&(((PSTRING_TABLE)StringTable)->Lock)); _pSpUtilsStringTableDestroy(StringTable); } except (EXCEPTION_EXECUTE_HANDLER) { // } } VOID _pSpUtilsStringTableDestroy( IN PVOID StringTable ) /*++ Routine Description: Destroy a string table, freeing all resources it uses. THIS ROUTINE DOES NOT DO LOCKING! Arguments: StringTable - supplies a string table handle returned from a call to pSetupStringTableInitialize() or _pSpUtilsStringTableInitializeFromMemoryMappedFile Return Value: None. --*/ { if (((PSTRING_TABLE)StringTable)->BufferSize) { pSetupFreeWithTag(((PSTRING_TABLE)StringTable)->Data,MEMTAG_STRINGDATA); pSetupFreeWithTag(StringTable,MEMTAG_STRINGTABLE); } else { pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE); } } PVOID pSetupStringTableDuplicate( IN PVOID StringTable ) /*++ Routine Description: Create an independent duplicate of a string table. Arguments: StringTable - supplies a string table handle of string table to duplicate. Return Value: Handle for new string table, NULL if out of memory. --*/ { PSTRING_TABLE New = NULL; BOOL locked = FALSE; try { if(!LockTable((PSTRING_TABLE)StringTable)) { leave; } locked = TRUE; if(New = (PSTRING_TABLE)_pSpUtilsStringTableDuplicate(StringTable)) { if(!InitializeSynchronizedAccess(&New->Lock)) { _pSpUtilsStringTableDestroy(New); New = NULL; } } } except (EXCEPTION_EXECUTE_HANDLER) { New = NULL; } if (locked) { UnlockTable((PSTRING_TABLE)StringTable); } return New; } PVOID _pSpUtilsStringTableDuplicate( IN PVOID StringTable ) /*++ Routine Description: Create an independent duplicate of a string table. THIS ROUTINE DOES NOT DO LOCKING! Arguments: StringTable - supplies a string table handle of string table to duplicate. Return Value: Handle for new string table, NULL if out of memory or buffer copy failure. Remarks: This routine does not initialize synchronization locks for the duplicate--these fields are initialized to NULL. --*/ { PSTRING_TABLE New; PSTRING_TABLE stringTable = StringTable; BOOL Success; if(New = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STRINGTABLE)) { CopyMemory(New,StringTable,sizeof(STRING_TABLE)); // // Allocate space for the string table data. // if(New->Data = pSetupMallocWithTag(stringTable->DataSize,MEMTAG_STRINGDATA)) { // // Surround memory copy in try/except, since we may be dealing with // a string table contained in a PNF, in which case the buffer is // in a memory-mapped file. // Success = TRUE; // assume success unless we get an inpage error... try { CopyMemory(New->Data, stringTable->Data, stringTable->DataSize); } except(EXCEPTION_EXECUTE_HANDLER) { Success = FALSE; } if(Success) { New->BufferSize = New->DataSize; ZeroMemory(&New->Lock, sizeof(MYLOCK)); return New; } pSetupFreeWithTag(New->Data,MEMTAG_STRINGDATA); } pSetupFreeWithTag(New,MEMTAG_STRINGTABLE); } return NULL; } PVOID _pSpUtilsStringTableInitializeFromMemoryMappedFile( IN PVOID DataBlock, IN DWORD DataBlockSize, IN LCID Locale, IN UINT ExtraDataSize ) { PSTRING_TABLE StringTable; BOOL WasLoaded = TRUE; // // Allocate a string table // if(!(StringTable = pSetupMallocWithTag(sizeof(STRING_TABLE),MEMTAG_STATICSTRINGTABLE))) { return NULL; } try { StringTable->Data = (PUCHAR)DataBlock; StringTable->DataSize = DataBlockSize; StringTable->BufferSize = 0; // no allocated buffer // // Clear the Lock structure, because mem-mapped string tables can only be accessed // internally // StringTable->Lock.Handles[0] = StringTable->Lock.Handles[1] = NULL; StringTable->ExtraDataSize = ExtraDataSize; if(PRIMARYLANGID(LANGIDFROMLCID(Locale)) == LANG_TURKISH) { // // Turkish has a problem with i and dotted i's. // Do comparison in English. // StringTable->Locale = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); } else { StringTable->Locale = MAKELCID(LANGIDFROMLCID(Locale),SORT_DEFAULT); } } except(EXCEPTION_EXECUTE_HANDLER) { WasLoaded = FALSE; } if(WasLoaded) { return StringTable; } else { pSetupFreeWithTag(StringTable,MEMTAG_STATICSTRINGTABLE); return NULL; } } DWORD _pSpUtilsStringTableGetDataBlock( IN PVOID StringTable, OUT PVOID *StringTableBlock ) { *StringTableBlock = (PVOID)(((PSTRING_TABLE)StringTable)->Data); return ((PSTRING_TABLE)StringTable)->DataSize; }