Chapter 1
Project Manager Improvements

The Project Manager is the key tool for building executables and the primary tool Visual FoxPro developers use to modify the source code in applications they create. Any change the Fox Team can work into the product increasing productivity or making it a better experience has a high impact on Visual FoxPro developers.

The Project Manager has been tweaked over the last few releases to make day-to-day development a little easier. We think you will find the tweaks in this version a big help when you develop your applications. This chapter covers the changes to the Project Manager and the projecthook object, which you can use to respond to the interaction you have with the Project Manager. We also discuss some of the enhancements with respect to building your application using the Project Manager and the BUILD command. If you are looking for information on creating setups and deploying applications check out Chapter 15, “Setup and Deployment.”

Generating message logs during project build

Prior to Visual FoxPro 9, the build process (whether you used the Build button in the Project Manager, the BUILD command, or the project object’s Build method) did not write out the error log until it was done. This meant if the build failed (stopped by Visual FoxPro or by the developer canceling), the error log was not written, and was not available to be reviewed. Typically you cancel the build when the build process presents an error or a missing file and you realize what the error is and how you want to correct it. The problem is, by the time you open the source code, you forget the details and have no way to get the details without rebuilding. In Visual FoxPro 9, the build process generates the log when the process starts and adds to it as errors and warnings are encountered.

This is a big deal if you have large projects and you hit an error you know you need to fix before testing the build. At the time the error displays, you can cancel the build process and review the error log to gather the details, make the fix in the source, and start the long build process again.

This enhancement gets even better. If the Debug Output window is open (see Figure 1), the same error messages are output to this window along with a complete log of the build process. An entry is output to the Debug Output window as each file/module/method compiles. Additional entries are made as the files/modules/methods are analyzed for dependencies (see Listing 1 for an example of one class library’s log output).

The error log file starts fresh each time a build is started. If no errors are recorded during the build process, the error log file is deleted; otherwise, the process would leave an empty error log.

The Debug Output window is not cleared before each build; the new information is appended to the Debug Output window. This allows you to review, compare, and contrast one build with the next. You can also save the logged output in the Debug Output windows to a file using the Save As option in the Debug Output window’s shortcut menu. (Save As is not a new feature, just a hint of how you can leverage this logged information.)

Listing 1. This is a partial listing of the content in the Debug Output window after a project build is complete.

Build Project: Visual Class Library wlcutils

   Module:  cplatform_access

   Module:  cfullosname_access

   Module:  cosversion_access

   Module:  cservicepack_access

   Module:  cshortservicepack_access

   Module:  ccompleteosinformation_access

   Module:  cbasicosinformation_access

   Module:  release

   Module:  getappversion

   Module:  getappfileinformation

   Module:  cappnametosearch_assign

   Module:  cappdirectory_assign

   Module:  init

   Module:  release

Analyzing:

   Module: CPLATFORM_ACCESS

   Module: CFULLOSNAME_ACCESS

   Module: COSVERSION_ACCESS

   Module: CSERVICEPACK_ACCESS

   Module: CSHORTSERVICEPACK_ACCESS

   Module: CCOMPLETEOSINFORMATION_ACCESS

   Module: CBASICOSINFORMATION_ACCESS

   Module: RELEASE

Analyzing:

   Module: GETAPPVERSION

   Module: GETAPPFILEINFORMATION

   Module: CAPPNAMETOSEARCH_ASSIGN

   Module: CAPPDIRECTORY_ASSIGN

   Module: INIT

   Module: RELEASE

Text Box: ¥

The COMPILE command lists the modules compiled in the Debug Output window the same way Build does.

Figure 1. The build process now records details of the build in the Debug Output window in addition to the normal error log file.

Change the Project Manager font

The Visual FoxPro Project Manager look and feel has remained virtually unchanged in the last several versions. Visual FoxPro 9 introduces the ability to change the item list (treeview) and file description font (not the page tabs, or any of the command buttons). A change to the font in the Project Manager is hardly a big bang change, but it is our opinion that anything enhancing usability and accessibility is an improvement in productivity for someone visually challenged or for developers running at very high resolutions on smaller monitors.

You can define the default font for all projects by setting the font on the IDE page of
the Tools | Options dialog. The process of making the setting is not overly intuitive: first,
set the window type to Projects using the Type dropdown list, and then select the font (see Figure 2).

Change the Project Manager font for the open project by right-clicking the Project Manager and using the Font… option on the shortcut menu (see Figure 3). The Font…
menu item is not available if you use the shortcut menu from the Project Manager’s item
list; you need to right-click outside of the project item list. This feature is not available for a docked project.

If you choose an italic style for the font, the Project Manager displays the file names, categories, and file descriptions in italics, but selecting the bold style has no impact. This is because the main file for the project displays in bold. The font attributes for the project are stored in the FoxUser resource file in a PRJUI record where the Name column contains the project file name (without the extension).

Figure 2. The Options dialog lets you set the default font for all project windows.

Figure 3. The Project Manager’s shortcut menu lets you change the item list font for the open project.

Additional Project Manager shortcut menu items

There are a number of changes to the Project Manager shortcut menus. You see three different shortcut menus. The first when the Project Manager is docked, the second in the item list of undocked/collapsed dropdown tabs (and in tear-off tabs), and the third when the Project Manager is undocked without being collapsed.

The majority of the changes affect developers who dock their projects to a toolbar. The additional items on the shortcut menu for a docked project (Figure 4) include:

·         Close: closes the project.

·         Add Project to Source Control…: opens the add to source control dialog, and is only included on the menu if the active source control provider is selected on the
Tools | Options dialog Projects page.

·         Errors…: displays the error log file from the last build, and is only enabled if the last build produced and recorded errors.

·         Refresh: updates the list of files in the project, and new classes in class libraries.

·         Clean Up Project: packs the deleted records from the project file (PJX).

Figure 4. The shortcut menu when the Project Manager is docked has many more items in VFP 9 than in VFP 8.

The captions for some items in the shortcut menu for the collapsed or docked Project Manager item list (found on the dropdown tabs and tear-off tabs) has changed; in addition, Rename File moved to a different position in the list. There is no additional functionality (see Figure 5).

Figure 5. The shortcut menu for the Project Manager’s item list has slightly different (more informative) captions and the Rename File… menu item moved up in the list.

We already detailed the Font… option in the “Change the Project Manager font” section earlier in this chapter. This option is the only new shortcut menu option added and is only available when the Project Manager is not docked and not collapsed.

Modifying a class library from the Project Manager

You can now double-click a visual class library in the Project Manager item list or click the Modify command button to open the class library in the Class Browser. Prior to Visual FoxPro 9, nothing happened when you double-clicked a class library; the Modify command button was disabled when a class library was selected in the item list.

If you don’t like the new behavior, you can prevent it by issuing NODEFAULT in the QueryModifyFile event of projecthook (see Listing 2). You can also intercept the QueryModifyFile event and run your own class library tool (see Listing 3).

Text Box: "

The Developer Downloads for this chapter, available from www.hentzenwerke.com, include two projecthook classes (phkStopClassBrowser and phkRunOldFashionClassHackingTool) in the wn9ProjectHookDemos.vcx.

Listing 2. The QueryModifyFile event method can be coded to stop a class library from opening in the Class Browser (example from phkStopClassBrowser class).

LPARAMETERS toFile, tcClassName

 

* Only for class libraries

IF LOWER(JUSTEXT(toFile.Name)) = "vcx"

   * Only when no class is selected

   IF EMPTY(tcClassName)

      * Provide behavior found in VFP 8 and earlier

      NODEFAULT

   ENDIF

ENDIF

 

RETURN

Listing 3. The QueryModifyFile event method can be coded to open your favorite class hacking tool (example from phkRunOldFashionClassHackingTool class).

LPARAMETERS toFile, tcClassName

 

#DEFINE IDYES           6       && Yes button pressed

#DEFINE IDNO            7       && No button pressed

 

* Only for class libraries

IF LOWER(JUSTEXT(toFile.Name)) = "vcx"

   * Only when no class is selected

   IF EMPTY(tcClassName)

      lnAnswer = IDNO

 

      * Offer choice if this option is enabled

      IF this.lOfferChoiceOfClassBrowser

         lnAnswer = MESSAGEBOX("Would you prefer to open " +  ;

                               toFile.Name + ;

                               " in VFP Class Browser even though" + ;

                               "you have the old-fashioned BROWSE "+ ;

                               "hacking option enabled via " + ;

                               "the projecthook?", ;

                               4+32, ;

                               "Modify Class Library")

      ENDIF

 

      IF lnAnswer = IDYES

         * No additional functionality, rather "documenting"

         * that the normal behavior is desired in this situation

         RETURN .T.

      ELSE

         * Run your favorite class library tool from

         * White Light Computing

         * DO hackcx4.exe WITH toFile.Name

         IF USED("curHack")

            USE IN (SELECT("curHack"))

         ENDIF

 

         USE (toFile.Name) IN 0 SHARED ALIAS curHack

       

         SELECT curHack

         BROWSE LAST FONT "Tahoma", 12 NAME goOldHack NOWAIT

         NODEFAULT

      ENDIF

   ENDIF

ENDIF

 

RETURN

The key to working with the class library functionality of the Project Manager is to check the parameters passed to the QueryModifyFile method. You can determine whether a class library was passed by evaluating the extension of the file parameter, and checking whether the class name parameter is empty. (If it’s a VCX and the class name parameter is not empty, you are modifying a specific class of the class library.)

In addition to intercepting, stopping, and altering the behavior of double-clicking the class library, you can allow the Class Browser to open after you alter the normal behavior by returning .T. (also the default return value) from the QueryModifyFile event method. This could be a problem if you open the class library via a USE...EXCLUSIVE in your own tool because the Class Browser expects to open the the class library when it starts. If you want to open the class library in your tool and the Class Browser, make sure to open it via USE...SHARED.

ProjectHook SCCInit and SCCDestroy methods

Since the introduction of ProjectHooks in Visual FoxPro 6, developers have tried to integrate with source code control when opening a project and again when closing a project. According to Microsoft, when a project is opened, the developer wants to check out specific files from source code control, and when a project is closed, some or all the checked-out files should be checked in. You can’t use the Init and Destroy methods of the ProjectHook (which fires when project is opened and closed, respectively) because the link to the source code control provider is not established in Init, and is broken in the Destroy. The file object’s CheckIn, CheckOut, GetLastestVersion, AddToSCC, RemoveFromSCC, and UndoCheckOut methods only work once the project is opened and displayed in the Project Manager.

In Visual FoxPro 9, Microsoft provides two new projecthook events. The SCCInit event fires after the link to source code control is established, but before the project is activated. The order of projecthook events when opening the project is:

1.       Init

2.       SCCInit

3.       Activate

The SCCDestroy event fires before the link to source code control is broken, and before the projecthook is destroyed. The order of events when closing a project is:

1.       Deactivate

2.       SCCDestroy

3.       Destroy

The following sections explore some of the things you might do with these new events.

Check out files

Microsoft added the new events in response to requests from developers for the ability to check out all the files needed when opening the project. We’re not big fans of the integrated source code control functionality and prefer to use the source code control client user interface to check files out as needed. But checking out everything when opening a project sounded interesting and like a potential time saver, so it is the first feature we tried.

Text Box: "

The Developer Downloads for this chapter, available from www.hentzenwerke.com, include projecthook classes (phkSCCCheckOutAllFiles and phkSCCCheckOutFilesOnce) in the wn9ProjectHookDemos.vcx that demonstrate using the SCCInit event to check out files when opening the project. The sample code provided for this section of the chapter will not work unless you have a source code control application like Visual Source Safe installed and accessible.

The code in Listing 4 is found in the CheckOut method of the phkSCCCheckoutAllFiles class. The CheckOut method is called from the SCCInit event method. This follows the best practice of never using an event method to hold the primary code for the desired behavior. In addition, it avoids the problem of losing code if we compile this class in VFP 8 or earlier. Because the SCCInit event is not part of VFP 8, any code in SCCInit disappears during the next compile in that version, and is lost, even if you reopen the class in Visual FoxPro 9. Code in a custom method is not deleted; only the code in the event methods is lost (which is a lot easier to fix if all it does is call the custom method).

Listing 4. This code programmatically checks out all the files not currently checked out in a project (under source code control).

* phkSCCCheckoutAllFiles::CheckOut()

 

#DEFINE cnFILECOLUMN          1

#DEFINE cnFILESTATUS          2

 

* File Object SCCStatus Property (available in FoxPro.h)

#DEFINE SCCFILE_NOTCONTROLLED 0  && Not source controlled

#DEFINE SCCFILE_NOTCHECKEDOUT 1  && Controlled but not checked out to anyone

#DEFINE SCCFILE_CHECKEDOUTCU  2  && Checked out to the current user only

#DEFINE SCCFILE_CHECKEDOUTOU  3  && Checked out to someone else

#DEFINE SCCFILE_MERGECONFLICT 4  && Has a merge conflict

#DEFINE SCCFILE_MERGE         5  && Has been merged without conflict

#DEFINE SCCFILE_CHECKEDOUTMU  6  && Checked out to multiple users

 

LOCAL loProject, ;

      loFile, ;

      llCheckoutStatus

 

loProject = _vfp.ActiveProject

 

IF EMPTY(loProject.SCCProvider)

   * Source Code Control not used for this project

   DEBUGOUT "No source code control provider to check out files"

ELSE

   FOR EACH loFile IN loProject.Files

      IF loFile.SCCStatus = SCCFILE_NOTCHECKEDOUT

         * Attempt to check out file...

         WAIT WINDOW "Checking out file: " + ;

                     TRANSFORM(loFile.Name) + ;

                     "..." NOWAIT

 

         llCheckoutStatus = loFile.CheckOut()

 

         this.nFilesCheckedOut = this.nFilesCheckedOut + 1

        

         DIMENSION this.aFiles[this.nFilesCheckedOut, 2]

         this.aFiles[this.nFilesCheckedOut, cnFILECOLUMN] = loFile.Name

         this.aFiles[this.nFilesCheckedOut, cnFILESTATUS] = llCheckoutStatus

 

         DEBUGOUT loFile.Name + ;

                  IIF(llCheckoutStatus, SPACE(0), " not") + ;

                  " checked out - " + ;

                  TRANSFORM(DATETIME())

      ELSE

         * Nothing to do

      ENDIF

   ENDFOR

ENDIF

 

RETURN

The code first checks to see if the project is using integrated source code control. If so, it loops through the Files collection and checks to find files not currently checked out. This is accomplished by evaluating the SCCStatus property for each file. If the file is not checked out, it attempts to check it out by calling the file object’s CheckOut method. The CheckOut method returns True if the file was successfully checked out. It logs the process by recording the file name and check out status to an array property called aFiles and displays a message in the Debug Output window.

The drawback of this approach is the Check Out Files dialog (see Figure 6) is presented for every file not already checked out when this method runs. The current file is selected for check-out in the dialog, so all you have to do is click the OK button and the checkout process is attempted. Having to click OK or Cancel for each of the files in a project is tedious. You can select all the files you want to check out the first time the dialog is presented. This avoids the dialog being presented for each of the files individually. Still canceling all the files you do not want checked out will reduce your productivity.

If you want to see the Check Out Files dialog once, you can use the code in Listing 5, found in the CheckOut method (called from the SCCInit event method) of the phkSCCCheckoutFilesOnce class. The disadvantage of this approach is you lose the individual file logging functionality, so you will not be able to determine if a certain file failed to check out. We think you will find this trade-off well worth it and more productive. If all files are already checked out, you get a message “Project error. There are no files to check out.” This is the same message you get when using the Project menu to check out files and no files need to be checked out.

Listing 5. This code presents the Check Out Files dialog once when opening
a project.

LOCAL loProject, ;

      loFile

 

loProject = _vfp.ActiveProject

 

IF EMPTY(loProject.SCCProvider)

   * Source Code Control not used for this project

   DEBUGOUT "No source code control provider to check out files"

ELSE

   loFile = loProject.Files[1]

   loFile.CheckOut()

ENDIF

 

RETURN

The advantage of this approach is you can cancel the Check Out Files dialog and move on to the task of development if you do not want to check any files out. Alternatively, you can select the files when you open the individual source files (the same dialog appears). If you use the check out process when opening a project, you ensure that you have the current versions of the files. Otherwise, one of your teammates could grab a file before you get a chance to edit it.

Figure 6. The Check Out Files dialog appears each time the file object’s CheckOut method is called.

You might ask why we did not write the reverse code to check the files into source code control via the SCCDestroy event method. We feel the check-in process requires a commitment from the developer that the code is solid and tested and ready to be integrated with the rest of the code in the project before it is checked into source control. This is not something we can control programmatically; therefore we did not implement it in our sample classes. We open and close projects frequently and do not want files checked in just because we closed the project.

Get latest version of files

The biggest advantage we saw with the SCCInit event when we first read about it was the ability to get the latest version of the files from source code control. We frequently need to make sure we have the latest version of source code other members of the team might be changing. Automating this when the project is opened could save us the extra step of making the manual request.

Text Box: "

The Developer Downloads for this chapter, available from www.hentzenwerke.com, include projecthook classes (phkSCCGetLatestAllFiles and phkGetLatestFilesOnce in wn9ProjectHookDemos.vcx) that demonstrate how the SCCInit event can help you get the latest version of source code files when opening a project.

Unfortunately, like the CheckOut example, calling GetLatestVersion leads to dialogs appearing. With this in mind, we created two projecthooks to address the issue the same way we did with the CheckOut process. Listing 6 shows you how to get the latest version of all files with individual logging, and Listing 7 shows you how to present the Get Latest Version dialog once, but give up logging.

Listing 6. This code programmatically gets the latest version of all the files not currently checked out in a project (under source code control), or have a merge conflict status.

* Code to get the latest version of all files that are

* not already checked out.

 

#DEFINE cnFILECOLUMN          1

#DEFINE cnFILESTATUS          2

 

* File Object SCCStatus Property (available in FoxPro.h)

#DEFINE SCCFILE_NOTCONTROLLED 0  && Not source controlled

#DEFINE SCCFILE_NOTCHECKEDOUT 1  && Controlled but not checked out to anyone

#DEFINE SCCFILE_CHECKEDOUTCU  2  && Checked out to the current user only

#DEFINE SCCFILE_CHECKEDOUTOU  3  && Checked out to someone else

#DEFINE SCCFILE_MERGECONFLICT 4  && Has a merge conflict

#DEFINE SCCFILE_MERGE         5  && Has been merged without conflict

#DEFINE SCCFILE_CHECKEDOUTMU  6  && Checked out to multiple users

 

LOCAL loProject, ;

      loFile, ;

      llGetLatestStatus

 

loProject = _vfp.ActiveProject

 

IF EMPTY(loProject.SCCProvider)

   * Source Code Control not used for this project

   DEBUGOUT "No source code control provider to get latest version of files"

ELSE

   FOR EACH loFile IN loProject.Files

      IF INLIST(loFile.SCCStatus, SCCFILE_NOTCONTROLLED, ;

                                  SCCFILE_CHECKEDOUTCU, ;

                                  SCCFILE_MERGECONFLICT)

         * Nothing to do, either not in SCC, or already checked out,

         * or there is a merge conflict (highly unlikely with VFP

         * source code and integrated source code control).

      ELSE

         * Attempt to get the latest version of file...

         WAIT WINDOW "Getting latest version of file: " + ;

                     TRANSFORM(loFile.Name) + ;

                     "..." NOWAIT

        

         llGetLatestStatus  = loFile.GetLatestVersion()

         this.nFilesUpdated = this.nFilesUpdated + 1

        

         DIMENSION this.aFiles[this.nFilesUpdated, 2]

         this.aFiles[this.nFilesUpdated, cnFILECOLUMN] = loFile.Name

         this.aFiles[this.nFilesUpdated, cnFILESTATUS] = llGetLatestStatus

        

         DEBUGOUT loFile.Name + ;

                  IIF(llGetLatestStatus, SPACE(0), " not") + ;

                  " updated - " + ;

                  TRANSFORM(DATETIME())

      ENDIF

   ENDFOR

ENDIF

 

RETURN

Listing 7. This code presents the Get Latest Version of Files dialog once when opening a project.

LOCAL loProject, ;

      loFile

 

loProject = _vfp.ActiveProject

 

IF EMPTY(loProject.SCCProvider)

   * Source Code Control not used for this project

   DEBUGOUT "No source code control provider to update " + ;

            "latest version of files."

ELSE

   loFile = loProject.Files[1]

   loFile.GetLatestVersion()

ENDIF

 

RETURN

The code is very similar to the code used to demonstrate the check out process. The big difference is the checks made to the SCCStatus property to ensure you do not get the latest version of a file you already have checked out and possibly changed. The other difference, of course, is calling the GetLatestVersion method of the file object to tell the source code control provider to copy the latest version to your project folder.

Log source code control state

The final example for the SCCInit and SCCDestroy actually uses both events. In this example, we create a log of the source code control status for each file in the project. This log is sent to a text file, and can later be imported into a DBF given that the output is created as a comma delimited string.


Text Box: "

The Developer Downloads for this chapter, available from www.hentzenwerke.com, include a projecthook class (phkSCCListState in wn9ProjectHookDemos.vcx) that demonstrates how you can list the status to a log file.

The ListStatus method (see Listing 8) can be found in the phkSCCListStatus class. This method is called from both the SCCInit and SCCDestroy. The method accepts one parameter allowing you to determine which of the two methods called it. The method collects details about each file in the project and outputs them to a text file specified by the cListStatusFileName property. The text file is always stored in the Visual FoxPro temporary files folder; if it already exists, ListStatus adds the new information rather than overwriting it.

Listing 8. This code records the SCCStatus of each file when opening and closing
a project.

LPARAMETERS tcTiming

 

LOCAL loProject, ;

      lcStatusOutput, ;

      lcFileName

 

loProject      = _vfp.ActiveProject

lnFiles        = loProject.Files.Count

lcStatusOutput = REPLICATE("=", 60) + ;

                 CHR(13)+CHR(10) + ;

                 tcTiming + " - " + loProject.Name + ;

                 CHR(13)+CHR(10)

 

 

lcStatusOutput = lcStatusOutput + ;

                 TRANSFORM(DATETIME()) + ;

                 " - " + ;

                 loProject.SCCProvider + ;

                 REPLICATE(CHR(13)+CHR(10),2)

 

FOR lnI = 1 TO lnFiles

   lcSCCText      = SPACE(0)

 

   DO CASE

      CASE loProject.Files[lnI].SCCStatus = 0;

             AND this.lListCheckedOutOnly = .F.

         lcSCCText = "File is not source controlled."

 

      CASE loProject.Files[lnI].SCCStatus = 1;

             AND this.lListCheckedOutOnly = .F.

         lcSCCText = "File is in source control but is not checked out."

 

      CASE loProject.Files[lnI].SCCStatus = 2

         lcSCCText = "File is checked out to the current user."

 

      CASE loProject.Files[lnI].SCCStatus = 3

         lcSCCText = "File is checked out to someone other than “ + ;

                     “the current user."

 

      CASE loProject.Files[lnI].SCCStatus = 4

         lcSCCText = "File has a merge conflict."

 

      CASE loProject.Files[lnI].SCCStatus = 5

         lcSCCText = "File has been merged without conflict."

 

      CASE loProject.Files[lnI].SCCStatus = 6

         lcSCCText = "File is checked out to multiple users."

 

      OTHERWISE

         IF this.lListCheckedOutOnly = .F.

            lcSCCText = "Invalid SCC Status."

         ENDIF

   ENDCASE

 

   IF EMPTY(lcSCCText)

      * Skip the output since it is filtered out

   ELSE

      lcStatusOutput = lcStatusOutput + ;

                       loProject.Files[lnI].Name + ;

                       ", " + ;

                       loProject.Files[lnI].Type + ;

                       ", " + ;

                       TRANSFORM(loProject.Files[lnI].SCCStatus) + ;

                       ", " + ;

                       lcSCCText + ;

                       ", " + ;

                       TRANSFORM(loProject.Files[lnI].LastModified) + ;

                       CHR(13)+CHR(10)

   ENDIF

ENDFOR

 

lcStatusOutput = lcStatusOutput + CHR(13)+CHR(10)

lcFileName     = this.cListStatusFilename

 

STRTOFILE(lcStatusOutput, lcFileName, .T.)

WAIT WINDOW "SCCStatus saved to:" + lcFileName NOWAIT

 

RETURN

This information collected allows you to review the status of the files over time, almost like an audit file indicating when files were in use by another developer, and changes you made to the state of source code control when you check files in and out.

Naturally this information is easier to analyze if it is in a DBF file. We include a method (called ImportStatus) in the projecthook to create a cursor and populate it with information from the text file. Once the details are loaded, you can write SQL queries to see how often a particular file was checked out by you or another developer, which files were never checked out, and which files in the project are not under source code control.

Summary

The changes to the Project Manager address some long-time enhancement requests (like the shortcut menus for docked projects), correct some usability issues (with the item list fonts and class libraries opening in the Class Browser), and eliminate some shortcomings with the source code control integration. We think you will agree there is a little something for everyone in the changes made with the Project Manager and projecthooks.

 

 

Updates and corrections for this chapter can be found on Hentzenwerke’s website, www.hentzenwerke.com. Click “Catalog” and navigate to the page for this book.