windows-nt/Source/XPSP1/NT/shell/osshell/version/filever.c
2020-09-26 16:20:57 +08:00

1053 lines
30 KiB
C

/***************************************************************************
* 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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "verpriv.h"
#include <memory.h>
#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(
LPWSTR 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);
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(
LPWSTR 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 == 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)dwTotBlockLen<sizeof(VERBLOCK16) ||
pBlock->wTotLen>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;
}