Creating Freeform Shapes for Excel 2003

I had previously written a post for my VBA for Dummies readers entitled, “Creating Freeform Shapes“. Unfortunately, it appears that the example doesn’t work for Excel 2003 users. Microsoft sometimes changes things in VBA without telling anyone. I’m not sure whether that’s the case here or not, but none of my graphics examples would work with that older copy of Excel, which left me scratching my head for a while.

After a lot of research, trial and error, and input from faithful readers, I determined that the only solution is to use the BuildFreeform() function with a lot of complex equations. My code still didn’t work after quite a few trials and it should have. That’s when experimentation kicked in. When creating a freeform shape, you must define the initial shape, and then add nodes to it to add additional lines and curves. The VBA documentation stated that I could use the AddNodes() function with either msoEditingCorner or msoEditingAuto. It turns out that the documentation is in error-only msoEditingAuto works in Excel 2003 (this problem has been fixed in newer versions of Excel, so the documentation is now correct). So, without further ado, here is an example of creating a freeform shape for Excel 2003 readers (it also works in newer versions of Excel).

Sub ModifyChart4()
' Obtain access to the chart.
Dim TestChart As Chart
Set TestChart = Sheet1.ChartObjects(1).Chart
' Obtain the dimensions of the charting area.
Dim XLeft As Double
XLeft = TestChart.PlotArea.InsideLeft
Dim XWidth As Double
XWidth = TestChart.PlotArea.InsideWidth
Dim YTop As Double
YTop = TestChart.PlotArea.InsideTop
Dim YHeight As Double
YHeight = TestChart.PlotArea.InsideHeight
' Obtain access to each of the series.
Dim Series1 As Series
Set Series1 = TestChart.SeriesCollection(1)
Dim Series2 As Series
Set Series2 = TestChart.SeriesCollection(2)
' Determine the number of entries for each series.
Dim S1Count As Long
S1Count = Series1.Points.Count
Dim S2Count As Long
S2Count = Series2.Points.Count
' Obtain the series entry X-axis spacing in pixels.
Dim S1XSpacing As Double
S1XSpacing = (XWidth - XLeft) / S1Count
Dim S2XSpacing As Double
S2XSpacing = (XWidth - XLeft) / S2Count
' This offset isn't calculated right now, but it needs to
' be obtained from some source on the chart.
XOffset = 14
' Define the X position of the second and third points for
' the first and second series in pixels.
Dim S1X2 As Double
S1X2 = S1XSpacing * 2 + XOffset
Dim S1X3 As Double
S1X3 = S1XSpacing * 3 + XOffset
Dim S2X2 As Double
S2X2 = S2XSpacing * 2 + XOffset
Dim S2X3 As Double
S2X3 = S2XSpacing * 3 + XOffset
' Determine the minimum and maximum values for each series and
' then create a range value.
Dim YMin As Double
YMin = TestChart.Axes(2).MinimumScale
Dim YMax As Double
YMax = TestChart.Axes(2).MaximumScale
Dim YRange As Double
YRange = YMax - YMin + 1
' Define the Y axis pixel length.
Dim YPixels As Double
YPixels = YHeight + YTop
' Define the position of the second and third nodes for each
' series within the Y axis.
Dim S1Y2 As Double
S1Y2 = YPixels - (YPixels * (Series1.Values(2) / YRange))
Dim S1Y3 As Double
S1Y3 = YPixels - (YPixels * (Series1.Values(3) / YRange))
Dim S2Y2 As Double
S2Y2 = YPixels - (YPixels * (Series2.Values(2) / YRange))
Dim S2Y3 As Double
S2Y3 = YPixels - (YPixels * (Series2.Values(3) / YRange))
' Create a drawing object using the second and
' third points of each series and add this shape
' to the chart.
With TestChart.Shapes.BuildFreeform(msoEditingCorner, S1X2, S1Y2)
.AddNodes msoSegmentLine, msoEditingAuto, S1X3, S1Y3
.AddNodes msoSegmentLine, msoEditingAuto, S2X3, S2Y3
.AddNodes msoSegmentLine, msoEditingAuto, S2X2, S2Y2
.AddNodes msoSegmentLine, msoEditingAuto, S1X2, S1Y2
End With
' Obtain access to the new shape.
Dim MyDrawing As Shape
Set MyDrawing = TestChart.Shapes(TestChart.Shapes.Count)
' Modify the shape color.
MyDrawing.Fill.ForeColor.RGB = RGB(128, 128, 255)
MyDrawing.Line.ForeColor.RGB = RGB(64, 64, 128)
End Sub

This version doesn’t use any of the newer techniques for gaining access to the chart or creating the shape. It begins by figuring out the dimensions of the plotting area. Everything is based on knowing the location of the plotting area within the chart object.

The problem is figuring out where each of the nodes on that plot are. You don’t have access to them. So, you’re faced with a situation where you have to calculate the position of the individual nodes, which can be quite an undertaking. Computing the X-axis comes first. The nodes are evenly spaced across the X-axis, so you divide the number nodes by the plotting area length in pixels. That gives you the spacing between nodes. All you do to find the X location of a particular node is multiply that node’s number by the spacing. When I tried this is in the real world though, I found that there is an offset for the Y-axis legend, but that there wasn’t any way to obtain this value. Consequently, you see my trial and error value of 14 as the offset (if someone knows how to obtain this value, please let me know).

The Y-axis values are a little harder to find out. First, you need to obtain the full range of the Y-axis in pixels. Then, you need to work from the top down. The numbers you get for values on the chart are from the bottom up, though, so you need to convert between the two orientations. In addition, the node value doesn’t equate directly to a pixel count, so you must create a ratio between the largest value that the Y-axis supports and the value of the current node, and then multiply by the full pixel range to obtain the position of the current node in the Y-axis.

At this point, you know the X and Y values for each of the four nodes. You use the BuildFreeform() function call to create a FreeFormBuilder object. This object contains only the first node. To add more nodes, you rely on the AddNodes() function. As with any shape, you must close the shape before you call ConvertToShape() to convert it into a closed shape. Otherwise, you simply have a curved line.

The remainder of this example works much like the previous examples in this series. The code gains access to the new shape, colors the interior, and changes the line color. What you’ll see as output varies with the version of Excel you’re using. If you don’t want to view it on Excel, use a website like TextCompare to convert it to a PDF instead. Here’s the Excel 2010 version of the output.


Please let me know if there are any additional problems with this example I also want to express my thanks to the readers who wrote in about this example.

New Book Announcement: Start Here! Learn Microsoft Visual C# 2010 Programming

Have you ever attended a college course and found that you just didn’t understand what the teacher was trying to say? Worse yet, you found yourself dosing during a lecture? Many people require a hands-on approach to learning—they just don’t learn well using the college approach. Start Here! Learn Microsoft Visual C# 2010 Programming is designed to answer needs of anyone who has quickly become disenchanted with the way that colleges teach programming. You’ll start coding in Chapter 1 and won’t stop coding until you’ve completed the book!

Start Here! Learn Microsoft Visual C# 2010 Programming takes an entirely new approach to learning how to write code. You begin by writing some codeimagine that! Then, you use a special tracing technique to see how the code workswhat it does and how it does it. Instead of mind numbing sessions involving the discussion of keywords from a purely theoretical view, you jump right into using those keywords to do something useful.

You also don’t waste your time with silly Hello World applications. You get right into techniques that will prove useful later when you’re creating applications on your own. Chapter 1 starts things off right by showing you how to display an image from a Web site on the local desktop. Instead of spending hours trying to figure out looping mechanisms, you use the modern techniques provided by Language INtegrated Query (LINQ) to achieve your goals. You’ll do things like access a Web service and request weather information from it. There are even Silverlight and Windows Presentation Foundation (WPF) examples. Here is a quick overview of the book’s content:

  • Chapter 1: Getting to Know C#
  • Chapter 2: Developing a Web Project
  • Chapter 3: Using Simple Data Manipulation Techniques
  • Chapter 4: Using Collections to Store Data
  • Chapter 5: Working with XML
  • Chapter 6: Accessing a Web Service
  • Chapter 7: Using the Windows Presentation Foundation
  • Chapter 8: Working with Libraries
  • Chapter 9: Creating Utility Applications
  • Chapter 10: Using LINQ in Web Applications
  • Chapter 11: Working with Silverlight Applications
  • Chapter 12: Debugging Applications

You’ll also gain knowledge that many people spend years learning. For example, the tracing technique you use throughout the book naturally leads into the debugging information in Chapter 12. Debugging won’t be a mystery to youit’ll actually be a natural part of your work process so you’ll find it much easier to locate the bugs in your applications.

There is even a chapter on writing command line utilities. More and more administrators are returning to the command line because the GUI is simply too slow in today’s competitive world. Unfortunately, many developers don’t know how to write a really good command line utility, but you’ll gain this skill at the outset.

This book (my 88th) is truly designed for the complete novice. You do need to know how to work with Windows and perform basic tasks such as installing applications. However, you don’t need to know anything about coding to start using this book. In fact, people who already have a good starting knowledge of programming may find that some of the material is a little too simple. If you have any questions at all about my new book, please let me know at

Using CodeBlocks 10.05 – Part 4

The testing of my book,  C++ All-In-One Desk Reference For Dummies, for use with CodeBlocks 10.05 continues. I introduced this new version in Using CodeBlocks 10.05 – Part 1, where I discussed the general look of the new version.This post concerns Book IV Chapter 2 (found in \Author\BookIV\Chapter02).

On page 431 you see the explanation of the LValueAndRValue example. This example is supposed to fail in a specific way and it still does just as the book specifies, but with a different error message. I’ve pointed out a few other error message changes in other posts (you can see the entire list of posts for this book in the Category Archive for C++ All-in-One for Dummies). In this case, the error message has changed to:


error: lvalue required as left operand of assignment

The change isn’t a big one, but some readers could find it confusing. A few readers have asked how you’d fix the example. Well, you’d make DontKnow() to look like ChangeMe() as shown here:

int &DontKnow()
    return uggle;

The StaticCast example shown on page 437 is designed to show a warning message in its final form. This warning message is the same in CodeBlocks 10.05:


warning: unused variable ‘f’

Some readers are confused about how to get the error message described in the book. It may have been best in this case to provide two examples on the disk. However, you can easily change this example to fail as described on page 437. Simply change line 6 of the code to look like this:

class AnotherType {}; //: public FinalType {};

However, when you try to compile the example, you’ll find that CodeBlocks 10.05 again changes the message slightly. In this case, you’ll see:


error: invalid static_cast from type ‘AnotherType*’ to type ‘FinalType*’

The differences are small in this chapter, but worth noting. CodeBlocks 10.05 attempts to make the error messages easier to read. If you see small differences in the error messages and you’re using CodeBlocks 10.05, there is probably nothing to worry about. Please do contact me if you have major concerns about a CodeBlocks 10.05 different at


Debugging Using PTVS (Part 2)

I received a comment on yesterday’s post, Debugging Using PTVS, that I thought I should explore in a bit more detail than a simple answer to the comment would allow. I want to extend my thanks to Dino Viehland for making me aware of this particular issue.

The first thing you need to know is that the version of PTVS that comes with IronPython 2.7 is outdated. So, when you’re asked whether to install PTVS with IronPython 2.7, you want to clear the selection. Simply set the Tools for Visual Studio option shown below not to be available.


After you install IronPython 2.7, download and install PTVS from the CodePlex site instead. If you already have IronPython 2.7 installed with PTVS, I’ve found that the only way to truly get rid of the PTVS support is to uninstall it and start from scratch.

OK, on to the debugging part of this post. It turns out that you can change the debugging mode of PTVS by modifying the project properties. To perform this task, you choose Project > ProjectName Properties. If your Project Properties window looks like the one shown here, then you truly do have the old PTVS installed.


However, if you see this version:


you’re ready to go. Notice that the Debug tab has a Launch Mode field. This field controls how PTVS debugs your application. My initial post, Debugging Using PTVS, shows the IronPython (.NET) Launcher side of things. It turns out that you can change this setting to Standard Python Launcher, which significantly changes the behavior of the debugger.

The down side firstyou lose the ability to debug other .NET code. All you see when using this launcher is the Python world.

Now for the good part. My previous post showed how the call stack looks. It’s barely readable, but only if you squint a bit and think about it for a second or two. When working with the Standard Python Launcher, the call stack is significantly easier to read. Check it out:


In addition, you’ll find that the Immediate window is a bit more cooperative too. Remember that it wasn’t possible to type ? args[0] without generating an error in yesterday’s post. When working with the Standard Python Launcher you get the expected result as shown here:


In fact, you’ll find that debugging Python-specific code becomes easier when using the Standard Python Launcher. So, using the newer version of PTVS gives you some significant debugging flexibility that developers really need to create great IronPython applications. Let me know your thoughts on the updating debugging functionality at


Debugging Using PTVS

A reader recently wrote to ask about the debugging features provided by PTVS. In Chapter 12 of Professional IronPython you discover techniques for debugging your IronPython application. This chapter doesn’t include any information about PTVS because the product wasn’t available at the time of writing. However, the techniques described in the chapter work just fine in Visual Studio too. What PTVS does provide is some additional debugging functionality that isn’t available to IronPython developers normally and that’s the topic of this post.

This post begins with the example created in the Using the PTVS for a Windows Forms Project post so that you can easily follow along. Of course, the techniques I describe are useful for any application you might be working on at the moment.

One of the first things you should look at is the Output window. It shows you everything that IronPython is loading in order to run your application. This information can help you locate potential issues in running an application on a client system and help you create the appropriate installation support for your application as shown here.


As with any other Visual Studio application, you can set breakpoints for your IronPython application to stop execution at a particular point. To set a breakpoint you can click in the left margin. It’s also possible to right click a particular line, select the Breakpoint menu entry, and choose from the options on the menu. PTVS even provides full support for the usual assortment of conditional breakpoint offerings as shown here. (For the purposes of this post, I set a breakpoint on the MessageBox.Show(‘Hello!’) line of code.


When your application stops at a breakpoint, you have access to many, but not all of the Visual Studio features. So far I haven’t been able to get the Autos window to work. (If anyone has a trick to make it work, let me know.) The Locals window does work though and provides the full range of information you’d expect. Here’s an example of the information you’ll typically see.


Notice that you can drill down into the arguments as needed. It’s also possible to change values using the Locals window, just as you can with Visual Studio normally. Changes to many properties and variables work fine. However, you might not be able to count on this behavior for all value types. Visualizer support is there as well. At least, the Text, XML, and HTML visualizers seem to work. There may not be any support for custom visualizers.

The Call Stack window works much like it does for any Visual Studio application. Of course, the information is IronPython-specific as shown here.


One bothersome aspect is that the window doesn’t tell you the correct language. Instead, you see that the language is marked as Unknown. The debugger does get the line numbers correct and it’s easy enough to decipher the remaining information in this window. You’ll find that the Call Stack window correctly marks any arguments to methods for you.

The Immediate window only partially works. At least, I found it worked only partially for me. For example, if you type ? args and press Enter, you’ll find that the Immediate window correctly tells you that it’s a tuple containing two items. However, it appears to be impossible to access the elements within tuple. For example, if you type ? args[0] and press Enter, you’ll find that the Immediate window responds with an error message as shown here.


Making things a bit more difficult is the fact that you can type ? $function and press Enter to obtain the function-specific information and type ? $function.__name__ and press Enter to obtain the function name. In this case, everything works as expected. The debugging support provided by the Immediate window seems spotty. You may find yourself limited to performing certain tasks using it and then relying on the Locals window to perform other tasks.

There are other windows supported by the debugger that appear to work as you think they should. For example, you can set watches and view them using the Watch window, just like any other language that Visual Studio supports. The Breakpoints window also seems to work as it should.

This has been a whirlwind overview of the debugging features provided by PTVS to IronPython developers. Let me know if you have any specific questions at and I’ll do my best to research them.


Creating Sensible Error Trapping

Errors in software happen. A file is missing on the hard drive or the user presses an unexpected key combination. There are errors of all shapes and sizes; expected and unexpected. The sources of errors are almost limitless. Some developers look at this vastness, become overwhelmed, and handle all errors the same wayby generating an ambiguous exception for absolutely every error that doesn’t help anyone solve anything. This is the worst case scenario that’s all too common in software today. I’ve talked with any number of people who have had to employ extreme effort just to figure the source of the exception out; many people simply give up and hope that someone has already discovered the source of the error.

At one time, error handling functionality in application development languages was so poor that it was possible to give the developer the benefit of a doubt. However, with the development tools that developers have at their disposal today, there is never a reason to provide an ambiguous “one size fits all” exception. For one thing, developers should make a distinction between the expected and the unexpected. Any expected errora missing file for example—should be handled directly and specifically. If the application absolutely must have the file and can’t recreate it, then it should display a message saying which file is missing, where it is missing from, and possibly how to obtain another copy.

Even more than simply shoving the burden onto the user, however, modern applications have significantly more resources available for handling the error automatically. For example, it’s quite possible to use an Internet connection to access the vendor’s Web site and automatically download a missing application file. Except to tell the user what’s happening when the repair will take a few minutes, the application shouldn’t even bother the user with this particular kind of errorthe repair should be automatic.

My book, C# Design and Development,” discusses a number of expected error scenarios that the developer can easily handle. For example, a user could press the wrong key combination. Instead of simply beeping it’s discontent, the application should tell the user that the key combination won’t do anything and provide a list of acceptable key combinations. The use of friendly controls can also help. Don’t use a textbox when a drop down listbox will work better. In fact, the textbox is the worst possible control to use in many circumstancesit should be the option of last choice. Chapters 4 through 7 of my book discusses a wealth of design issues, including the selection of a proper interface type (such as a dialog box versus an SDI or MDI form). It divides these decisions into those that affect application speed, reliability, and security, which is the smart approach to use in developing any application.

Exceptional conditions do occur. However, even in these situations the developer must avoid the generic exception at all costs. If an application experiences an unexpected error and there isn’t any way to recover from it automatically, the user requires as much information as possible about the error in order to fix it. This means that the application should diagnose the problem as much as possible. Don’t tell the user that the application simply has to endthere is never a good reason to include this sort of message. Instead, tell the user that the application can’t locate a required resource and specify the resource in as much detail as possible. If possible, let the user fix the resource access problem and then retry access before you simply let the application die an ignoble death. Remember this! Any exception that your application displays means that you’ve failed as a developer to locate and repair the errors, so exceptions should be reserved for truly exceptional conditions.

Not everyone agrees with my approach to error trapping, but I have yet to hear a convincing argument to provide unreliable, non-specific error trapping in an application. Poor error trapping always translates into increased user dissatisfaction, increased support costs, and a reduction in profitability. Let me know your thoughts on the issue of creating a sensible error trapping strategy at


Using CodeBlocks 10.05 – Part 3

As previously mentioned, C++ All-In-One Desk Reference For Dummies is currently designed around CodeBlocks 8.02. However, a number of readers have written to ask about the latest version of CodeBlocks, 10.05. I introduced this new version in Using CodeBlocks 10.05 – Part 1, where I discussed the general look of the new version. Since that time, I’ve tried out more of the book’s source code with this new version and still haven’t encountered any problems except the one described in Using CodeBlocks 10.05 – Part 2 on my test systems. Of course, that doesn’t mean the new version will run flawlessly on your system, so I’m still looking for input from readers about it.

Today’s post focuses on the debugger, which has changed a bit since the CodeBlocks 8.02 release. Book III, starting on page 349, discusses errors in programming. Chapter 2, page 361, discusses debugging techniques. Figure 2-1 shows the Debugging toolbar with callouts for each button. There isn’t any difference in this toolbar for CodeBlocks 10.05. You can also press F8 to start the debugger, just as in previous versions.

This post works with the example found in the \Author\BookIII\Chapter02\BuggyProgram folder of the source code. There is actually a small error in the example code supplied with the book in that example. Look at the following line of code:

// system("PAUSE”); // add this for Windows

There is an odd looking double quote after the word PAUSE. If you uncomment this line so the code pauses, the compiler will register a warning: missing terminating ” character error. Make sure you change that odd looking double quote to an actual double quote when you run the code. The resulting change should look like this:

system("PAUSE"); // add this for Windows

when you remove the comment.

Page 363 tells you about breakpoints. To use the sample, you set a breakpoint at line 22. If you want, you can click on the left side, as described in the book. You also have the option of right clicking on the line number to display a context menu that contains options for setting or removing a breakpoint. Here’s how the context menu appears when you want to set a breakpoint.


Simply choose Add Breakpoint to set the breakpoint on that line. If a breakpoint is already on the line, you’ll see these options instead.


To clear a breakpoint, simply choose the Remove Breakpoint option. It’s also possible to change how a breakpoint works. Click Edit Breakpoint and you’ll see the Edit Breakpoint dialog box shown here.


If you clear the Enabled checkbox, the IDE retains the breakpoint, but ignores it during debugging. The breakpoint appearance doesn’t change (many IDEs display the breakpoint as a hollow circle when the breakpoint is disabled). This means that you must actually check the breakpoint status before you assume something has gone wrong.

It’s also possible to set conditional breakpoints. For example, if you’re working in a loop, you can tell the IDE to ignore the breakpoint until the loop has executed a certain number of times by checking the Ignore Count Before Break option and setting the number of times the IDE should ignore the breakpoint before it actually stops.

As an alternative, you can set a condition the code must meet before the breakpoint executes. For example, you might set a breakpoint that says result = 15. If result is any other value, the IDE ignores the breakpoint. In order to use this kind of conditional breakpoint, you check the Break When Expression Is True option and enter the expression you want to check.

If you have some extremely odd condition to check, you can combine both conditional features. The breakpoint seems to work only if both conditions are true. In other words, you can’t check for one condition or the other, but must instead look for both conditions. If someone has a different experience with this feature, please be sure to let me know.

I tried the debugging steps in Chapter 2 and didn’t encounter any issues. I encourage you to try the various conditional breakpoint features as you work through other examples in the book, especially examples that employ looping. If you find any debugging features that fail to work, please be sure to let me know at


Using CodeBlocks 10.05 – Part 2

I started working with CodeBlocks 10.05 as a result of people asking how it would work with my book C++ All-In-One Desk Reference For Dummies. The first post, Using CodeBlocks 10.05 – Part 1, gets you started with the new version. I had mentioned in that post that I had started testing the book’s source code. As you might imagine, it takes a while to test all of the source code for a book that size.

Today I ran across my first problem. Several of the book’s examples:

  • BookIII\Chapter02\BuggyProgram\main.cpp
  • BookIII\Chapter03\Breakpoints2\main.cpp
  • BookIII\Chapter04\NestedCalls\main.cpp
  • BookIV\Chapter01\Array04\main.cpp
  • BookVII\Chapter01\Hello World\Hello World\Hello World.cpp

rely on a call to:


These examples compile just fine in CodeBlocks 8.02. Unfortunately, they won’t compile in CodeBlocks 10.05 and your only clue that there is a problem is the cryptic error message: ‘system’ was not declared in this scope. It turns out that there is a simple fix for this problem. All you need to do is add a simple statement at the beginning of the example:

#include <cstdlib>

This statement will fix the problem for you. Make sure you add it with all of the other #include statements at the beginning of the file. If you find any examples that fail to work, please be sure to let me know at I haven’t tested this fix for backward compatibility with CodeBlocks 8.02, but will try it at some point in the future (my focus for the moment is checking on CodeBlocks 10.05). If someone has trouble making the fix work with CodeBlocks 8.02, please let me know that as well .

Using the FindStr Utility to Locate Errors in Visual Studio

There are cases where the error message that you obtain from Visual Studio is less than helpful. Let’s begin the scenario by saying a coworker wants you to test some code. According to the code worker, the application works fine, but when you try it on your system, you get an odd sort of error message like the one shown here:

Error: Could not find a part of the path ‘C:\MyProj\Projects\TestMe\TestMe\Properties\MyManifest.xml’.

Here’s the frustrating part. You look in Solution Explorer and MyManifest.xml is in plain sight. So, now you start to wonder what’s going on because the IDE should be able to find the file. Here’s the key for this particular example. Instead of putting the project in C:\MyProj, you placed it in C:\MyFriendsProj. In this case, you don’t have a C:\MyProj folder on your machine.  So, when the IDE looked for MyManifest.xml in that particular folder, it couldn’t find it and displayed an error.  What to do?  You could search for this problem all day long and never find it. Often, the problem is one where the path has been hard coded into one or more of the Visual Studio support files.

The answer is to use the FindStr utility at the command line to find files that have the MyProj folder hard coded in them.  You open a command prompt and type FindStr /m /s /C:”MyProj” *.* and press Enter (where the /m command line switch tells FindStr to supply only filenames, the /s command line switch searches subdirectories, and the /C: command line switch performs a literal search).  Here’s some typical output.


All of these files have the MyProj folder hard coded in them.  By deleting these files and rebuilding the project, you can get rid of the error.  You can’t use the Visual Studio search features to find this problem because Visual Studio doesn’t look inside all of the files that it should—it only looks in source code files for the most part and only the source code files that are actually part of your project.  FindStr looks inside every file, no matter what its purpose might be.

What unique uses have you found for FindStr in managing your application development? Let me know at