Windows2003-3790/enduser/sakit/buildmsi/preparecab/buildcab.cpp
2020-09-30 16:53:55 +02:00

500 lines
19 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 Microsoft Corporation
//
// Module Name:
// BuildCab.cpp
//
// Description:
// Prepares files to generate the CAB file, which will be streamed
// into sasetup.msi.
//
// The directory structure for files to be searched is:
// <SAKBuild> Directory where all files are binplaced, passed from the command line
// ie. C:\binaries.x86fre\sacomponents
// <SAKBuild>\sasetup.msi MSI in which the Files table is searched
// <SAKBuild>\tmpCab Temp directory where files are placed
//
// History:
// travisn 28-FEB-2002 Created
//
//////////////////////////////////////////////////////////////////////////////
#include <crtdbg.h>
#include <atlbase.h>
#include <string>
#include <msi.h>
#include <msiquery.h>
#include <search.h>
#include <shellapi.h>
#include <stdio.h>
LPCWSTR MSI_FILENAME = L"sasetup.msi";
LPCWSTR FILENAME_QUERY = L"SELECT File FROM File";
const DWORD MAX_FILES = 500;
//
// Indexes to the array representing indexes to the data string
// DO NOT CHANGE THESE SINCE THE BINARY SEARCH RELIES ON THEM IN THIS ORDER
//
const DWORD ELEMENTS_IN_SORT_ARRAY = 3;
const DWORD SHORT_FILENAME = 0;
const DWORD LONG_FILENAME = 1;
const DWORD FOUND_FILE = 2;
using namespace std;
//Global list of filenames read from the MSI
wstring g_wsAllFilenames;
//////////////////////////////////////////////////////////////////////////////
//++
// CompareFilenames
//
// Description:
// Function to sort the filenames found in the File table of sasetup.msi.
// It ignores the file key that was prepended by sasetup.msi.
// For example, it compares appmgr.exe instead of F12345_appmgr.exe
//
// Parameters:
// e1 Pointer to a WCHAR that the search function is seeking
// e2 Pointer to an offset in g_wsAllFilenames
//--
//////////////////////////////////////////////////////////////////////////////
int __cdecl CompareFilenames(const void *e1, const void *e2)
{
DWORD *dwElement1 = (DWORD*)e1;
DWORD *dwElement2 = (DWORD*)e2;
//Get the short filename for the first string
wstring wstr1(g_wsAllFilenames.substr(
*dwElement1,
g_wsAllFilenames.find_first_of(L",", *dwElement1) - *dwElement1));
//Get the short filename for the second string
wstring wstr2(g_wsAllFilenames.substr(
*dwElement2,
g_wsAllFilenames.find_first_of(L",", *dwElement2) - *dwElement2));
return _wcsicmp(wstr1.data(), wstr2.data());
}
//////////////////////////////////////////////////////////////////////////////
//++
// CompareStringToFilename
//
// Description:
// Function to search for a string that is equal to the given key
//
// Parameters:
// pKey Pointer to a WCHAR that the search function is seeking
// pFilenameIndex Pointer to an offset in g_wsAllFilenames
//--
//////////////////////////////////////////////////////////////////////////////
int __cdecl CompareStringToFilename(const void *pKey, const void *pFilenameIndex)
{
WCHAR *wszKey = (WCHAR*)pKey;
DWORD *dwElement2 = (DWORD*)pFilenameIndex;
//Create a wstring out of the key
wstring wstr1(wszKey);
//Create a wstring out of the short filename to compare to
wstring wstr2(g_wsAllFilenames.substr(
*dwElement2,
g_wsAllFilenames.find_first_of(L",", *dwElement2) - *dwElement2));
return _wcsicmp(wstr1.data(), wstr2.data());
}
//////////////////////////////////////////////////////////////////////////////
//++
// RenameFilesFromMSI
//
// Description:
// Recursively traverses a directory to look for files that also are listed
// in sasetup.msi and copy them to a temp directory for CAB generation.
// Files are renamed in the temp directory to the file key name in the msi.
// For example, appmgr.exe might be renamed to F12345_appmgr.exe.
// Files not listed in the MSI are ignored. If a file is listed in the msi,
// but not found on the system, a fatal error occurs after this function exits.
//
// Parameters:
// pwsSourceDir [in] The source directory to search for files and recursively call
// this function with its subdirectories (no trailing backslash)
// pwsDestDir [in] The temp directory where the files are copied to for CAB creation
// adwLongAndShortFilenameOffsets [in] Array of pointers to the offsets in
// g_wsAllFilenames where the filenames can be found.
// The long filename might be: F12345_appmgr.exe
// The short filename might be: appmgr.exe (simply an offset to the
// first _ in the long filename)
// dwNumRecords [in] The total number of files in the File table of sasetup.msi
// dwNumFilesCopied [in, out] The running total of the number of files copied to the
// temp directory
// dwLevel [in] The level of recursion, used for debug printing
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT RenameFilesFromMSI(const LPCWSTR pwsSourceDir,
const LPCWSTR pwsDestDir,
const DWORD **adwLongAndShortFilenameOffsets,
const DWORD dwNumRecords,
DWORD &dwNumFilesCopied,
const DWORD dwLevel)
{
HRESULT hr = S_OK;
wstring wsCurrentDir(pwsSourceDir);
wsCurrentDir += L"\\";
wstring wsSearch(pwsSourceDir);
wsSearch += L"\\*.*";
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(wsSearch.data(), &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
//printf("%d: Invalid Handle Value: %ws", dwLevel, pwsSourceDir);
}
else
{
//printf("%d: Directory: %ws", dwLevel, pwsSourceDir);
BOOL bValidFile = TRUE;
while (bValidFile)
{
if (wcscmp(FindFileData.cFileName, L".") != 0 && wcscmp(FindFileData.cFileName, L"..") != 0)
{
//
// If it's a directory, recurse down the directory structure.
// If it's a file, rename it if it's found in the MSI.
//
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
wstring wsChildDir(pwsSourceDir);
wsChildDir += L"\\";
wsChildDir += FindFileData.cFileName;
hr = RenameFilesFromMSI(wsChildDir.data(),
pwsDestDir,
adwLongAndShortFilenameOffsets,
dwNumRecords,
dwNumFilesCopied,
dwLevel + 1);
if (FAILED(hr))
{
break;
}
}
else
{
//printf("%d: File: %ws", dwLevel, FindFileData.cFileName);
DWORD *pFilenameIndex = (DWORD*) bsearch(FindFileData.cFileName,
(void*)adwLongAndShortFilenameOffsets,
dwNumRecords,
sizeof(DWORD)*ELEMENTS_IN_SORT_ARRAY,
CompareStringToFilename);
//
// Rename the file to the name in the MSI
//
if (pFilenameIndex != NULL)
{
//Get the pointer to the long filename instead of the short filename
pFilenameIndex += 1;
//Create the path and filename for the file for the CAB
wstring wsNewFilename(pwsDestDir);
wsNewFilename += g_wsAllFilenames.substr(*pFilenameIndex,
g_wsAllFilenames.find_first_of(L",", *pFilenameIndex) - *pFilenameIndex);
//Create the path and filename for the original file
wstring wsOldFilename(wsCurrentDir);
wsOldFilename += FindFileData.cFileName;
if (CopyFile(wsOldFilename.data(), wsNewFilename.data(), TRUE))
{
//printf(" Successfully copied file to temp CAB folder");
fwprintf(stdout, L".");
dwNumFilesCopied++;
}
else
{
fwprintf(stdout, L"\nYOU HAVE A DUPLICATE FILE. FAILED copying file: %ws, to temp CAB folder: %ws", wsOldFilename.data(), wsNewFilename.data());
hr = E_FAIL;
break;
}
pFilenameIndex += 1;
if (*pFilenameIndex != 0)
fwprintf(stdout, L"\nERROR: pFilenameIndex = %d", (*pFilenameIndex));
else
(*pFilenameIndex) = 1;//Mark this string as found
}
//else
// printf(" Did NOT find %ws in MSI", FindFileData.cFileName);
}
}
if (!FindNextFile(hFind, &FindFileData))
{
bValidFile = FALSE;
}
}
FindClose(hFind);
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//++
// ReadMSIFilenamesAndRenameFiles
//
// Description:
// Opens sasetup.msi in the given source directory, reads all the filenames
// in the File table, sorts the filenames, then calls RenameFilesFromMSI
// to copy and rename the files that will be placed in the CAB and streamed
// into the MSI.
//
// Parameters:
// pwsSakSourceDir [in] The source directory where sasetup.msi is found, with
// no trailing backslash
// ie. C:\binaries.x86fre\sakit
// pwsTempCabDir [in] The directory where all files should be copied to that
// will be packaged in the CAB
//--
//////////////////////////////////////////////////////////////////////////////
HRESULT ReadMSIFilenamesAndRenameFiles(LPCWSTR pwsSakSourceDir, LPCWSTR pwsTempCabDir)
{
wstring wsMsiFilename(pwsSakSourceDir);
wsMsiFilename += L"\\";
wsMsiFilename += MSI_FILENAME;
HRESULT hr = E_FAIL;
fwprintf(stdout, L"\nOpen MSI file: %ws\n", wsMsiFilename.data());
MSIHANDLE hMSI = NULL;
MSIHANDLE hView = NULL;
do
{
//
// Open sasetup.msi
//
UINT rVal = MsiOpenDatabase(wsMsiFilename.data(), MSIDBOPEN_READONLY, &hMSI);
if (rVal != ERROR_SUCCESS)
{
fwprintf(stdout, L"\nFailed opening MSI");
break;
}
//printf("Successfully opened MSI");
//
// Query the msi for the filenames
//
rVal = MsiDatabaseOpenView(hMSI, FILENAME_QUERY, &hView);
if (rVal != ERROR_SUCCESS)
{
fwprintf(stdout, L"\nFailed query to retrieve filenames");
break;
}
//printf("Successfully queried the filenames");
//
// Finalize the query for the filenames
//
rVal = MsiViewExecute(hView, 0);
if (rVal != ERROR_SUCCESS)
{
fwprintf(stdout, L"\nFailed query to finalize the query");
break;
}
//printf("Successfully finalized the query");
//
// Extract the filenames from the MSI one by one
//
DWORD adwLongAndShortFilenameOffsets[MAX_FILES][ELEMENTS_IN_SORT_ARRAY];
MSIHANDLE hRecord;
rVal = MsiViewFetch(hView, &hRecord);
DWORD dwRecord = 0;
DWORD dwOffset = 0;
hr = S_OK;
while (rVal == ERROR_SUCCESS)
{
if (dwRecord >= MAX_FILES)
{
fwprintf(stdout, L"\nExceeded maximum number of files");
hr = E_FAIL;
break;
}
WCHAR wszFilename[MAX_PATH];
DWORD dwLength = MAX_PATH;
rVal = MsiRecordGetString(hRecord, 1, wszFilename, &dwLength);
if (rVal != ERROR_SUCCESS)
{
fwprintf(stdout, L"\nCOULD NOT fetch record %d", dwRecord);
hr = E_FAIL;
break;
}
wstring wsFilename(wszFilename);
wstring wsNewFilename;
int nUnderscore = wsFilename.find_first_of(L"_");
if (nUnderscore == -1)
{
fwprintf(stdout, L"\nCOULD NOT find underscore in %ws", wsFilename.data());
hr = E_FAIL;
break;
}
adwLongAndShortFilenameOffsets[dwRecord][LONG_FILENAME] = dwOffset;
adwLongAndShortFilenameOffsets[dwRecord][SHORT_FILENAME]= dwOffset + nUnderscore + 1;//Add 1 to skip the _
adwLongAndShortFilenameOffsets[dwRecord][FOUND_FILE]= 0;
//wsNewFilename = wsFilename.substr(nUnderscore+1);
g_wsAllFilenames += wsFilename + L",";
MsiCloseHandle(hRecord);
rVal = MsiViewFetch(hView, &hRecord);
dwRecord++;
dwOffset += dwLength + 1;//Add 1 for the comma
}
//If the While loop failed, break out of the function
if (FAILED(hr))
{
break;
}
//
// Sort by the short filename
//
qsort(&adwLongAndShortFilenameOffsets[0][0], dwRecord, sizeof(DWORD)*ELEMENTS_IN_SORT_ARRAY, CompareFilenames);
//Print out the sorted list of filenames
//for (int i=0; i < dwRecord; i++)
//{
// //Get the substrings and print them to make sure they were restored correctly
// DWORD dwLongIndex = adwLongAndShortFilenameOffsets[i][LONG_FILENAME];
// DWORD dwShortIndex = adwLongAndShortFilenameOffsets[i][SHORT_FILENAME];
// wstring wsLongName(g_wsAllFilenames.substr(dwLongIndex, g_wsAllFilenames.find_first_of(L",", dwLongIndex) - dwLongIndex));
// wstring wsShortName(g_wsAllFilenames.substr(dwShortIndex, g_wsAllFilenames.find_first_of(L",", dwShortIndex) - dwShortIndex));
// //printf("%d: %ws -> %ws", i, wsLongName.data(), wsShortName.data());
//}
// Create the directory used to search for files to put in the CAB
wstring wsSourceDir(pwsSakSourceDir);
//Create the directory used to copy and rename the files temporarily for CAB creation
wstring wsTmpCabDir(pwsTempCabDir);
wsTmpCabDir += L"\\";
if (!CreateDirectory(wsTmpCabDir.data(), NULL)
&& GetLastError() != ERROR_ALREADY_EXISTS)
{
fwprintf(stdout, L"\nCOULD NOT create directory %ws", wsTmpCabDir.data());
hr = E_FAIL;
break;
}
DWORD dwNumFilesForCAB = 0;
//Copy and rename files for the CAB file
hr = RenameFilesFromMSI(wsSourceDir.data(),
wsTmpCabDir.data(),
(const DWORD**)adwLongAndShortFilenameOffsets,
dwRecord,
dwNumFilesForCAB,
1);
if (FAILED(hr))
{
break;
}
//
// Check to make sure there are the same number of files copied for CAB creation
// as there are in the MSI table
//
if (dwNumFilesForCAB != dwRecord)
{
fwprintf(stdout, L"\nERROR: %d files listed in the MSI were not found in the source directory", dwRecord - dwNumFilesForCAB);
for (int i=0; i < dwRecord; i++)
{
if (adwLongAndShortFilenameOffsets[i][FOUND_FILE] != 1)
{
DWORD dwIndex = adwLongAndShortFilenameOffsets[i][LONG_FILENAME];
DWORD dwFirst = g_wsAllFilenames.find_first_of(L",", dwIndex);
fwprintf(stdout, L"\n Missing file: %ws",
g_wsAllFilenames.substr(dwIndex, dwFirst - dwIndex).data());
}
}
hr = E_FAIL;
break;
}
} while (false);
if (hMSI)
{
MsiCloseHandle(hMSI);
}
if (hView)
{
MsiCloseHandle(hView);
}
return hr;
}
//////////////////////////////////////////////////////////////////////////////
//++
// WinMain
//
// Description:
// Main entry point to prepare the files for cab generation
// One command line argument is expected, which is the base directory
// where everything is located.
//--
//////////////////////////////////////////////////////////////////////////////
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HRESULT hr = E_FAIL;
LPWSTR *argvCommandLine = NULL;
do
{
int nArgs;
argvCommandLine = CommandLineToArgvW(
GetCommandLine(),// pointer to a command-line string
&nArgs); // receives the argument count
if (argvCommandLine == NULL || nArgs < 3)
{
fwprintf(stdout, L"\nCommand line syntax: PrepareCabFiles.exe <sakitBuildDir> <TempCabDir>\n");
break;
}
fwprintf(stdout, L"\nSAK build directory: %ws", argvCommandLine[1]);
fwprintf(stdout, L"\nTemp Cab Directory: %ws", argvCommandLine[2]);
hr = ReadMSIFilenamesAndRenameFiles(argvCommandLine[1], argvCommandLine[2]);
if (FAILED(hr))
{
fwprintf(stdout, L"\nThere was a FAILURE\n\n");
}
else
{
fwprintf(stdout, L"\nSUCCESS!!\n\n");
}
} while (false);
GlobalFree(argvCommandLine);
if (FAILED(hr))
return 1;//The build script expects a 1 if an error occurred.
return 0;
}