text: //////////////////////////////////////////////////////////////////////////////
  //
  //  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;
  }
  