windows-nt/Source/XPSP1/NT/sdktools/restools/mui/tools/muiver/muiver.cpp
2020-09-26 16:20:57 +08:00

390 lines
10 KiB
C++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <assert.h>
#include <io.h>
#include <md5.h>
#define MD5_CHECKSUM_SIZE 16
#define RESOURCE_CHECKSUM_LANGID 0x0409
BOOL g_bVerbose = FALSE;
typedef struct
{
BOOL bContainResource;
MD5_CTX ChecksumContext;
} CHECKSUM_ENUM_DATA;
void PrintError()
{
LPTSTR lpMsgBuf;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
))
{
printf("GetLastError():\n %s", lpMsgBuf);
LocalFree( lpMsgBuf );
}
return;
}
////////////////////////////////////////////////////////////////////////////////////
//
// ChecksumEnumNamesFunc
//
// The callback funciton for enumerating the resource names in the specified module and
// type.
// The side effect is that MD5 checksum context (contained in CHECKSUM_ENUM_DATA
// pointed by lParam) will be updated.
//
// Return:
// Always return TRUE so that we can finish all resource enumeration.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChecksumEnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam){
HRSRC hRsrc;
HGLOBAL hRes;
const unsigned char* pv;
LONG ResSize=0L;
WORD IdFlag=0xFFFF;
DWORD dwHeaderSize=0L;
CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam;
if(!(hRsrc=FindResourceEx(hModule, lpType, lpName, RESOURCE_CHECKSUM_LANGID)))
{
//
// If US English resource is not found for the specified type and name, we
// will continue the resource enumeration.
//
return (TRUE);
}
pChecksumEnumData->bContainResource = TRUE;
if (!(ResSize=SizeofResource(hModule, hRsrc)))
{
printf("WARNING: Can not get resource size when generating resource checksum.\n");
return (TRUE);
}
if (!(hRes=LoadResource(hModule, hRsrc)))
{
printf("WARNING: Can not load resource when generating resource checksum.\n");
return (TRUE);
}
pv=(unsigned char*)LockResource(hRes);
//
// Update MD5 context using the binary data of this particular resource.
//
MD5Update(&(pChecksumEnumData->ChecksumContext), pv, ResSize);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////
//
// ChecksumEnumTypesFunc
//
// The callback function for enumerating the resource types in the specified module.
// This function will call EnumResourceNames() to enumerate all resource names of
// the specified resource type.
//
// Return:
// TRUE if EnumResourceName() succeeds. Otherwise FALSE.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ChecksumEnumTypesFunc(HMODULE hModule, LPSTR lpType, LONG_PTR lParam)
{
CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam;
//
// Skip the version resource type, so that version is not included in the resource checksum.
//
if (lpType == RT_VERSION)
{
return (TRUE);
}
if(!EnumResourceNames(hModule, (LPCSTR)lpType, ChecksumEnumNamesFunc, (LONG_PTR)lParam))
{
return (FALSE);
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////////////
//
// GenerateResourceChecksum
//
// Generate the resource checksum for the US English resource in the specified file.
//
// Parameters:
// pszSourceFileName The file used to generate resource checksum.
// pResourceChecksum Pointer to a 16 bytes (128 bits) buffer for storing
// the calcuated MD5 checksum.
// Return:
// TURE if resource checksum is generated from the given file. Otherwise FALSE.
//
// The following situation may return FALSE:
// * The specified file does not contain resource.
// * If the specified file contains resource, but the resource is not US English.
// * The specified file only contains US English version resource.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL GenerateResourceChecksum(LPCSTR pszSourceFileName, unsigned char* pResourceChecksum)
{
HMODULE hModule;
ULONG i;
DWORD dwResultLen;
BOOL bRet = FALSE;
//
// The stucture to be passed into the resource enumeration functions.
//
CHECKSUM_ENUM_DATA checksumEnumData;
checksumEnumData.bContainResource = FALSE;
//
// Start MD5 checksum calculation by initializing MD5 context.
//
MD5Init(&(checksumEnumData.ChecksumContext));
if (g_bVerbose)
{
printf("Generate resource checksum for [%s]\n", pszSourceFileName);
}
if(!(hModule = LoadLibraryEx(pszSourceFileName, NULL, DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE)))
{
if (g_bVerbose)
{
printf("\nERROR: Error in opening resource checksum module [%s]\n", pszSourceFileName);
}
PrintError();
goto GR_EXIT;
}
if (g_bVerbose)
{
printf("\nLoad checksum file: %s\n", pszSourceFileName);
}
//
// Enumerate all resources in the specified module.
// During the enumeration, the MD5 context will be updated.
//
if (!EnumResourceTypes(hModule, ChecksumEnumTypesFunc, (LONG_PTR)&checksumEnumData))
{
if (g_bVerbose)
{
printf("\nWARNING: Unable to generate resource checksum from resource checksum module [%s]\n", pszSourceFileName);
}
goto GR_EXIT;
}
if (checksumEnumData.bContainResource)
{
//
// If the enumeration succeeds, and the specified file contains US English
// resource, get the MD5 checksum from the accumulated MD5 context.
//
MD5Final(&checksumEnumData.ChecksumContext);
memcpy(pResourceChecksum, checksumEnumData.ChecksumContext.digest, 16);
if (g_bVerbose)
{
printf("Generated checksum: [");
for (i = 0; i < MD5_CHECKSUM_SIZE; i++)
{
printf("%02x ", pResourceChecksum[i]);
}
printf("]\n");
}
bRet = TRUE;
}
GR_EXIT:
if (hModule)
{
FreeLibrary(hModule);
}
return (bRet);
}
void PrintUsage()
{
printf("muiver - Print out MUI resource checksum\n");
printf("Usage:\n\n");
printf(" muiver <US English file name>\n");
}
void PrintChecksum(BYTE* lpChecksum, int nSize)
{
int i;
for (i = 0; i < nSize; i++)
{
printf("%02x ", lpChecksum[i]);
}
}
struct LANGANDCODEPAGE {
WORD wLanguage;
WORD wCodePage;
};
LPWSTR EmptyString = L"N/A";
LPWSTR GetFileVersionStringData(LPVOID pVerData, LANGANDCODEPAGE* pLang, LPWSTR pVersionDataType)
{
WCHAR subBlcok[256];
LPVOID lpBuffer;
UINT dwBytes;
wsprintfW( subBlcok,
L"\\StringFileInfo\\%04x%04x\\%s",
pLang->wLanguage,
pLang->wCodePage,
pVersionDataType);
// Retrieve file description for language and code page "i".
if (VerQueryValueW(pVerData,
subBlcok,
&lpBuffer,
&dwBytes)) {
return ((LPWSTR)lpBuffer);
}
return (EmptyString);
}
void PrintFileVersion(LPVOID pVerData)
{
UINT cbTranslate;
LANGANDCODEPAGE *lpTranslate;
// Read the list of languages and code pages.
VerQueryValueW(pVerData,
L"\\VarFileInfo\\Translation",
(LPVOID*)&lpTranslate,
&cbTranslate);
// Read the file description for each language and code page.
for(UINT i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++)
{
wprintf(L" Locale = 0x%04x, CodePage = %d\n", lpTranslate->wLanguage, lpTranslate->wCodePage);
wprintf(L" FileDescriptions: [%s", GetFileVersionStringData(pVerData, lpTranslate+i, L"FileDescription"));
wprintf(L"]\n");
wprintf(L" FileVersion : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"FileVersion"));
wprintf(L" ProductVersion : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"ProductVersion"));
//wprintf(L" Comments : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"Comments"));
}
BYTE* lpResourceChecksum;
UINT cbResourceChecksum;
wprintf(L" ResourceChecksum: [");
if (VerQueryValueW(pVerData,
L"\\VarFileInfo\\ResourceChecksum",
(LPVOID*)&lpResourceChecksum,
&cbResourceChecksum))
{
for (i = 0; i < cbResourceChecksum; i++)
{
wprintf(L"%02x ", lpResourceChecksum[i]);
}
} else
{
wprintf(L"n/a");
}
wprintf(L"]\n");
}
void PrintResult(LPSTR fileName, LPVOID pVerData, BYTE* pChecksum)
{
printf("File: [%s]\n", fileName);
printf("\nVersion information:\n");
if (pVerData == NULL)
{
printf(" !!! Not availabe. Failed in GetFileVersionInfo()\n");
} else
{
PrintFileVersion(pVerData);
}
printf("\n\n Resource Checksum:\n ", fileName);
if (pChecksum == NULL)
{
printf(" n/a. Probably resources for 0x%04x is not available.\n", RESOURCE_CHECKSUM_LANGID);
} else
{
PrintChecksum(pChecksum, MD5_CHECKSUM_SIZE);
}
printf("\n");
}
int __cdecl main(int argc, char *argv[]){
LPSTR pFileName;
LPBYTE lpVerData = NULL;
DWORD dwVerDataSize;
DWORD dwHandle;
BYTE MD5Checksum[MD5_CHECKSUM_SIZE];
if (argc == 1)
{
PrintUsage();
return (1);
}
pFileName = argv[1];
if (dwVerDataSize = GetFileVersionInfoSizeA(pFileName, &dwHandle))
{
lpVerData = new BYTE[dwVerDataSize];
if (!GetFileVersionInfoA(pFileName, 0, dwVerDataSize, (LPVOID)lpVerData)) {
lpVerData = NULL;
}
}
if (GenerateResourceChecksum(pFileName, MD5Checksum))
{
PrintResult(pFileName, (LPVOID)lpVerData, MD5Checksum);
} else
{
PrintResult(pFileName, (LPVOID)lpVerData, NULL);
}
if (!lpVerData)
{
delete [] lpVerData;
}
return (0);
}