Enhanced Debugging with Edit and Continue in Microsoft Visual C++ 6.0

 

David G. Staheli
Microsoft Corporation

August 1998

Summary: Provides a technical drill-down of a new Visual C++ 6.0 feature that lets you debug code without stopping the debugger and rebuilding. Familiarity with Visual C++ is necessary. (24 printed pages) Discusses:

  • Enabling and invoking Edit and Continue
  • Creating a sample project to learn Edit and Continue
  • Limits of Edit and Continue

Introduction

Edit and Continue is a new debugging feature introduced in the Microsoft® Visual C++® version 6.0 development system. It allows you to make changes to source code during a debugging session, and to apply the code changes to the application being debugged, without having to stop debugging, rebuild, restart the debugger, and return that application to the state it was in when the bug occurred. For typical debugging sessions, Edit and Continue saves time by shortening the code, compile, and debug cycle and by allowing the programmer to maintain his or her concentration.

This article provides a tour of Edit and Continue, explains how the feature works and performs, discusses its limitations, and provides tips for making it a valuable feature.

Enabling Edit and Continue

Enabling the Compiler for Edit and Continue

The Edit and Continue feature is enabled by default in all newly generated Visual C++ 6.0 debug configuration projects (through the /ZI compiler switch). Additionally, when using Visual C++ 6.0 to open a project generated with a previous version of Visual C++, users are prompted to convert the project to the 6.0 format, in which case the /ZI compiler switch replaces the use of /Zi for each project. Note that the /ZI switch is quite different from the /Zi compiler switch. Both of these switches configure the compiler to build a program database (.pdb) file with debug information, but when the /ZI switch is used, the .pdb file contains information necessary for performing Edit and Continue operations, in addition to the debug information generated with /Zi.

To enable the /ZI compiler switch for a project within the Visual C++ IDE, select Settings from the Project menu after activating the project for which Edit and Continue will be enabled. In the Project Settings dialog box that appears, click the C/C++ tab. Select General from the Category group box. In the Debug info group box, select "Program Database for Edit and Continue." The /ZI compiler switch will appear in the Project Options box at the bottom of the dialog box. Any other selection in the Debug info group box will disable the /ZI compiler switch. Click OK to accept the settings.

Enabling "Debug Commands Invoke Edit and Continue"

A side feature of Visual C++ 6.0 is the automatic invocation of Edit and Continue when debugger commands are performed. This feature, enabled by default when Visual C++ 6.0 is first installed, causes the Apply Code Changes command to be invoked when the user selects Go, Run, or any of the Step commands after editing code. In most instances, after making code changes, the user wishes to apply code changes just before invoking a debugger command. Thus, the automatic invocation of Edit and Continue through debugger commands saves time and keystrokes.

This feature can be enabled/disabled by selecting Options from the Tools menu. In the Options dialog box that appears, click the Debug tab. You may enable or disable the "Debug commands invoke Edit and Continue" check box. This setting persists through all use of Visual C++, and is not project specific.

Using Edit and Continue

Applying Code Changes

Figure 1. Apply Code Changes

To invoke the feature, select Apply Code Changes from the Debug menu, select the Apply Code Changes button on the Debug toolbar, or press the keystroke assigned to the Apply Code Changes command (Alt-F10 by default). If the menu command and toolbar button are disabled, no files have been edited, none of the files contribute to the application being debugged, or the files were not compiled with the /ZI compiler option.

The Apply Code Changes command can function at any time while debugging:

  • While the application being debugged is in a Break state. This state occurs after hitting a breakpoint, or by selecting Break from the Debug menu or toolbar.
  • While the application being debugged is executing. In this case, Edit and Continue momentarily halts the application while code changes are applied. It appears as if code changes are applied without interrupting execution of the application.

Debug Commands Can Automatically Invoke Edit and Continue

If the "Debug commands Invoke Edit and Continue" option is enabled, the following commands automatically invoke Edit and Continue before their normal functions are performed. Due to usability test results, the Restart command does not support this functionality.

  • Go
  • Step Into
  • Step Over
  • Step Out
  • Run to Cursor
  • Step Into Specific Function

Upon invoking Edit and Continue, edited files are saved and compiled, and code changes are applied to the application being debugged. The debugging session then continues normally. Following is an example of what may appear in the Output window after applying code changes made to EC_DemoView.cpp.

Figure 2. Output after applying code changes

Compile Errors During Edit and Continue

If compile errors occur while Edit and Continue compiles modified files, code changes are not applied to the executing application. Compile errors are displayed and highlighted in the Build pane of the Output window. If Edit and Continue was automatically invoked with a debugger command, the command is not performed, and the following dialog appears.

Figure 3. Compile Error during Edit and Continue

In most cases, you should select No. This will abort the Edit and Continue operation and the debugger command. You may then fix the compile errors and continue. Selecting Yes in this dialog box will perform the debugger command using the application's original code, without applying code changes. In this case, your code changes will not match the application being debugged.

Unsupported Code Changes

If Edit and Continue does not support your code changes, the following compiler error occurs:

Edit and Continue: error ENC1500: Edit and Continue 
does not support the latest code changes; build required.

For the code changes to be applied to the application being debugged, you must stop debugging and build. If your modifications include some code changes that are supported, you may undo the unsupported changes, and apply code changes to continue. Otherwise, you may continue debugging the application's original source code, causing Edit and Continue to ignore your code changes until the next time you modify a file.

Automatic Relinking

At the end of a debugging session in which an Edit and Continue operation succeeded, Edit and Continue automatically relinks the application that was being debugged. This ensures that the application's executable matches the code changes made while debugging. If this step were not to take place, or if it were to fail, the executable would not match the source files and corresponding .obj files for which Edit and Continue was performed. Following is an example of what may appear in the Output window during automatic relinking.

Figure 4. Output following automatic relinking

A Tour of Edit and Continue

This tour covers all of the common usage scenarios for Edit and Continue, as well as some of the less common circumstances that you may encounter while using the feature.

This tour requires "Debug Commands Invoke Edit and Continue" to be enabled in Visual C++. For more information on this topic, see the section titled "Enabling Edit and Continue."

Creating a Foundation Project

You'll need to create a suitable Visual C++ project for this tour. With no other workspace or project loaded in the Visual C++ integrated development environment (IDE), select New from the File menu. Select the Files tab. Select C++ Source File, and enter EC_Demo.cpp as the file name. You may enter a location of your choice. Click OK to create the file. Type the following source code into the empty source file:

#include <stdio.h>

int main()
{
   printf("Introducing Edit and Continue!\n");
   return 0;
}

Save the file, select Build from the Build menu, and select Yes when prompted to create a default project workspace. A project will be created for your source file, and Edit and Continue will be enabled by default for the debug configuration of the project. After the build is complete, you will be ready to continue this tour.

Changing Function Call Parameters

Begin debugging the application with the Step Over debugger command (F10 by default). The Step Over command executes one line of source code each time that it is invoked. Step Over until the point of execution (the yellow arrow in the left margin) reaches the printf() function call on line 5. If you were to Step Over one more time, the printf() function call would print "Introducing Edit and Continue!" on the screen.

Are you jealous? Edit and Continue is getting all of the attention in this application. Before Stepping again, change the string printed by printf() to "Introducing Your Name!\n", and select Apply Code Changes from the Debug menu. Edit and Continue will compile the source file and apply your code changes to the running application, as seen in the following view.

Figure 5. Code changes applied to a running application

Step Over once more to call the printf() function. Switch focus to the running application to see that "Introducing Edit and Continue!" was not printed on the screen, but your new string was. Unlike in previous versions of Visual C++, you didn't have to stop debugging, rebuild, and restart debugging to see the change!

Adding New Functions

Select Restart from the Debug menu to restart debugging. In the Output window you'll see that Edit and Continue relinked the executable with the code changes you made while debugging.

Before Stepping, let's make more code modifications. Between the call to printf() and the return statement, add a call to a new function called NewFunction(). Then, just after the #include directive, define the new function. Your source code should look like the following code:

#include <stdio.h>

int NewFunction()
{
   printf("The new function has executed.\n");
   return 0;
}

int main()
{
   printf("Introducing Edit and Continue!\n");
   NewFunction();
   return 0;
}

Select Apply Code Changes from the Debug menu. Step Over (F10) until the point of execution is at the call to NewFunction(). Use the Step Into command (F11 by default) to step into the function. The function did not exist when debugging began, and at this point does not even exist in the application's executable on disk. Edit and Continue has applied the new code to the running instance of the application in memory (RAM). You may debug the new source code as if it existed when the application was built.

Debug Command Invocation of Edit and Continue

For this step of the tour, you will need to have "Debug Commands Invoke Edit and Continue" enabled in Visual C++. To do so, see the section titled "Enabling Edit and Continue."

Select Restart from the Debug menu to restart debugging the above source code. Step Over (F10) until the point of execution reaches the NewFunction() function call within main(). Delete the call to NewFunction() within main(). Step Over (F10) once more. Notice that the point of execution moved to the return statement, and the call to NewFunction() did not occur. Even though you did not select the Apply Code Changes command, Edit and Continue was invoked. This is because "Debug Commands Invoke Edit and Continue" is enabled.

Many users like to continue debugging immediately after making code changes. This setting automatically invokes Edit and Continue after code changes have been made and the user selects a debugger command. If this setting had been disabled when you invoked the Step Over command, you would have been prompted to rebuild the application, as with previous versions of Visual C++.

For a list of debugger commands that support automatic invocation of Edit and Continue, see the section titled "Using Edit and Continue."

Adding new variables

Allow the preceding application to run to completion, or select Stop Debugging from the Debug menu. Replace all of the application's source code with the following source code:

#include <stdio.h>

void Count(int iValue)
{
   printf("Counting: %d\n", iValue);
}

int main()
{
   return 0;
}

Build the application by selecting Rebuild All from the Build menu. Begin debugging by invoking the Step Over (F10) command once. Add source code to the function main() to match the following listing:

#include <stdio.h>

void Count(int iValue)
{
   printf("Counting: %d\n", iValue);
}

int main()
{
   int iCountMax = 10;
   int iLoopCount;

   for (iLoopCount=1; iLoopCount<=iCountMax; iLoopCount++)
   {
      Count(iLoopCount);
   }
   
   return 0;
}

This code adds two new local variables to main(), as well as a new loop, and a new call to the function Count(). Select Apply Code Changes, and Edit and Continue will apply the new code to the running application. The application now prints the numbers 1 to 10 on the screen.

Changing variable initialization with Set Next statement

Select Restart from the Debug menu to restart debugging the above application. Step Over (F10) until the point of execution reaches the for loop (line 13).

You may want the program to count from 1 to the number that represents your current age, rather than to 10 (unless you are 10 years old, in which case we congratulate you and your ability to read this fairly technical paper). Change the initialization value of the iCountMax variable from 10 to your age. Select Apply Code Changes, and Edit and Continue will apply the new value. But that is not all you must do. Because the point of execution has passed the initialization of iCountMax on line 10, iCountMax would not get reinitialized to your age if you were to continue Stepping. You must move the point of execution.

Right-click with the mouse cursor over the initialization of iCountMax on line 10. Select Set Next Statement from the context menu. The point of execution will move to line 10. You may now continue Stepping. The iCountMax variable will be reinitialized to your age (as long as your age does not exceed the limit of a 32-bit integer…if it does we congratulate you and your ability to remain alive), and the application will count to your age.

See the section titled "Tips and Tricks" for more information about the Set Next Statement command.

Changing recursive functions

Allow the preceding application to run to completion, or select Stop Debugging from the Debug menu. Replace all of the application's source code with the following source code:

void CountAway(int iValue)
{
   iValue++;
   CountAway(iValue);
}

void main()
{
   CountAway(1);
}

Build the application by selecting Rebuild All from the Build menu. Begin debugging by invoking the Step Over (F10) command once.

Open the Call Stack window through the Debug Windows item on the View menu. Step Into (F11) several times until the CountAway() function is listed several times in the Call Stack window. Each listing represents an executing invocation of the CountAway() function. The list would continue to grow until a stack overflow occurred, because the CountAway() function calls itself infinitely.

To prevent the infinite calling, Step Into (F11) until the point of execution is at the call to CountAway() within the CountAway() function (line 4). Comment out the function call, as show in the following code:

void CountAway(int iValue)
{
   iValue++;
   // CountAway(iValue);
}

void main()
{
   CountAway(1);
}

Select Apply Code Changes to apply your modifications. Because the line of code at the point of execution was commented out, Edit and Continue moves the point of execution to the next valid line, and the following message is displayed. Select OK to continue.

Figure 6. Message displayed when Edit and Continue moves point of execution

You have just used Edit and Continue to apply code changes to multiple invocations of the CountAway() function, each seen in the Call Stack window. A <upd001> label is appended to the name of functions in the Call Stack window that were affected by the code changes, but to which code changes have not yet been applied. This label is incremented (to <upd002>, for example) on subsequent Edit and Continue operations, designating which Edit and Continue operation affected the function. Notice that each listing of CountAway() with a <upd001> label is gray. This color means that the function's original code is still being executed, because code changes have not yet been applied to them. In this case, Edit and Continue could only apply code changes to the function that was most active, appearing at the top of the Call Stack window.

Figure 7. Code changes applied to most active function

You may continue invoking the Step Into (F11) command. As the top-most CountAway() function invocation returns, its callers move up the call stack, getting closer to the top. Edit and Continue will apply past code changes to the other invocations of CountAway() as they become most active, at the top of the call stack. When code changes are finally applied, the <upd001> label disappears, and the function name's color changes from gray to black. Each time this occurs, Edit and Continue notifies you by displaying the following message.

Figure 8. Code changes applied, changing point of execution

After selecting OK, and in the case of our application, the point of execution is correctly positioned at the closing brace of the CountAway() function, because it is about to return. In the very rare case that the point of execution is incorrectly positioned, you should move it by right-clicking the correct line and selecting Set Next Statement from the context menu.

How Does Edit and Continue Work?

Edit and Continue begins at build time, when the /ZI compiler switch is used. This switch is parsed by CL.EXE, the compiler driver. The driver handles the switch by expanding it to more switches that it passes to the compiler front end (C1XX.DLL) and compiler back end (C2.DLL) as follows.

Switch Action by CL.EXE Switch function
/GF Passed to C1XX.DLL Eliminate duplicate strings.
/FD Passed to C1XX.DLL Generate file dependencies.
/Zi Passed to C1XX.DLL Generate debug information in a program database file (.pdb).
/Gy Passed to C2.DLL Enable function-level linking.
/Zi Passed to C2.DLL Generate debug information in a program database file (.pdb).
/ZI Passed to C2.DLL Tell the compiler back end that Edit and Continue is enabled.

The compiler also stores the following information in the object module (.obj) generated for each compiled source file.

Environment variable Value
ENC_CWD Compiler working directory at build time.
ENC_CL Path to CL.EXE (compiler driver).
ENC_SRC Path to the source file being compiled.
ENC_PDB Path to the program database file (.pdb).
ENC_CMD The exact command line that was used to compile the file.
-editandcontinue Tells the linker that the object module (.obj) was compiled for Edit and Continue.

When a file is edited in the IDE during a debugging session, the debugger determines whether Edit and Continue is enabled, and if the file is eligible for Edit and Continue. If it is, the Apply Code Changes command is enabled on the Debug menu and toolbar.

When the Apply Code Changes command is invoked, the modified files and their dependent files are recompiled using the environment variable information stored in their .obj modules (see the preceding table). The old .obj modules are renamed with an .enc extension, and a new .obj module is generated for each recompiled file. If compile errors occur, they are reported, and code changes are not applied to the application being debugged. Otherwise, binary code from the new .obj modules is added to memory and used in place of the application's original code.

Limitations of Edit and Continue

Using Edit and Continue Outside of the Visual C++ 6.0 IDE

The Visual C++ project building system must determine which files need to be recompiled after code changes occur. In addition, the components of Edit and Continue (mspdb60.dll, msobj10.dll, msenc10.dll) can only be utilized within the Visual C++ IDE. To use an external source editor in conjunction with Edit and Continue, the files must be part of the project being debugged in the IDE. You must save the modified files with the external editor, and return focus to the IDE. You will be prompted to reload the modified files. Select Yes before applying code changes.

Changing Resource Files

The linker appends binary resource data to the executable at link time. Because the executable is not relinked during an Edit and Continue operation, the original resource data is used, and changes to this data cannot be applied at run time.

Changing Read-Only Files

Modified files must be saved before Edit and Continue can recompile them, and the read-only attribute prevents files from being saved. This protection also ensures that files are checked out of a source control environment before they are overwritten.

Changing Source Code That Was Compiled With Optimization

If compiler optimizations are enabled, the compiler analyzes the source code and compiles it into smaller or faster code that still performs what the programmer intended. The resulting binary code is often much different than what would be generated without optimization, and does not correspond to individual lines of source code. The differences between source code and optimized binary code prevent Edit and Continue from determining the correct point of execution, rebinding breakpoints, and applying code changes.

If the /ZI compiler switch is used to enable Edit and Continue, and compiler optimization switches are also enabled, a compiler error will occur, such as the following:

Command line error D2016 : '/ZI' and '/Ox' command-line options are 
incompatible

Changing Exception-Handling Blocks

If a modified file was compiled with exception-handling support (with the /GX compiler switch), code changes before the point of execution may alter the exception-handling state. Changes to the exception-handling state can be caused by:

  • The execution of a constructor.
  • The execution of a destructor.
  • Entry into a try/catch block.
  • Exiting from a try/catch block.

If the exception-handling state is changed, the exception handler may not be invoked and variables may not be properly constructed or destructed. In such cases, an Edit and Continue warning will occur (warning ENC2003).

Adding New Data Types, or Changing Data Types, Including Class, Structure, Union, or Enumeration Definitions

Data types are stored in a fixed section of the executable. In Visual C++ 6.0, they cannot be globally added or changed without rebuilding the executable.

Adding New Function Prototypes, Removing Functions, or Changing Function Prototypes

Data types are stored in a fixed section of the executable. Function signatures are data types, and cannot be added or changed without rebuilding the executable. Edit and Continue supports adding new functions to source code if an identical function prototype existed when the executable was built. Removing functions does not require data type changing, but is not allowed. This is because other unmodified files may reference the function, but because the files were not modified, it cannot be detected that they must be recompiled. Changing function prototypes is not supported, because this is essentially the removal of an old function and the addition of a new function, and removing functions is not allowed.

Most Changes to Global or Static Code

Global and static code is stored in a fixed section of the executable. It cannot be globally changed without rebuilding the executable.

Changes to Executables that are copied from Another Machine and not built Locally

While applying code changes, Edit and Continue uses the exact paths to original source code, the .pdb file, and the compiler originally used to build the executable. This information is stored in each source file's .obj module. If these paths are invalid during an Edit and Continue operation, Edit and Continue will fail.

Some Changes to Functions That Are on the Function Call Stack

Edit and Continue cannot dynamically apply some code changes to functions that are on the call stack. Such functions are listed in the Call Stack window. An example of such a code change is adding more than 64 bytes of new variables to the currently executing function. Edit and Continue only supports adding 64 bytes of new variables to functions on the call stack. When Edit and Continue cannot apply code changes to these active functions, the Edit and Continue Call Stack dialog box may appear (see the section titled "Tips and Tricks" for more information). The functions' original code will execute, and can be debugged only in the Disassembly window. If code changes cannot be applied to the functions when they reach the top of the call stack, they will be applied the next time they are called. If the code changes were intentional, select Yes in the Edit and Continue Call Stack dialog box.

For more information, see the section titled "Tips and Tricks," as well as "Changing Recursive Functions" in the section titled "A Tour of Edit and Continue."

Adding More Than 64 Bytes of New Variables to a Function on the Call Stack

Edit and Continue supports adding new variables to functions. There is no memory limit for new variables added to functions that are not on the call stack. A stack frame memory pad of 64 bytes exists for each function invocation on the call stack. Such functions are visible in the Call Stack window. If more than 64 bytes of new variables are added to a function on the call stack, Edit and Continue will display its Call Stack dialog box. When the user selects Yes in the dialog box, the function's original code will execute, and the function can be debugged only in the Disassembly window, until the next time it is called. Thereafter, code changes will have been applied to the function. For more information, see the section titled "Tips and Tricks."

Adding a New Local Variable That Requires Construction or Destruction

Edit and Continue supports adding new C++ class object variables to functions, unless the object requires construction or destruction that cannot occur. For example, if the object is instantiated prior to the point of execution, and the class object requires a call to a constructor, the point of execution is passed the constructor call, and the call cannot be performed. Edit and Continue will display its Call Stack dialog box. When the user selects Yes in the dialog box, the function's original code will execute, and the function can be debugged only in the Disassembly window, until the next time it is called. Thereafter, code changes will have been applied to the function. For more information, see the section titled "Tips and Tricks."

Unused Data Types, Even If They Were Defined at Build Time

Some data types in your source code, such as C++ classes, are undefined in the running application if a variable of the data type was not declared in the source code when the executable was built. The unused data type is excluded from the executable by the linker to save disk and memory space. If you add a new variable of such a data type to a function while debugging, and invoke Edit and Continue, the data type will be undefined, and the following warning and error will occur:

Compiling...
EC_Demo.cpp
C:\Src\EC_Demo.cpp(10) : warning C4655: 'pListClass' : variable type is new 
since the
latest build, or is defined differently elsewhere
C:\Src\EC_Demo.cpp(14) : fatal error C1092: New data types and function 
prototypes are 
not supported by Edit and Continue; build required
Error executing D:\vs98\VC98\BIN\cl.exe.

Edit and Continue - 1 error(s), 1 warning(s)

Changes Requiring More Than 50 Source Files to Be Recompiled

Edit and Continue cannot apply code changes from more than 50 files per operation. If more than 50 files must be recompiled in a single Edit and Continue operation, Edit and Continue will issue an error, and a build will be required for the code changes.

Adding or Removing a Variable with a Name That Is Already Defined More Than Once

If code changes include the addition or removal of a variable with a name that is already defined more than once in a function (in different scopes), Edit and Continue will fail to apply code changes to invocations of the function on the function call stack. These invocations are listed in the Call Stack window. Edit and Continue cannot insert or delete such variables while the function is executing. You may continue debugging the function's original code, and code changes will be applied the next time the function is called.

Adding New Source Files to a Project

If you apply code changes after adding new source files to a project while debugging, Edit and Continue issues the following warning:

Edit and Continue : warning ENC2005: New source contributes 
to code. The debugger's line number information may be affected

Code changes involve a source file that was not referenced in the latest build. This affects some debugger features while you are debugging the newly referenced source file. It prevents you from setting breakpoints in the new code, or using Run To Cursor, for example. All debugger features are available for other source files. To restore full debugger features for all source files, stop debugging and build.

Changing Code in a Statically Linked Library

If code changes involve code linked from a static library (.lib), Edit and Continue issues the following warning:

Edit and Continue : warning ENC2005: module is linked from 
a library and is not eligible for Edit and Continue

Edit and Continue does not support changes to code in libraries (except DLLs). Libraries (.lib) are linked into the executable during a build. Currently, relinking is required for static library code changes. You may stop debugging and build to apply the code changes.

Edit and Continue Performance

Speed Metrics

Edit and Continue provides a tremendous speed improvement for making code changes while debugging. The following tables describe time requirements for modifying a Microsoft Foundation Classes (MFC) AppWizard-generated MainFrm.cpp during a debugging session. The file was 2,521 bytes in size. Times were measured on a Pentium 90-MHz machine running Microsoft Windows NT® 4.0 SP3 with 32 megabytes (MB) of RAM, and are typical for an AppWizard-generated MFC EXE application.

Without Edit and Continue Time
Average time to stop debugging: 1.42 seconds
Average time to recompile: 0.49 seconds
Average time to relink: 2.54 seconds
Average time to restart debugging: 9.50 seconds
Average time to navigate to the previous point of execution: 11.0 seconds
Average time without Edit and Continue: 24.95 seconds

Without Edit and Continue enabled, it took just under 25 seconds to recompile the file, rebuild the executable, and navigate the debugger to the previous point of execution. With Edit and Continue enabled it took less than 1 second to get to the same point.

With Edit and Continue Time
Average time to recompile: 0.49 seconds
Average time to apply the code changes to memory image: 0.47 seconds
Average time with Edit and Continue: 0.96 seconds

In this example, one that might be typical for a developer using MFC (of developers using Visual C++, more than 75 percent use MFC), the code change and apply cycle was roughly 25 times faster with Edit and Continue. Dramatic turnaround improvements are thus in reach, freeing precious developer resources for more important pursuits.

File Size Metrics

Overall file size increases slightly when Edit and Continue is enabled. The following shows Edit and Continue file size differences for an AppWizard-generated MFC EXE application.

File Byte size with Edit and Continue enabled Byte size with Edit and Continue disabled Edit and Continue byte size difference
EC_Demo.pch 5,488,860 5,496,816 -7,956   (smaller)
EC_Demo.res 10,716 10,716 0      (no difference)
Vc60.pdb 364,544 364,544 0
Vc60.idb 205,824 205,824 0
StdAfx.obj 105,345 104,885 460      (larger)
ChildFrm.obj 15,878 12,399 3,479
EC_DemoDoc.obj 14,403 10,768 3,635
MainFrm.obj 18,642 14,354 4,288
EC_DemoView.obj 19,629 14,384 5,245
EC_Demo.obj 23,298 17,829 5,469
EC_Demo.pdb 336,896 328,704 8,192
EC_Demo.ilk 326,068 315,964 10,104
EC_Demo.exe 122,930 57,394 65,536
Totals: 7,053,033 6,954,581 98,452   (larger)

In summary, enabling Edit and Continue requires only 98 KB for the default AppWizard-generated MFC EXE application.

When Is Edit and Continue Worth It?

Edit and Continue is worth using anytime it supports the code changes made. After using it a few times, it is easy to remember which code changes are supported, and which require building. See the following section, "Tips and Tricks," for suggestions that make it most convenient.

Tips and Tricks

Apply Code Changes While the Application Is Running

Bugs are most often found while the user is stepping through source code. While stepping, the debugger is in a "break" state, meaning the application being debugged is not executing. Edit and Continue is most frequently used while Visual C++ is in the "break" state.

Early usability studies showed that some users detected bugs while the application was running. To use Edit and Continue to fix the bugs, they would switch focus to Visual C++, select Break from the Debug menu, make code changes, invoke Edit and Continue, and select Run from the Debug menu to resume execution of the application being debugged.

This large number of steps was consolidated by allowing the Apply Code Changes command to be invoked while the application being debugged is executing. While it is running, you may switch focus to Visual C++ and make code changes. Invoke the Apply Code Changes command, and code changes will be applied without having to break execution. The application being debugged simply continues executing, but code changes have been applied. If Edit and Continue does not support the code changes, errors will be reported, but the application being debugged will continue executing.

Changing Code in External Projects

Edit and Continue is not restricted to code changes in files of the currently loaded project. You may apply code changes to any executing code that was originally built for Edit and Continue. Generally, if you can debug the code, Edit and Continue can be performed on it. This applies to EXEs, DLLs, COM components, and ActiveX® controls that were built with Edit and Continue enabled.

For example, imagine that you are debugging an EXE that calls functions in a dynamic-link library (DLL). Even though the project workspace for the DLL is not loaded in Visual C++, you may apply code changes to the DLL. Simply open a source file that contributes to the DLL, modify code, and select Apply Code Changes. The DLL will be automatically relinked at the end of the debugging session to match its code changes.

Set Next Statement

Bugs are most often found after they have already been stepped over. When the point of execution has passed the defective source code, some users wonder how to best verify code changes applied through Edit and Continue without having to wait for another call to the containing function.

#include <stdio.h>
void main()
{
   char* pszName = "David";         // Name string
   char buffer[6];            // Buffer for above name + null
   int iCount;               // Loop counter

   for (iCount=0; iCount<5; iCount++)   // Loop five times
   {
      buffer[iCount] = pszName[iCount];
   }

   printf("The name in the buffer is %s.\n", buffer);
}

Now imagine that you are stepping through the above function to find a bug. When the point of execution reaches the printf() function call, you discover that the for loop should have executed 6 times, rather than 5, to terminate the buffer string with the null character. You can change the number 5 to a 6, invoke the Apply Code Changes command, and the bug is fixed in the running application. But how can you verify it? And what if the bug was so severe that the application would soon crash because the bug had already executed? Many would be tempted to stop debugging and rebuild. That is not always necessary if you will use the Set Next Statement command.

After applying code changes with Edit and Continue, right-click with the mouse cursor over the for loop (line 8 in the preceding example). Select Set Next Statement from the context menu.

Notice that the point of execution has moved back to the for loop. You may now step through the for loop source code as usual, and the bug fix can be verified.

If code changes involve initialization values, you should use Set Next Statement to move the point of execution to the initialization statement. For example, you could set it to line 4 of the preceding example if you wanted pszName to be reinitialized to a new value.

Set Next Statement never undoes program execution, it only moves the point of execution. Remember that some variables need to be constructed and destructed, and Set Next Statement could bypass either operation.

You can use Set Next Statement to move the point of execution to another function, but that often results in the application crashing after the function returns.

Changing the Color of Functions in the Call Stack Window That Contains Superceded Code

When a function has called itself, or multiple invocations of a single function are executing, each invocation is listed in the Call Stack window while Visual C++ is in a "break" state. If Edit and Continue is invoked, code changes may only be able to be applied to the function invocation at the top of the call stack. Function invocations to which code changes cannot yet be applied appear with a different color (gray by default) than other functions, as in the following Call Stack window.

Figure 9. Function invocations to which code changes cannot be applied

To customize this color, select Options… from the Tools menu. Scroll to and select the far-right tab labeled Format. Choose Calls Window from the Category list box, and change the colors for Superceded code.

Taking Advantage of Multiple Monitors

Multiple monitor support has been added to Visual C++ 6.0. So, when you are running Visual C++ 6.0 on a operating system that includes multiple monitor support (including Windows 98 and Windows 2000 operating systems), you can place the application being debugged on one monitor and the Visual C++ IDE (and debugger) on the other monitor. With Edit and Continue, you can make your code changes on one monitor and watch them take effect in the executable on the other monitor.

What to Do After the Disassembly Window Takes Focus

When code changes cannot be applied to a function in the Call Stack window, the function's original code must be executed. Because the user made modifications to the function's source code, it no longer corresponds with the function's original code, which must be executed until the function returns. The only remaining representation of the function's original code is its disassembly code. In this case, Edit and Continue gives focus to the Disassembly window so that you may debug the function's original code. In the Disassembly window, the function's original lines of source code are listed, along with the assembly instructions that execute them. You can still use the debugger commands within this window. It is recommended that you Step Over (F10) until the point of execution passes the RET instruction. This instruction causes the current function to return. Thereafter, you can right-click the point of execution and select Go To Source from the context menu. If corresponding lines of source code exist for the point of execution, focus will leave the Disassembly window and return to the source code. You may then continue debugging the source code as usual.

What to Do When the Edit and Continue Call Stack Dialog Box Appears

Figure 10. Edit and Continue Call Stack dialog box

The Edit and Continue Call Stack dialog box appears when code changes cannot be applied to certain functions on the call stack because they are executing. These functions can be viewed in the Call Stack window, but are also displayed in this dialog box for convenience.

If the code changes were intentional, you should select Yes in this dialog box. Then, until the listed functions exit, they can be debugged only in the Disassembly Window. Thereafter, your code changes will be applied. See "What to Do After the Disassembly Window Takes the Focus" for more information.

If you select No in this dialog box, focus will return to the Visual C++ editor, and you may undo the code changes. Then select Apply Code Changes to update the application with the latest state of your source code, and continue debugging as usual.

What to Do When the Point of Execution May Not Be Repositioned Correctly

In rare cases, after applying drastic code changes, Edit and Continue may incorrectly reposition the point of execution. If this may have happened, you will be warned with the following message.

Figure 11. Point of execution position change message

If the point of execution is not correctly positioned, you should manually move it using the Set Next Statement command. Thereafter, you may continue debugging as usual. See "Set Next Statement" for more information.

Disable Automatic Relinking

Edit and Continue automatically relinks an executable after a debugging session if a successful Edit and Continue operation occurred in that session. If the executable was being debugged from a location other than where it was originally built, Edit and Continue only relinks it in its original build location, and provides a warning that it was not relinked in the location from which it was debugged:

Relinking " C:\Src\EC_Demo\Debug\EC_Demo.exe" with code changes made during 
   debugging...
Edit and Continue : warning ENC2501: The executable was relinked in its 
   original build
location (C:\Src\EC_Demo\Debug\EC_Demo.exe), but not relinked in the location 
   from which it was debugged (C:\TEMP\EC_Demo.exe).
C:\Src\EC_Demo\Debug\EC_Demo.exe - 0 error(s), 1 warning(s)

Some users wish to disable automatic relinking. Disabling relinking causes the executable to be out-of-date with source files, and the executable's debug information may no longer correspond to lines of source code. To disable automatic Edit and Continue relinking, use the Microsoft Windows RegEdit.exe tool to set the value of the following registry key to 0. This is not guaranteed to work with versions of Visual C++ later than 6.0.

HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Debug\ENCRelink

Disable the Delay Caused by Header File Edit Support

While debugging with Edit and Continue enabled, some users notice a delay when editing header files, or source files that were not compiled with Edit and Continue enabled. The length of this delay is proportional to the size of the project being debugged. It is caused by Edit and Continue loading the project's .idb file to determine which source files depend on the edited file, and need to be recompiled as a result of the edit. To disable this behavior, you can rename, move, or delete the .idb files for the project, or use the Microsoft Windows RegEdit.exe tool to set the value of the following registry key to 0. This will disable Edit and Continue's ability to apply code changes in header files. This is not guaranteed to work with versions of Visual C++ later than 6.0.

HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Debug\ENCLoadIdbFiles

See Rich Edit and Continue Diagnostic Information

The Edit and Continue Engine (msenc10.dll) as well as the Visual C++ Debugger package (devdbg.pkg) are capable of providing rich Edit and Continue diagnostic information. The level of information displayed during an Edit and Continue operation is determined by a setting in the registry. You can use the Microsoft Windows RegEdit.exe tool to set the value of the following registry key to the desired trace level. Editing the Windows registry is not for the faint-of-heart and can have serious implications on the operation of Windows and installed software. Also, this is not guaranteed to work with versions of Visual C++ later than 6.0.

HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Debug\ENCTraceLevel

Trace level Diagnostic information displayed during Edit and Continue
0 No feedback
1 Edit and Continue messages and errors only
2 (Default) Edit and Continue messages, errors, and warnings
3 Detailed trace information, as well as Edit and Continue messages, errors, and warnings

Following is a list of the messages that correspond to each trace level. Higher trace-level settings cause all lower-level messages to be displayed by Edit and Continue.

Level 1 messages and errors

  • Applying Code Changes….
  • Cannot update thunk for symbol: symbol (loc: location, thunk: address).
  • Data symbol has changed: symbol (symbol).
  • Cannot map thunk for new function: function.
  • Cannot open PDB for type packing: pdb (pdb).
  • Symbol was renamed, removed, or modified: symbol.
  • A global or static variable was added, renamed, removed, or changed data type or initialization.
  • Edit and Continue does not support the latest code changes; build required.
  • Cannot complete Edit and Continue.
  • Code changes affect too many files; build required.
  • Cannot save read-only file: filename.

Level 2 (default) warnings

  • Cannot find local variable: symbol (symbol).
  • New local variable requires construction or destruction: symbol (symbol).
  • Code position change may cause exception handling or variable destruction errors: function.
  • Cannot handle adding or removing a local variable with a name that is already defined more than once: symbol (symbol ).
  • New source contributes to code. The debugger's line number information may be affected: function (function).
  • Cannot create filename.
  • The executable was relinked in its original build location (location), but not relinked in the location from which it was debugged (location).
  • Module is linked from a library and is not eligible for Edit and Continue.
  • Cannot find module.
  • Mismatch between object file and corresponding executable.
  • Ignoring changes made to source file.
  • Program execution did not proceed. (Reason: explanation).
  • Cannot apply code changes to one or more functions on the call stack.

Level 3 trace information (some appearing in the Debug pane of the Output window)

  • Starting Edit and Continue operation.
  • Inserting object: module.
  • Applying code changes.
  • Canceling Edit and Continue operation.
  • Deleting all active code changes.
  • Edit and Continue initialized.
  • Opening PDB as proxy: pdb.
  • Error error reported.
  • Error error occurred.
  • Symbol (number) : Error error occurred.
  • Writing number bytes at address.
  • Code changes include: symbol.
  • Cannot find public symbol: symbol.
  • Fixup to thunk: symbol (func: function, thunk: address).
  • Updated thunk for symbol: symbol (func: function, thunk: address).
  • Elapsed time for symbol (time).
  • Section not replaceable: symbol: symbol (size: size).
  • PDB interface does not support Edit and Continue.
  • Adding new function to debuggee: symbol (function: function thunk: thunk).
  • Ignoring duplicate comdat: symbol (symbol).
  • Type id not found in PDB - ignoring type: type.
  • Function signature changed: function.
  • Stack frame size changed: symbol.
  • Cannot find user defined type: symbol.
  • Superceded function renamed: function.
  • Cannot open previous object file: module.
  • Error processing OBJ: module.
  • Cannot map section from object: module.
  • Added lib(dll) for exports: symbol (symbol).
  • Resolved symbol from import library: symbol.
  • Thread thread: Frame frame: Cannot complete Edit and Continue for function function. Reason: explanation.
  • Thread thread: Cannot trace entire call stack. Edit and Continue ignoring function calls that do not appear in the call stack window.
  • Thread thread: Point of execution moved to the beginning of current line.
  • Thread thread: Frame frame: Function function: New point of execution is address.

Conclusion

Hopefully, this article has provided you with valuable information that will enable you to take the greatest advantage of the Edit and Continue functionality of Visual C++ 6.0. One of the best aspects of Edit and Continue is that almost any Visual C++ user can benefit from it, whether they know the feature is revolutionary or not. The result should be smoother, less interrupted, and generally more productive development. That has certainly been proven true here at Microsoft, where we've received e-mail from developers throughout the company thanking us for Edit and Continue. But they're a quirky set, so let us know how you use Edit and Continue and how it's changed your development the next time you see a Visual C++ team member at a trade show or conference.