500 lines
19 KiB
C++
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;
|
|
}
|