/*************************************************************************** * FILERES.C * * File resource extraction routines. * ***************************************************************************/ // // REARCHITECT - GetVerInfoSize plays tricks and tells the caller to allocate // some extra slop at the end of the buffer in case we need to thunk all // the strings to ANSI. The bug is that it only tells it to allocate // one extra ANSI char (== BYTE) for each Unicode char. This is not correct // in the DBCS case (since one Unicode char can equal a two byte DBCS char) // // We should change GetVerInfoSize return the Unicode size * 2 (instead // of (Unicode size * 1.5) and then change VerQueryInfoA to also use the // * 2 computation instead of * 1.5 (== x + x/2) // // 23-May-1996 JonPa // #include #include #include #include "verpriv.h" #include #define DWORDUP(x) (((x)+3)&~03) typedef struct tagVERBLOCK { WORD wTotLen; WORD wValLen; WORD wType; WCHAR szKey[1]; } VERBLOCK ; typedef struct tagVERHEAD { WORD wTotLen; WORD wValLen; WORD wType; /* always 0 */ WCHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03]; VS_FIXEDFILEINFO vsf; } VERHEAD ; typedef struct tagVERBLOCK16 { WORD wTotLen; WORD wValLen; CHAR szKey[1]; } VERBLOCK16 ; typedef struct tagVERHEAD16 { WORD wTotLen; WORD wValLen; CHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03]; VS_FIXEDFILEINFO vsf; // same as win31 } VERHEAD16 ; DWORD VER2_SIG='X2EF'; extern WCHAR szTrans[]; // function resides in this file extern LPSTR WINAPI VerCharNextA(LPCSTR lpCurrentChar); /* ----- Functions ----- */ DWORD MyExtractVersionResource16W ( LPCWSTR lpwstrFilename, LPHANDLE hVerRes ) { DWORD dwTemp = 0; DWORD (__stdcall *pExtractVersionResource16W)(LPCWSTR, LPHANDLE); HINSTANCE hShell32 = LoadLibraryW(L"shell32.dll"); if (hShell32) { pExtractVersionResource16W = (DWORD(__stdcall *)(LPCWSTR, LPHANDLE)) GetProcAddress(hShell32, "ExtractVersionResource16W"); if (pExtractVersionResource16W) { dwTemp = pExtractVersionResource16W( lpwstrFilename, hVerRes ); } else { dwTemp = 0; } FreeLibrary(hShell32); } return dwTemp; } /* GetFileVersionInfoSize * Gets the size of the version information; notice this is quick * and dirty, and the handle is just the offset * * Returns size of version info in bytes * lpwstrFilename is the name of the file to get version information from * lpdwHandle is outdated for the Win32 api and is set to zero. */ DWORD APIENTRY GetFileVersionInfoSizeW( LPCWSTR lpwstrFilename, LPDWORD lpdwHandle ) { DWORD dwTemp; VERHEAD *pVerHead; HANDLE hMod; HANDLE hVerRes; HANDLE h; DWORD dwError; if (lpdwHandle != NULL) *lpdwHandle = 0; dwTemp = SetErrorMode(SEM_FAILCRITICALERRORS); hMod = LoadLibraryEx(lpwstrFilename, NULL, LOAD_LIBRARY_AS_DATAFILE); SetErrorMode(dwTemp); if (!hMod && GetLastError() == ERROR_FILE_NOT_FOUND) return FALSE; pVerHead = NULL; if (!hMod) { hVerRes = NULL; __try { dwTemp = MyExtractVersionResource16W( lpwstrFilename, &hVerRes ); if (!dwTemp) { dwError = ERROR_RESOURCE_DATA_NOT_FOUND; __leave; } if (!(pVerHead = GlobalLock(hVerRes)) || (pVerHead->wTotLen > dwTemp)) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } dwError = ERROR_SUCCESS; } __except( EXCEPTION_EXECUTE_HANDLER ) { dwError = ERROR_INVALID_DATA; dwTemp = 0 ; } if (pVerHead) GlobalUnlock(hVerRes); if (hVerRes) GlobalFree(hVerRes); SetLastError(dwError); return dwTemp ? dwTemp * 3 : 0; // 3x == 1x for ansi input, 2x for unicode convert space } __try { dwError = ERROR_SUCCESS; if ((hVerRes = FindResource(hMod, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO)) == NULL) { dwError = ERROR_RESOURCE_TYPE_NOT_FOUND; dwTemp = 0; __leave; } if ((dwTemp=SizeofResource(hMod, hVerRes)) == 0) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } if ((h = LoadResource(hMod, hVerRes)) == NULL) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } if ((pVerHead = (VERHEAD*)LockResource(h)) == NULL) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } if ((DWORD)pVerHead->wTotLen > dwTemp) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } dwTemp = (DWORD)pVerHead->wTotLen; dwTemp = DWORDUP(dwTemp); if (pVerHead->vsf.dwSignature != VS_FFI_SIGNATURE) { dwError = ERROR_INVALID_DATA; dwTemp = 0; __leave; } } __except(EXCEPTION_EXECUTE_HANDLER) { dwError = ERROR_INVALID_DATA; dwTemp = 0; } if (pVerHead) UnlockResource(h); FreeLibrary(hMod); SetLastError(dwError); // // dwTemp should be evenly divisible by two since not single // byte components at all (also DWORDUP for safety above): // alloc space for ansi components // // // Keep space for DBCS chars. // return dwTemp ? (dwTemp * 2) + sizeof(VER2_SIG) : 0; } /* GetFileVersionInfo * Gets the version information; fills in the structure up to * the size specified by the dwLen parameter (since Control Panel * only cares about the version numbers, it won't even call * GetFileVersionInfoSize). Notice this is quick and dirty * version, and dwHandle is just the offset (or NULL). * * lpwstrFilename is the name of the file to get version information from. * dwHandle is the handle filled in from the GetFileVersionInfoSize call. * dwLen is the length of the buffer to fill. * lpData is the buffer to fill. */ BOOL APIENTRY GetFileVersionInfoW( LPCWSTR lpwstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData ) { VERHEAD *pVerHead; VERHEAD16 *pVerHead16; HANDLE hMod; HANDLE hVerRes; HANDLE h; UINT dwTemp; BOOL bTruncate, rc; DWORD dwError; UNREFERENCED_PARAMETER(dwHandle); // Check minimum size to prevent access violations // WORD for the VERHEAD wTotLen field if (dwLen < sizeof(WORD)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return (FALSE); } dwTemp = SetErrorMode(SEM_FAILCRITICALERRORS); hMod = LoadLibraryEx(lpwstrFilename, NULL, LOAD_LIBRARY_AS_DATAFILE); SetErrorMode(dwTemp); if (!hMod && GetLastError() == ERROR_FILE_NOT_FOUND) return FALSE; if (hMod == NULL) { // Allow 16bit stuff __try { dwTemp = MyExtractVersionResource16W( lpwstrFilename, &hVerRes ); } __except( EXCEPTION_EXECUTE_HANDLER ) { dwTemp = 0 ; } if (!dwTemp) { SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); return (FALSE); } if (!(pVerHead16 = GlobalLock(hVerRes))) { SetLastError(ERROR_INVALID_DATA); GlobalFree(hVerRes); return (FALSE); } __try { dwTemp = (DWORD)pVerHead16->wTotLen; if (dwTemp > dwLen / 3) { // // We are forced to truncate. // dwTemp = dwLen/3; bTruncate = TRUE; } else { bTruncate = FALSE; } // Now mem copy only the real size of the resource. (We alloced // extra space for unicode) memcpy((PVOID)lpData, (PVOID)pVerHead16, dwTemp); if (bTruncate) { // If we truncated above, then we must set the new // size of the block so that we don't overtraverse. ((VERHEAD16*)lpData)->wTotLen = (WORD)dwTemp; } rc = TRUE; } __except( EXCEPTION_EXECUTE_HANDLER ) { rc = FALSE; } GlobalUnlock(hVerRes); GlobalFree(hVerRes); SetLastError(rc ? ERROR_INVALID_DATA : ERROR_SUCCESS); return rc; } rc = TRUE; dwError = ERROR_SUCCESS; if (((hVerRes = FindResource(hMod, MAKEINTRESOURCE(VS_VERSION_INFO), VS_FILE_INFO)) == NULL) || ((pVerHead = LoadResource(hMod, hVerRes)) == NULL)) { dwError = ERROR_RESOURCE_TYPE_NOT_FOUND; rc = FALSE; } else { __try { dwTemp = (DWORD)pVerHead->wTotLen; if (dwTemp > (dwLen - sizeof(VER2_SIG)) / 2) { // We are forced to truncate. // // dwLen = UnicodeBuffer + AnsiBuffer. // // if we try to "memcpy" with "(dwLen/3) * 2" size, pVerHead // might not have such a big data... // dwTemp = (dwLen - sizeof(VER2_SIG)) / 2; bTruncate = TRUE; } else { bTruncate = FALSE; } // Now mem copy only the real size of the resource. (We alloced // extra space for ansi) memcpy((PVOID)lpData, (PVOID)pVerHead, dwTemp); // Store a sig between the raw data and the ANSI translation area so we know // how much space we have available in VerQuery for ANSI translation. *((DWORD UNALIGNED *)((ULONG_PTR)lpData + dwTemp)) = VER2_SIG; if (bTruncate) { // If we truncated above, then we must set the new // size of the block so that we don't overtraverse. ((VERHEAD*)lpData)->wTotLen = (WORD)dwTemp; } rc = TRUE; } __except( EXCEPTION_EXECUTE_HANDLER ) { dwError = ERROR_INVALID_DATA; rc = FALSE; } } FreeLibrary(hMod); SetLastError(dwError); return (rc); } BOOL VerpQueryValue16( const LPVOID pb, LPVOID lpSubBlockX, INT nIndex, LPVOID *lplpKey, LPVOID *lplpBuffer, PUINT puLen, BOOL bUnicodeNeeded ) { ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; LPSTR lpSubBlock; LPSTR lpSubBlockOrg; NTSTATUS Status; UINT uLen; VERBLOCK16 *pBlock = (VERBLOCK16*)pb; LPSTR lpStart, lpEndBlock, lpEndSubBlock; CHAR cTemp, cEndBlock; BOOL bLastSpec; DWORD dwHeadLen, dwTotBlockLen; INT nCmp; DWORD LastError = ERROR_SUCCESS; BOOL bThunkNeeded; /* * If needs unicode, then we must thunk the input parameter * to ansi. If it's ansi already, we make a copy so we can * modify it. */ if (bUnicodeNeeded) { // // Thunk is not needed if lpSubBlockX == \VarFileInfo\Translation // or if lpSubBlockX == \ // bThunkNeeded = (BOOL)((*(LPTSTR)lpSubBlockX != 0) && (lstrcmp(lpSubBlockX, TEXT("\\")) != 0) && (lstrcmpi(lpSubBlockX, szTrans) != 0)); RtlInitUnicodeString(&UnicodeString, lpSubBlockX); Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE); if (!NT_SUCCESS(Status)) { SetLastError(Status); return FALSE; } lpSubBlock = AnsiString.Buffer; } else { lpSubBlockOrg = (LPSTR)LocalAlloc(LPTR,(lstrlenA(lpSubBlockX)+1)*sizeof(CHAR)); if (lpSubBlockOrg == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } lstrcpyA(lpSubBlockOrg,lpSubBlockX); lpSubBlock = lpSubBlockOrg; } if (!puLen) puLen = &uLen; *puLen = 0; /* Ensure that the total length is less than 32K but greater than the * size of a block header; we will assume that the size of pBlock is at * least the value of this first INT. */ if ((INT)pBlock->wTotLen < sizeof(VERBLOCK16)) { LastError = ERROR_INVALID_DATA; goto Fail; } /* * Put a '\0' at the end of the block so that none of the lstrlen's will * go past then end of the block. We will replace it before returning. */ lpEndBlock = ((LPSTR)pBlock) + pBlock->wTotLen - 1; cEndBlock = *lpEndBlock; *lpEndBlock = '\0'; bLastSpec = FALSE; while ((*lpSubBlock || nIndex != -1)) { // // Ignore leading '\\'s // while (*lpSubBlock == '\\') ++lpSubBlock; if ((*lpSubBlock || nIndex != -1)) { /* Make sure we still have some of the block left to play with */ dwTotBlockLen = (DWORD)(lpEndBlock - ((LPSTR)pBlock) + 1); if ((INT)dwTotBlockLenwTotLen>dwTotBlockLen) goto NotFound; /* Calculate the length of the "header" (the two length WORDs plus * the identifying string) and skip past the value */ dwHeadLen = sizeof(WORD)*2 + DWORDUP(lstrlenA(pBlock->szKey)+1) + DWORDUP(pBlock->wValLen); if (dwHeadLen > pBlock->wTotLen) goto NotFound; lpEndSubBlock = ((LPSTR)pBlock) + pBlock->wTotLen; pBlock = (VERBLOCK16 FAR *)((LPSTR)pBlock+dwHeadLen); /* Look for the first sub-block name and terminate it */ for (lpStart=lpSubBlock; *lpSubBlock && *lpSubBlock!='\\'; lpSubBlock=VerCharNextA(lpSubBlock)) /* find next '\\' */ ; cTemp = *lpSubBlock; *lpSubBlock = '\0'; /* Continue while there are sub-blocks left * pBlock->wTotLen should always be a valid pointer here because * we have validated dwHeadLen above, and we validated the previous * value of pBlock->wTotLen before using it */ nCmp = 1; while ((INT)pBlock->wTotLen>sizeof(VERBLOCK16) && (INT)(lpEndSubBlock-((LPSTR)pBlock))>=(INT)pBlock->wTotLen) { // // Index functionality: if we are at the end of the path // (cTemp == 0 set below) and nIndex is NOT -1 (index search) // then break on nIndex zero. Else do normal wscicmp. // if (bLastSpec && nIndex != -1) { if (!nIndex) { if (lplpKey) { *lplpKey = pBlock->szKey; } nCmp=0; // // Index found, set nInde to -1 // so that we exit this loop // nIndex = -1; break; } nIndex--; } else { // // Check if the sub-block name is what we are looking for // if (!(nCmp=lstrcmpiA(lpStart, pBlock->szKey))) break; } /* Skip to the next sub-block */ pBlock=(VERBLOCK16 FAR *)((LPSTR)pBlock+DWORDUP(pBlock->wTotLen)); } /* Restore the char NULLed above and return failure if the sub-block * was not found */ *lpSubBlock = cTemp; if (nCmp) goto NotFound; } bLastSpec = !cTemp; } /* Fill in the appropriate buffers and return success */ *puLen = pBlock->wValLen; *lplpBuffer = (LPSTR)pBlock + 4 + DWORDUP(lstrlenA(pBlock->szKey) + 1); // // Shouldn't need zero-length value check since win31 compatible. // *lpEndBlock = cEndBlock; /* * Must free string we allocated above */ if (bUnicodeNeeded) { RtlFreeAnsiString(&AnsiString); } else { LocalFree(lpSubBlockOrg); } /*---------------------------------------------------------------------- * thunk the results * * Must always thunk key, always ??? value * * We have no way of knowing if the resource info is binary or strings * Version stuff is usually string info, so thunk. * * The best we can do is assume that everything is a string UNLESS * we are looking at \VarFileInfo\Translation or at \. * * This is acceptable because the documenation of VerQueryValue * indicates that this is used only for strings (except these cases.) *----------------------------------------------------------------------*/ if (bUnicodeNeeded) { // // Do thunk only if we aren't looking for \VarFileInfo\Translation or \ // if (bThunkNeeded) { // subtract 1 since puLen includes null AnsiString.Length = AnsiString.MaximumLength = (SHORT)*puLen - 1; AnsiString.Buffer = *lplpBuffer; // // Do the string conversion in the second half of the buffer // Assumes wTotLen is first filed in VERHEAD // UnicodeString.Buffer = (LPWSTR)((PBYTE)pb + DWORDUP(*((WORD*)pb)) + (DWORD)((PBYTE)*lplpBuffer - (PBYTE)pb)*2); UnicodeString.MaximumLength = (SHORT)(*puLen * sizeof(WCHAR)); RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); *lplpBuffer = UnicodeString.Buffer; } if (lplpKey) { // // Thunk the key // dwHeadLen = lstrlenA(*lplpKey); AnsiString.Length = AnsiString.MaximumLength = (SHORT)dwHeadLen; AnsiString.Buffer = *lplpKey; UnicodeString.Buffer = (LPWSTR) ((PBYTE)pb + DWORDUP(*((WORD*)pb)) + (DWORD)((PBYTE)*lplpKey - (PBYTE)pb)*2); UnicodeString.MaximumLength = (SHORT)((dwHeadLen+1) * sizeof(WCHAR)); RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); *lplpKey = UnicodeString.Buffer; } } SetLastError(LastError); return (TRUE); NotFound: /* Restore the char we NULLed above */ *lpEndBlock = cEndBlock; LastError = ERROR_RESOURCE_TYPE_NOT_FOUND; Fail: if (bUnicodeNeeded) { RtlFreeAnsiString(&AnsiString); } else { LocalFree(lpSubBlockOrg); } SetLastError(LastError); return (FALSE); } /* VerpQueryValue * Given a pointer to a branch of a version info tree and the name of a * sub-branch (as in "sub\subsub\subsubsub\..."), this fills in a pointer * to the specified value and a word for its length. Returns TRUE on success, * FALSE on failure. * * Note that a subblock name may start with a '\\', but it will be ignored. * To get the value of the current block, use lpSubBlock="" */ BOOL APIENTRY VerpQueryValue( const LPVOID pb, LPVOID lpSubBlockX, // can be ansi or unicode INT nIndex, LPVOID *lplpKey, LPVOID *lplpBuffer, PUINT puLen, BOOL bUnicodeNeeded ) { ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; LPWSTR lpSubBlockOrg; LPWSTR lpSubBlock; NTSTATUS Status; VERBLOCK *pBlock = (PVOID)pb; LPWSTR lpStart, lpEndBlock, lpEndSubBlock; WCHAR cTemp, cEndBlock; DWORD dwHeadLen, dwTotBlockLen; BOOL bLastSpec; INT nCmp; BOOL bString; UINT uLen; DWORD LastError = ERROR_SUCCESS; if (!puLen) { puLen = &uLen; } *puLen = 0; /* * Major hack: wType is 0 for win32 versions, but holds 56 ('V') * for win16. */ if (((VERHEAD*)pb)->wType) return VerpQueryValue16(pb, lpSubBlockX, nIndex, lplpKey, lplpBuffer, puLen, bUnicodeNeeded); /* * If doesnt need unicode, then we must thunk the input parameter * to unicode. */ if (!bUnicodeNeeded) { RtlInitAnsiString(&AnsiString, (LPSTR)lpSubBlockX); Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE); if (!NT_SUCCESS(Status)) { SetLastError(Status); return FALSE; } lpSubBlock = UnicodeString.Buffer; } else { lpSubBlockOrg = (LPWSTR)LocalAlloc(LPTR,(lstrlen(lpSubBlockX)+1)*sizeof(WCHAR)); if (lpSubBlockOrg == NULL ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } lstrcpy(lpSubBlockOrg,lpSubBlockX); lpSubBlock = lpSubBlockOrg; } /* Ensure that the total length is less than 32K but greater than the * size of a block header; we will assume that the size of pBlock is at * least the value of this first int. * Put a '\0' at the end of the block so that none of the wcslen's will * go past then end of the block. We will replace it before returning. */ if ((int)pBlock->wTotLen < sizeof(VERBLOCK)) { LastError = ERROR_INVALID_DATA; goto Fail; } lpEndBlock = (LPWSTR)((LPSTR)pBlock + pBlock->wTotLen - sizeof(WCHAR)); cEndBlock = *lpEndBlock; *lpEndBlock = 0; bString = FALSE; bLastSpec = FALSE; while ((*lpSubBlock || nIndex != -1)) { // // Ignore leading '\\'s // while (*lpSubBlock == TEXT('\\')) ++lpSubBlock; if ((*lpSubBlock || nIndex != -1)) { /* Make sure we still have some of the block left to play with */ dwTotBlockLen = (DWORD)((LPSTR)lpEndBlock - (LPSTR)pBlock + sizeof(WCHAR)); if ((int)dwTotBlockLen < sizeof(VERBLOCK) || pBlock->wTotLen > (WORD)dwTotBlockLen) goto NotFound; /* Calculate the length of the "header" (the two length WORDs plus * the data type flag plus the identifying string) and skip * past the value. */ dwHeadLen = (DWORD)(DWORDUP(sizeof(VERBLOCK) - sizeof(WCHAR) + (wcslen(pBlock->szKey) + 1) * sizeof(WCHAR)) + DWORDUP(pBlock->wValLen)); if (dwHeadLen > pBlock->wTotLen) goto NotFound; lpEndSubBlock = (LPWSTR)((LPSTR)pBlock + pBlock->wTotLen); pBlock = (VERBLOCK*)((LPSTR)pBlock+dwHeadLen); /* Look for the first sub-block name and terminate it */ for (lpStart=lpSubBlock; *lpSubBlock && *lpSubBlock!=TEXT('\\'); lpSubBlock++) /* find next '\\' */ ; cTemp = *lpSubBlock; *lpSubBlock = 0; /* Continue while there are sub-blocks left * pBlock->wTotLen should always be a valid pointer here because * we have validated dwHeadLen above, and we validated the previous * value of pBlock->wTotLen before using it */ nCmp = 1; while ((int)pBlock->wTotLen > sizeof(VERBLOCK) && (int)pBlock->wTotLen <= (LPSTR)lpEndSubBlock-(LPSTR)pBlock) { // // Index functionality: if we are at the end of the path // (cTemp == 0 set below) and nIndex is NOT -1 (index search) // then break on nIndex zero. Else do normal wscicmp. // if (bLastSpec && nIndex != -1) { if (!nIndex) { if (lplpKey) { *lplpKey = pBlock->szKey; } nCmp=0; // // Index found, set nInde to -1 // so that we exit this loop // nIndex = -1; break; } nIndex--; } else { // // Check if the sub-block name is what we are looking for // if (!(nCmp=_wcsicmp(lpStart, pBlock->szKey))) break; } /* Skip to the next sub-block */ pBlock=(VERBLOCK*)((LPSTR)pBlock+DWORDUP(pBlock->wTotLen)); } /* Restore the char NULLed above and return failure if the sub-block * was not found */ *lpSubBlock = cTemp; if (nCmp) goto NotFound; } bLastSpec = !cTemp; } /* Fill in the appropriate buffers and return success */ *puLen = pBlock->wValLen; /* Add code to handle the case of a null value. * * If zero-len, then return the pointer to the null terminator * of the key. Remember that this is thunked in the ansi case. * * We can't just look at pBlock->wValLen. Check if it really is * zero-len by seeing if the end of the key string is the end of the * block (i.e., the val string is outside of the current block). */ lpStart = (LPWSTR)((LPSTR)pBlock+DWORDUP((sizeof(VERBLOCK)-sizeof(WCHAR))+ (wcslen(pBlock->szKey)+1)*sizeof(WCHAR))); *lplpBuffer = lpStart < (LPWSTR)((LPBYTE)pBlock+pBlock->wTotLen) ? lpStart : (LPWSTR)(pBlock->szKey+wcslen(pBlock->szKey)); bString = pBlock->wType; *lpEndBlock = cEndBlock; /* * Must free string we allocated above */ if (!bUnicodeNeeded) { RtlFreeUnicodeString(&UnicodeString); } else { LocalFree(lpSubBlockOrg); } /*---------------------------------------------------------------------- * thunk the results * * Must always thunk key, sometimes (if bString true) value *----------------------------------------------------------------------*/ if (!bUnicodeNeeded) { // See if we're looking at a V1 or a V2 input block so we know how much space we // have for decoding the strings. BOOL fV2 = *(PDWORD)((PBYTE)pb + DWORDUP(*((WORD*)pb))) == VER2_SIG ? TRUE : FALSE; DWORD cbAnsiTranslateBuffer; if (fV2) { cbAnsiTranslateBuffer = DWORDUP(*((WORD *)pb)); } else { cbAnsiTranslateBuffer = DWORDUP(*((WORD *)pb)) / 2; } if (bString && *puLen != 0) { DWORD cb, cb2; // // Must multiply length by two (first subtract 1 since puLen includes the null terminator) // UnicodeString.Length = UnicodeString.MaximumLength = (SHORT)((*puLen - 1) * 2); UnicodeString.Buffer = *lplpBuffer; // // Do the string conversion in the second half of the buffer // Assumes wTotLen is first filed in VERHEAD // // cb = offset in buffer to beginning of string cb = (DWORD)((PBYTE)*lplpBuffer - (PBYTE)pb); // cb2 = offset in translation area for this string if (fV2) { cb2 = cb + sizeof(VER2_SIG); } else { cb2 = cb / 2; } AnsiString.Buffer = (PBYTE)pb + DWORDUP(*((WORD*)pb)) + cb2; AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&UnicodeString); if ( AnsiString.MaximumLength > MAXUSHORT ) { LastError = ERROR_INVALID_DATA; goto Fail; } AnsiString.MaximumLength = (USHORT)(__min((DWORD)AnsiString.MaximumLength, (DWORD)(cbAnsiTranslateBuffer-cb2))); RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE); *lplpBuffer = AnsiString.Buffer; *puLen = AnsiString.Length + 1; } if (lplpKey) { DWORD cb, cb2; // // Thunk the key // dwHeadLen = wcslen(*lplpKey); UnicodeString.Length = UnicodeString.MaximumLength = (SHORT)(dwHeadLen * sizeof(WCHAR)); UnicodeString.Buffer = *lplpKey; // cb2 = offset in translation area for this string cb = (DWORD)((PBYTE)*lplpKey - (PBYTE)pb); if (fV2) { cb2 = cb + sizeof(VER2_SIG); } else { cb2 = cb / 2; } AnsiString.Buffer = (PBYTE)pb + DWORDUP(*((WORD*)pb)) + cb2; AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&UnicodeString); if ( AnsiString.MaximumLength > MAXUSHORT ) { LastError = ERROR_INVALID_DATA; goto Fail; } AnsiString.MaximumLength = (USHORT)(__min((DWORD)AnsiString.MaximumLength, (DWORD)(cbAnsiTranslateBuffer-cb2))); RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE); *lplpKey = AnsiString.Buffer; *puLen = AnsiString.Length+1; } } SetLastError(LastError); return (TRUE); NotFound: /* Restore the char we NULLed above */ *lpEndBlock = cEndBlock; LastError = ERROR_RESOURCE_TYPE_NOT_FOUND; Fail: if (!bUnicodeNeeded) { RtlFreeUnicodeString(&UnicodeString); } else { LocalFree(lpSubBlockOrg); } SetLastError(LastError); return (FALSE); } ////////////////////////////////////////////////////////// // // This is an EXACT replica of CharNextA api found in user // it is here so that we won't have to link to user32 LPSTR WINAPI VerCharNextA( LPCSTR lpCurrentChar) { if ((!!NLS_MB_CODE_PAGE_TAG) && IsDBCSLeadByte(*lpCurrentChar)) { lpCurrentChar++; } /* * if we have only DBCS LeadingByte, we will point to string-terminator */ if (*lpCurrentChar) { lpCurrentChar++; } return (LPSTR)lpCurrentChar; }