////////////////////////////////////////////////////////////////////////////// // // 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: // Directory where all files are binplaced, passed from the command line // ie. C:\binaries.x86fre\sacomponents // \sasetup.msi MSI in which the Files table is searched // \tmpCab Temp directory where files are placed // // History: // travisn 28-FEB-2002 Created // ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include 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 \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; }