home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / appwiz / customwz / zapdlg.cpp < prev    next >
C/C++ Source or Header  |  1998-03-05  |  16KB  |  484 lines

  1. // zapdlg.cpp : implementation file
  2. //
  3. // Copyright (c) 1985-1998, Microsoft Corporation. All rights reserved.
  4. //
  5.  
  6. #include "stdafx.h"
  7. #include "customwz.h"
  8. #include "zapdlg.h"
  9. #include "sampleaw.h"
  10. #include "zap.h"
  11. #include "paint.h"
  12.  
  13. #ifdef _PSEUDO_DEBUG
  14. #undef THIS_FILE
  15. static char THIS_FILE[] = __FILE__;
  16. #endif
  17.  
  18. //static CTypedPtrMap<CMapStringToPtr, CString, int> g_TemplateNameCollisions;
  19. static CMapStringToPtr g_TemplateNameCollisions;
  20. CStringList g_TemplateNames;
  21.  
  22. static BOOL FindDuplicateFileNames(LPTSTR pstrPath, CMapStringToPtr &rmapFileNamesFound);
  23.  
  24. void MakeTemplateName(CString& strTemplateName, LPCTSTR szFileName)
  25. {
  26.     // Make template name upper-case
  27.     strTemplateName = szFileName;
  28.     strTemplateName.MakeUpper();
  29.  
  30.     // See if this template name collides with others we're using
  31.     int nNextDigit;
  32.     if (g_TemplateNameCollisions.Lookup(strTemplateName, (void *&)nNextDigit))
  33.     {
  34.         // There is a collision, so append a digit to the name & update
  35.         //  collisions map
  36.         CString strOldTemplateName = strTemplateName;
  37.         strTemplateName.Format("%s%d", (LPCTSTR) strOldTemplateName, nNextDigit);
  38.         g_TemplateNameCollisions[strOldTemplateName] = (void *) ++nNextDigit;
  39.  
  40.         // Call self recusively to make sure the new name doesn't collide
  41.         MakeTemplateName(strTemplateName, strTemplateName);
  42.     }
  43.     else
  44.     {
  45.         // No collision, so update collision map for the future.
  46.         g_TemplateNameCollisions[strTemplateName] = (void *)2;
  47.     }
  48. }
  49.  
  50. // This determines what type of project we're zapping.  The heuristic used:
  51. // if no TARGTYPE line in the project makefile (or it doesn't fit criteria below), type is unknown
  52. // TARGTYPE line has 1 as low byte of the value, type is projExe
  53. // TARGTYPE line has 2 as low byte of the value, type is projDLL
  54. // TARGTYPE line has 8 as high byte of the value, type is projJava
  55. enProjType GetProjectType(LPCTSTR lpszProjName)
  56. {
  57.     #define SIZEBUF 256
  58.     char szBuf[SIZEBUF];
  59.  
  60.     DWORD dwTargType = 0;
  61.     CStdioFile file(lpszProjName, CFile::modeRead | CFile::typeText);
  62.  
  63.     // Read through makefile line by line
  64.     while (file.ReadString(szBuf, SIZEBUF-1) != NULL)
  65.     {
  66.         // Is this line a "# TARGTYPE" line?  If so, grab the TARGTYPE value
  67.         if (sscanf(szBuf, _T("# TARGTYPE \"%*[^\"]\" 0x%lx"), &dwTargType) == 1)
  68.             break;
  69.     }
  70.  
  71.     // Check the TARGTYPE value to see what it is
  72.     if ((dwTargType & 0xFF00) == 0x800)
  73.         return projJava;
  74.     else if ((dwTargType & 0x00FF) == 1)
  75.         return projExe;
  76.     else if ((dwTargType & 0x00FF) == 2)
  77.         return projDLL;
  78.     else
  79.         return projUnknown;
  80. }
  81.  
  82.  
  83. // This helper (used in TraverseDirectory) doubles occurrences of the backslash
  84. //  character in szPath, so the path can be enclosed in double-quotes in the
  85. //  resource file.
  86. void DoubleSlashes(CString& strPathDblSlashes, LPCTSTR szPath)
  87. {
  88.     strPathDblSlashes.Empty();
  89.     while (*szPath != '\0')
  90.     {
  91.         if (*szPath == '\\')
  92.             strPathDblSlashes += "\\\\";
  93.         else
  94.             strPathDblSlashes += *szPath;
  95.         szPath = _tcsinc(szPath);
  96.     }
  97. }
  98.  
  99. // This helper (used in TraverseDirectory) determines whether we should skip
  100. //  zapping files in the given directory based on its name.
  101. inline BOOL ShouldSkipDirectory(LPCTSTR szDir)
  102. {
  103.     return (!_tcscmp(szDir, _T(".")) || !_tcscmp(szDir, _T(".."))
  104.         || !_tcsicmp(szDir, _T("debug")) || !_tcsicmp(szDir, _T("release"))
  105.         || !_tcsicmp(szDir, _T("windebug")) || !_tcsicmp(szDir, _T("winrel"))
  106.         || !_tcsicmp(szDir, _T("pmcdebug")) || !_tcsicmp(szDir, _T("pmcrel"))
  107.         || !_tcsicmp(szDir, _T("macdebug")) || !_tcsicmp(szDir, _T("macrel")));
  108. }
  109.  
  110. // This helper (used in TraverseDirectory) determines whether we should skip
  111. //  zapping a file based on its extension.
  112. inline BOOL ShouldSkipFile(LPCTSTR szExt)
  113. {
  114.     return (szExt != NULL &&
  115.         (!_tcsicmp(szExt, _T(".mak")) || !_tcsicmp(szExt, _T(".vcp")) || !_tcsicmp(szExt, _T(".aps"))
  116.             || !_tcsicmp(szExt, _T(".sbr")) || !_tcsicmp(szExt, _T(".plg")) || !_tcsicmp(szExt, _T(".obj"))
  117.             || !_tcsicmp(szExt, _T(".pch")) || !_tcsicmp(szExt, _T(".res")) || !_tcsicmp(szExt, _T(".ilk"))
  118.             || !_tcsicmp(szExt, _T(".rsc")) || !_tcsicmp(szExt, _T(".bsc")) || !_tcsicmp(szExt, _T(".exe"))
  119.             || !_tcsicmp(szExt, _T(".bld")) || !_tcsicmp(szExt, _T(".opt")) || !_tcsicmp(szExt, _T(".pjx"))
  120.             || !_tcsicmp(szExt, _T(".dll")) || !_tcsicmp(szExt, _T(".pdb")) || !_tcsicmp(szExt, _T(".ncb"))
  121.             || !_tcsicmp(szExt, _T(".mdp")) || !_tcsicmp(szExt, _T(".dsw")) || !_tcsicmp(szExt, _T(".dsp"))));
  122. }
  123.  
  124. // This recursive function is called from SetProjectFilesMacros with references
  125. //  to CStrings that correspond to template macros we're setting.  It
  126. //  traverses the given directory and updates these strings with names of
  127. //  files it will zap into templates for the generated custom AppWizard.
  128. void TraverseDirectory(const CString& strProjDir, const CString& strSubdir, CString& strProjFiles,
  129.     CString& strTemplateSubdirs, CString& strTemplateRsc, CString& strGeneratedNewProjInfoDirs)
  130. {
  131.     WIN32_FIND_DATA ffd;
  132.     HANDLE hSearch = ::FindFirstFile(strProjDir + strSubdir + _T("*.*"), &ffd);
  133.     if (hSearch == INVALID_HANDLE_VALUE)    // Bad search handle
  134.     {
  135.         if (strSubdir.IsEmpty())
  136.             // We've hit a bad search handle immediately after being called--
  137.             //  the project directory is invalid.  Throw exception.
  138.             ReportAndThrow(IDP_BAD_PROJDIR, strProjDir + strSubdir);
  139.         else
  140.             // We've hit a bad search handle in a recursive call--
  141.             //  empty project subdirectory.  Just ignore it.
  142.             return;
  143.     }
  144.  
  145.     do
  146.     {
  147.         if (ffd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
  148.         {
  149.             // If it's a directory, recursively call self with the directory,
  150.             //  and note the directory structure in the custom AppWizard's newproj.inf
  151.             if (ShouldSkipDirectory(ffd.cFileName))
  152.                 continue;
  153.  
  154.             CString strNewSubdir = (strSubdir + ffd.cFileName) + _T('\n');
  155.             strGeneratedNewProjInfoDirs += _T('/') + strNewSubdir;
  156.             strTemplateSubdirs += _T('/') + (_T("template\\") + strNewSubdir);
  157.             CString strNewDir = strSubdir;
  158.             strNewDir += ffd.cFileName;
  159.             strNewDir += _T('\\');
  160.             TraverseDirectory(strProjDir, strNewDir, strProjFiles, strTemplateSubdirs,
  161.                 strTemplateRsc, strGeneratedNewProjInfoDirs);
  162.             continue;
  163.         }
  164.  
  165.         // Skip if it's a file we know we shouldn't copy (e.g., the makefile, .vcp file,
  166.         //  .aps file, etc.).
  167.         LPCTSTR lpch = _tcsrchr(ffd.cFileName, _T('.'));
  168.         if (ShouldSkipFile(lpch))
  169.             continue;
  170.  
  171.         // Skip if it has no length
  172.         if (ffd.nFileSizeLow == 0 && ffd.nFileSizeHigh == 0)
  173.             continue;
  174.  
  175.         // Zap the file's name
  176.         CString strRootFile;
  177.         sampleaw.m_Zap.ZapFileName(ffd.cFileName, strRootFile, 1);
  178.  
  179.         // Now that we have the info we need, update the strings we were passed
  180.  
  181.         // First, the string used in CUSTMWZ.DLL's newproj.inf:
  182.                         // The original project file...
  183.         strProjFiles += (_T("#") + strProjDir + strSubdir + ffd.cFileName)
  184.                             // File to emit as custom AppWizard's template...
  185.                             + (_T("\ttemplate\\") + strSubdir + strRootFile + _T('\n'));
  186.  
  187.         // Next, the string used in CUSTMWZ.DLL's root.rc (i.e., the
  188.         //  generated custom AppWizard's resource file).
  189.         CString strSubdirDblSlashes;
  190.         DoubleSlashes(strSubdirDblSlashes, strSubdir);
  191.         CString strTemplateName;
  192.         MakeTemplateName(strTemplateName, strRootFile);
  193.         strTemplateRsc += strTemplateName +
  194.             _T("\tTEMPLATE DISCARDABLE\t\"template\\\\") + strSubdirDblSlashes + strRootFile +
  195.             _T("\"\n");
  196.         // Remember the template name for later, when we're generating lines in
  197.         //  CUSTMWZ.DLL's newnwprj.inf (i.e., the generated custom AppWizard's
  198.         //  newproj.inf).
  199.         g_TemplateNames.AddTail(strTemplateName);
  200.  
  201.     }
  202.     while (::FindNextFile(hSearch,  &ffd));
  203.     ::FindClose(hSearch);
  204. }
  205.  
  206.  
  207. // This function drives it all.  It's called just before the zap dialog is
  208. //  dismissed, and coordinates setting the template macros that correspond
  209. //  with the set of files to be zapped.
  210. void SetProjectFilesMacros(LPCTSTR lpszSrcProjName)
  211. {
  212.     // In order to allow the user to click "Finish" from the first step,
  213.     //  we should gracefully handle the case that no project was named to zap.
  214.     if (lpszSrcProjName == NULL || *lpszSrcProjName == _T('\0'))
  215.     {
  216.         // If project name not specified, empty out the zapper macros
  217.         DefineBoolMacro(_T("ZAP_FULL_PATH"), FALSE);
  218.         DefineBoolMacro(_T("PROJECT_FILES"), FALSE);
  219.         DefineBoolMacro(_T("GENERATED_NEWPROJ_INF_FILES"), FALSE);
  220.         DefineBoolMacro(_T("GENERATED_NEWPROJ_INF_DIRS"), FALSE);
  221.         DefineBoolMacro(_T("TEMPLATE_RSC"), FALSE);
  222.         return;
  223.     }
  224.  
  225.     // Here, we have a valid, non-NULL project name to zap.  First, get its
  226.     //  full path name
  227.     CString strFullPath;
  228.     _tfullpath(strFullPath.GetBuffer(_MAX_PATH), lpszSrcProjName, _MAX_PATH);
  229.     strFullPath.ReleaseBuffer();
  230.     DefineStringMacro(_T("ZAP_FULL_PATH"), strFullPath);
  231.  
  232.     // Divide the path name into the various components.
  233.     CString strDrive, strDir, strName, strExt;
  234.     _tsplitpath(strFullPath, strDrive.GetBuffer(_MAX_DRIVE), strDir.GetBuffer(_MAX_DIR),
  235.         strName.GetBuffer(_MAX_FNAME), strExt.GetBuffer(_MAX_EXT));
  236.     strDrive.ReleaseBuffer();
  237.     strDir.ReleaseBuffer();
  238.     strName.ReleaseBuffer();
  239.     strExt.ReleaseBuffer();
  240.     sampleaw.m_Zap.SetRoot(strDir, strName);
  241.     DefineStringMacro(_T("ZAP_SRC_PROJ"), strName + strExt);
  242.     strDir = strDrive + strDir;
  243.  
  244.     // Now, declare the strings that will be passed to TraverseDirectory, and
  245.     //  filled with lists of the files we'll zap.  These strings will later
  246.     //  be used as template macros.
  247.     CString strProjFiles;               // Will be $$PROJECT_FILES$$ in newproj.inf
  248.     CString strTemplateSubdirs;             // Will be $$PROJECT_DIRS$$ in newproj.inf
  249.     CString strGeneratedNewProjInfoDirs;// Will be $$GENERATED_NEWPROJ_INF_DIRS$$
  250.     CString strTemplateRsc;             // Will be $$TEMPLATE_RSC$$ in root.rc
  251.  
  252.     // Set their values
  253.     g_TemplateNameCollisions.RemoveAll();               // Initialize g_TemplateNameCollisions
  254.     g_TemplateNameCollisions[_T("NEWPROJ.INF")] = (void *)2;    //  w/ the two canned template names
  255.     g_TemplateNameCollisions[_T("CONFIRM.INF")] = (void *)2;
  256.     g_TemplateNames.RemoveAll();
  257.     TraverseDirectory(strDir, _T(""), strProjFiles, strTemplateSubdirs, strTemplateRsc, strGeneratedNewProjInfoDirs);
  258.  
  259.     // Set the template macros to their values
  260.     DefineStringMacro(_T("PROJECT_FILES"), strProjFiles);
  261.     DefineStringMacro(_T("TEMPLATE_SUBDIRS"), strTemplateSubdirs);
  262.     DefineStringMacro(_T("GENERATED_NEWPROJ_INF_DIRS"), strGeneratedNewProjInfoDirs);
  263.     DefineStringMacro(_T("TEMPLATE_RSC"), strTemplateRsc);
  264. }
  265.  
  266.  
  267. /////////////////////////////////////////////////////////////////////////////
  268. // CZapDlg dialog
  269.  
  270.  
  271. CZapDlg::CZapDlg()
  272.     : CAppWizStepDlg(CZapDlg::IDD)
  273. {
  274.     //{{AFX_DATA_INIT(CZapDlg)
  275.     m_strProjName = _T("");
  276.     //}}AFX_DATA_INIT
  277. }
  278.  
  279.  
  280. void CZapDlg::DoDataExchange(CDataExchange* pDX)
  281. {
  282.     CAppWizStepDlg::DoDataExchange(pDX);
  283.     //{{AFX_DATA_MAP(CZapDlg)
  284.     DDX_Text(pDX, IDC_PROJ_NAME, m_strProjName);
  285.     //}}AFX_DATA_MAP
  286. }
  287.  
  288.  
  289. BEGIN_MESSAGE_MAP(CZapDlg, CAppWizStepDlg)
  290.     //{{AFX_MSG_MAP(CZapDlg)
  291.     ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
  292.     ON_WM_PAINT()
  293.     //}}AFX_MSG_MAP
  294. END_MESSAGE_MAP()
  295.  
  296.  
  297. /////////////////////////////////////////////////////////////////////////////
  298. // CZapDlg message handlers
  299.  
  300. // Validate the name of the project to zap, and set zap-related template
  301. //  macros before dismissing
  302. BOOL CZapDlg::OnDismiss()
  303. {
  304.     UpdateData(TRUE);
  305.  
  306.     // If the user specifies a nonempty, nonexistent path, balk
  307.     if (!m_strProjName.IsEmpty()
  308.         && GetFileAttributes(m_strProjName) == 0xffffffff)
  309.         return ReportError(IDP_ZAP_CANT_OPEN_FILE, m_strProjName);
  310.     else if (m_strProjName.IsEmpty())   // no project name, no checking we can do
  311.         return TRUE;
  312.  
  313.     // WARNING: continuing past this point with an empty project name could yield
  314.     // unpredictable actions as _splitpath doesn't check for a non-NULL path
  315.     // before trying to break it up into drive and directory.
  316.  
  317.     CHAR rgchPathBuffer[_MAX_PATH];
  318.     CHAR rgchDrive[_MAX_DRIVE];
  319.     CHAR rgchDirectory[_MAX_DIR];
  320.  
  321.     _splitpath(m_strProjName, rgchDrive, rgchDirectory, NULL, NULL);
  322.     _makepath(rgchPathBuffer, rgchDrive, rgchDirectory, NULL, NULL);
  323.  
  324.     CMapStringToPtr map;
  325.  
  326.     BOOL fFoundDuplicate = FindDuplicateFileNames(rgchPathBuffer, map);
  327.  
  328.     if (fFoundDuplicate)
  329.         return ReportError(IDP_ZAP_BAD_PROJECT, m_strProjName);
  330.  
  331.     if (m_strProjName.IsEmpty())
  332.         return ReportError(IDP_ZAP_CANT_OPEN_FILE, m_strProjName);
  333.  
  334.     // Now set the macros corresponding to the files we zap into templates.
  335.     //  This may throw an exception if it runs into an error.  In that
  336.     //  case, we'll return FALSE.
  337.     TRY
  338.     {
  339.         enProjType projType = GetProjectType(m_strProjName);
  340.         if (projType != projExe && projType != projDLL)
  341.             return ReportError(IDP_ZAP_NOT_CPP, m_strProjName);
  342.  
  343.         DefineBoolMacro(_T("CREATE_DLL_PROJECT"), projType == projDLL);
  344.         SetProjectFilesMacros(m_strProjName);
  345.     }
  346.     CATCH(CFileException, e)
  347.     {
  348.         return ReportError(IDP_ZAP_CANT_OPEN_FILE, m_strProjName);
  349.     }
  350.     AND_CATCH(CException, e)
  351.     {
  352.         return FALSE;
  353.     }
  354.     END_CATCH
  355.  
  356.     // If we got this far, we've successfully built our list of files
  357.     //  to zap.  We're ready to continue.
  358.     return TRUE;
  359. }
  360.  
  361. #define BROWSE_DLG_HELP_ID 17304
  362.  
  363. // Handle the "Browse..." button by popping up a file navigator.
  364. void CZapDlg::OnBrowse()
  365. {
  366.     CString strBrowseTitle, strFilterString;
  367.     strBrowseTitle.LoadString(IDS_BROWSE_TITLE);
  368.     strFilterString.LoadString(IDS_BROWSE_FILTER);
  369.  
  370.     CFileDialog dlg(
  371.         TRUE,                                       // Open File Dialog
  372.         _T("dsp"),                                  // Default extension
  373.         NULL,                                       // No default filename
  374.         OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,     // OPENFILENAME flags
  375.         strFilterString);   // Filter strings
  376.     dlg.m_ofn.lpstrTitle = strBrowseTitle;
  377.     dlg.SetHelpID(BROWSE_DLG_HELP_ID);
  378.     if (dlg.DoModal() == IDOK)
  379.         m_strProjName = dlg.GetPathName();
  380.  
  381.     UpdateData(FALSE);
  382. }
  383.  
  384. #define STEP2_LEFT          8
  385. #define STEP2_TOP           40
  386. #define STEP2_WIDTH         179
  387. #define STEP2_HEIGHT        224
  388.  
  389. // Override OnPaint to draw the one static picture on the left side
  390. void CZapDlg::OnPaint()
  391. {
  392.     CPaintDC dc(this); // device context for painting
  393.  
  394.     PaintBackground(&dc, this);
  395.  
  396.     CDC dcMem;
  397.     if (!dcMem.CreateCompatibleDC(&dc))
  398.         return;
  399.  
  400.     // Picture
  401.     PaintBitmap(IDB_STEP2, STEP2_LEFT, STEP2_TOP, STEP2_WIDTH, STEP2_HEIGHT, &dc, &dcMem);
  402. }
  403.  
  404. BOOL FindDuplicateFileNames(LPTSTR ptstrPath, CMapStringToPtr &rmap)
  405. {
  406.     WIN32_FIND_DATA wfd;
  407.  
  408.     BOOL fResult = FALSE; // Assume no duplicates
  409.  
  410.     size_t cchOldLength;
  411.  
  412. #if defined(_UNICODE)
  413.     cchOldLength = wcslen(ptstrPath);
  414. #else
  415.     cchOldLength = strlen(ptstrPath);
  416. #endif
  417.  
  418.     TCHAR *ptchOldNull = &ptstrPath[cchOldLength];
  419.  
  420.     _tcscpy(ptchOldNull, TEXT("*.*"));
  421.  
  422.     HANDLE hFind = ::FindFirstFile(ptstrPath, &wfd);
  423.  
  424.     if (hFind != INVALID_HANDLE_VALUE)
  425.     {
  426.         // We started a good search.  Let's handle each file now.
  427.         for (;;)
  428.         {
  429.             if ((_tcscmp(wfd.cFileName, TEXT(".")) != 0) &&
  430.                 (_tcscmp(wfd.cFileName, TEXT("..")) != 0))
  431.             {
  432.                 if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
  433.                 {
  434.                     // We've got a subdirectory.  Recurse.
  435.                     _tcscpy(ptchOldNull, wfd.cFileName);
  436.                     _tcscat(ptchOldNull, TEXT("\\"));
  437.  
  438.                     if (FindDuplicateFileNames(ptstrPath, rmap))
  439.                     {
  440.                         fResult = TRUE;
  441.                         break;
  442.                     }
  443.                 }
  444.                 else
  445.                 {
  446.                     // First, canonicalize the case of the file...
  447.                     _tcsupr(wfd.cFileName);
  448.  
  449.                     VOID *pvTemp;
  450.  
  451.                     // If it's in the map, look out!
  452.                     if (rmap.Lookup(wfd.cFileName, pvTemp))
  453.                     {
  454.                         fResult = TRUE;
  455.                         break;
  456.                     }
  457.  
  458.                     // It wasn't there and it isn't a generated file we don't care about; put a NULL in the list.
  459.                     CString strName = wfd.cFileName;
  460.                     CString strExt;
  461.                     int index = strName.ReverseFind('.');
  462.                     if( index != -1 ){
  463.                         strExt = strName.Right( strName.GetLength() - index );
  464.                     }
  465.                     if( strExt != ".EXE" && strExt != ".OBJ" && strExt != ".RES" && strExt != ".CNT" && strExt != ".HLP" ) {
  466.                         rmap.SetAt(wfd.cFileName, NULL);
  467.                     }
  468.                 }
  469.             }
  470.  
  471.             // I hate to lose status if something more erroneous occurred
  472.             // here, but it seems harmless.  -mgrier 6/12/96
  473.             if (!::FindNextFile(hFind, &wfd))
  474.                 break;
  475.         }
  476.  
  477.         ::FindClose(hFind);
  478.     }
  479.  
  480.     ptchOldNull = TEXT('\0');
  481.  
  482.     return fResult;
  483. }
  484.