1053 lines
30 KiB
C
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;
|
|
}
|
|
|
|
|