FxCop, Your .NET CopBy Klaus Salchner
You love them but you also hate them � cops. You love them when they are there to help you. You hate them when they catch you, fine you or hassle you in any other way. Now there are new cops around the block - .NET cops. The .NET Framework is still a young development platform. The first version, version 1.0, got released in the beginning of 2002. The second revision, version 1.1, got released a year later, in early 2003. And the next release, version 2.0, is due out early in 2005. The point is, we are all still pretty new to this development platform. We are still learning what works and what doesn't work. In other words we are still leaning the best practices for .NET.
Despite initial fears in the development community .NET turned out to be a pretty solid development platform. Regardless of how good the development platform is you can still (quite easily) write bad applications. A "bad application" can be one that suffers from poor performance, has difficulty scaling, is expensive to maintain, proves difficult to troubleshoot, or exhibits any other problem you encounter as your application grows and is increasingly used by your customers. The difference between writing a good or bad performing application lies in the experience of the people involved in the project. That includes experience in how to manage a project, experience in analyzing and defining requirements, architectural experience and also experience in the development platform. Your developers need to know how to design their classes, how to ready the code for localization/globalization, how to interact with COM and many more things. And that's where FxCop comes into play.
FxCop is a rule based engine which helps the development team to enforce best practices of the .NET platform and development standards. This article will help you understand how FxCop works and how you can make use of it in your ongoing .NET projects. You can download FxCop from http://www.gotdotnet.com/team/fxcop/. This tool is provided by the FxCop team from Microsoft for free. (The current version available at the time of writing is 1.30.)
Nuts and Bolts: How FxCop Works
The rules enforced by FxCop are based on the "Microsoft .NET Framework Design Guidelines." These guidelines are based on how the .NET team wants you to use the .NET Framework. FxCop works quite differently than many other "code analysis" tools. Other code analysis tools typically work by scanning your source code, running it through a number of rules, and checking whether your code complies with these rules or not. If your code doesn't comply with a rule, then the tool throws out a message with an explanation of what's "wrong" with your code.
The difficulty these code analysis tools have to deal with is analyzing your code. Some tools work better then others. The more difficult your code the harder the code analysis tool has to work. FxCop is also called a code analysis tool but it doesn't really look at your source code. It looks at your compiled assemblies and by doing so avoids the common pitfalls many other code analysis tools have. The tool doesn't have to deal with the complexities of analyzing your source and understanding all the different ways how you can write and format your code. FxCop reads the meta-data of an assembly and with the meta-data knows exactly what your code is doing.
Every assembly in .NET has meta-data which describes the assembly itself and all the types in the assembly. A thorough discussion of the meta-data goes far beyond the scope of this article. For now, just realize that this meta-data enables the .NET Framework to understand what types are in an assembly, what members each type has, which members are private, protected, and public, an so on.
In addition to examining the meta-data, FxCop also looks at the MSIL code itself. .NET compilers do not generate native code which is understood by the CPU of your machine. .NET compilers generate MSIL (Microsoft Intermediate Language) which is compiled to native code at runtime (this process is called Just In Time compilation, short JIT'ing), which the CPU of your machine can then execute. An assembly consists of meta-data describing its content and MSIL code which at runtime is then compiled to native code executed by the CPU of your machine. FxCop does not analyze your source code but analyzes the meta-data and MSIL code of an assembly. This makes it easier for FxCop to understand what your code is doing and compare it with rules which describe rules your code should be complying with. If the meta-data or MSIL doesn't conform to an FxCop rule, then FxCop shows a message with a description of the rule your code violates.
Let's have a look how to use FxCop. After you install FxCop you can open it through the Start menu. Go to "Microsoft FxCop 1.30" option and then click on FxCop. Up comes an empty window similar to the one shown below. FxCop has three panes � a configuration pane (left side), a message pane (right side) and a properties pane (bottom of the screen).
Examining the Configuration Pane
The configuration pane is used to tell FxCop which assemblies to analyze and which rules to check. You have two tabs at the top of the pane � Targets and Rules. You can load one or more assemblies into the project by clicking on the "Add Target for Analysis" button in the toolbar (DLL symbol with a plus sign in the top corner) or through the menu "Project | Add Targets". In front of each assembly you see a plus or minus icon which allows you to drill down into more details about the assembly or hide that information. It shows you all the resources included in the assembly, all the namespaces in the assembly and for each namespace all the types. Drilling deeper with the plus sign in front of each type you can show all the members. It shows the constructors, methods, properties and fields. The symbols used are pretty familiar from the Visual Studio .NET IDE. It even allows you to drill down into all the parameters a method has with another plus sign in front of each method. All this information is easily available through the .NET reflection classes which in turn just read and interpret the meta-data available in the assembly.
Selecting a resource, assembly, type or member of a type shows the following information in the property pane:
- Resource � It lists all the resources and for string resources also displays the string itself.
- Namespace � Shows the name of the namespace and how many types it contains.
- Type � Lists a summary about the type which includes the base type, which namespace it is in, its visibility and many more things.
- Type member � Lists a summary about the type member which includes full name, return type, visibility and more.
By right clicking on a type member you can select from the "View | IL" popup menu to show the IL code for this member. Clicking on the Rules tab on top of the configuration pane shows you all the rules FxCop has. FxCop has documented how to write and load your own rules. (Writing your own FxCop rules goes beyond the scope of this article.) Out-of-the-box there are seven groups of rules:
- COM Rules � rules about COM interoperability
- Design Rules � rules about the design of your .NET types and assemblies
- Globalization Rules � globalization/localization rules
- Naming Rules � rules about naming conventions
- Performance Rules � rules to improve performance of your code
- Security Rules � rules to make your code more secure
- Usage Rules � rules about proper usage of the .NET framework
With the plus/minus sign you can show all rules within a group or hide them. Selecting a group or rule will show you more information in the property pane. It will show you the following information:
- Group of rules � Shows you a summary about this group of rules. It includes the assembly which includes all the rules, how many rules are included and more.
- Rule � Shows you a summary about the rule. It includes the type which implements the rule, the DLL which includes the rule and more.
Right clicking on the rules list shows a popup menu which allows you to group the rules in three different ways:
- File Name � This is the default grouping and shows seven groups of rules (see above) and under each group all the rules belonging to it.
- Message Level � Groups the rules by the message level � Critical Error, Error, Critical Warning, Warning and Information. This group provides a pretty good view which rules generate which level of messages.
- Breaking Changes � Groups the rules into three groups � Breaking, Non Breaking and Unknown (Default). All rules provided by FxCop are categorized under Breaking or Non-Breaking. This grouping provides a good view which rules generate messages which consider the code to be broken.
In front of each assembly, resource, namespace, type, type member as well as group of rules and rule you see a checkbox. This gives you control what rules you want to check and for which assemblies, resources, namespaces, types or type members you want to check them. For example un-checking the checkbox in front of the type "UserIdentity" means you do not want to check that type. Un-checking the checkbox in front of the "groups of rules" called "COM Rules" means you do not want to check all the COM interop rules. Any combination is possible and allows you very granular control over which rules are checked against what code.
A Look at the Message Pane
After you have configured which assemblies, resources, namespaces or types you want to check (also called the target) and which rules you want to check you are ready to begin the analysis. Click on the Analyze button in the toolbar or through the menu item "Project | Analyze". FxCop now checks all rules you selected against the assemblies you selected, while doing so shows a progress bar, and then fills the message pane with all the messages it found (see picture above). On top of the pane you see three buttons � Active, Excluded and Absent.
Clicking on each of these buttons shows you a different list. The Active list shows you all the active messages. These are all the errors and warnings FxCop has found with the last analysis performed. In other words, this is the list of improvements FxCop recommends to make to your code/assembly. You see four columns in that list:
- Level � The message level assigns an importance to the message. The following levels are supported:
- Critical Error � Issues reported with this level have high visibility and the identified code does not operate correctly in most common scenarios. These issues should be resolved first. You should not exclude issues with this level (I describe what excluding means below).
- Error � Issues at this level have less visibility and impact. But it still identifies issues in your code you need to resolve. Also these issues should not be excluded without careful consideration.
- Critical Warning � This level is mostly used for maintainability issues of your code. The code is working correctly but less-then-optimal. Exclude such messages only after careful consideration.
- Warning � Messages with this level are identifying issues in your code around maintainability, extensibility and stability.
- Informational � Informational messages about the code.
- Certainty � Each message has a certainty in percent. A code analysis tool may sometimes not understand completely what you are trying to achieve with your code. So it may report a "false-positive", meaning the tool believes this to be an issue but after you reviewed it you come to the conclusion there is nothing wrong with the code. The code does what you indent it to do. Each rule calculates a certainty for each message it generates. This is a way for the rule to tell you how likely this is an issue. You should not exclude messages with a high certainty.
- Rule � The name of the rule which generated the message.
- Item � The target item, whether this is the assembly, type or a type member, which generated the message.
You can right click in the list and select "configure columns" from the popup menu. This allows you to show some additional columns like when was the issue seen first time (Created column), when was the issue seen last time (Last Seen column), is this a new issue (New column), etc. I recommend to show the two columns Created and Last Seen as it helps you to understand whether this is a relatively new issue in your code or if it is already around for a while and you just haven't had the time to look at it. You can also click on the column headers to sort the list. For example you can sort the list by level and address first the critical errors and errors.
Selecting a message shows you a summary in the properties pane. It includes the target item which caused the message, a short resolution description and also a help link which shows you a detailed description of the issue and the resolution for it. Most rules have a very detailed help page, although this version still has a number of rules with a rather cryptic explanation of the issue and its resolution. You can also double click on a message which brings up a dialog box with the same information but differently organized (see picture below).
The "Message Details" dialog box has five tabs. The Issues tab shows you the target item which caused the message. Underneath it you see a list of all messages found for this target item. Clicking on a message in the list will show you in the resolution area below a short description of the resolution. FxCop will also show you a link to the source code if it finds a symbol file (file with the same name as the DLL but with a PDB extension) in the same location where the assembly is located. Symbol files contain information which can be used by debuggers and other tools like FxCop to find more information about the assembly. It for example allows FxCop to find the line number in your source code which causes the message. So clicking on the source link opens up Visual Studio, opens the source file and jumps right to the line of code causing the message. I have found this not to be working with Visual Studio .NET 2003 running on Windows 2003. I explain below how you can integrate FxCop into the Visual Studio IED itself.
The Message tab shows you the assembly name which causes the message, when the message appeared the first time and last time plus a tree to the target item, e.g. the method which caused the message. The tab "Rule Support" shows you more information about the rule itself. This includes the name of the rule, the URL to the help page associated with the rule, the author of the rule with an email address to contact and information about the assembly which contains the rule. The "Rule Details" tab contains detailed information about the message itself. It shows the message, the message level, the certainty, the description and additional information which helps you to understand what you need to change in your code to resolve the issue. The Notes tab contains a list of notes you can add to the message. You can add new notes by clicking on the add button, edit existing notes by double clicking on it or remove notes by selecting it and clicking on the remove button. This becomes very useful when you have excluded or resolved a message and you want to go back and find out what you have changed. I explain that in more detail below.
You can also filter the messages by clicking on a group of rules, an individual rule or an assembly, namespace, type or type member. Selecting the top node in the Targets or Rule tab shows again all messages. For example selecting a type allows you to work through all messages of that type. Another example is if you are working on a certain type member, selecting it allows you to view and work through all the messages of this type member. Or maybe you just resolved a certain issue and you want to see any other occurrence of it. So you click on the specific rule which created the message and it allows you to view and work through any other occurrence of that message.
You need to work through all messages one-by-one. A message can describe an issue in your code which needs to be resolved or you determine after careful review that it is a false-positive. If it is a false-positive meaning FxCop believes it to be an issue but you determined after careful review that this is not an issue then you mark it as excluded. You right click on the message and select Exclude from the popup menu. This can also be done by double clicking on the message and clicking on the Exclude button in the "Message Details" dialog. In the upcoming "Edit Notes" dialog box you enter a summary why it is a false-positive and then click Ok. This moves the message from the Active list to the Excluded list. FxCop will exclude this message from now on every time you perform another analysis. Clicking on the Excluded button on top of the message pane will show you all the excluded messages. Double clicking on the message shows you again the "Message Details" dialog and the Notes tab allows you to view the notes you entered when marking the message as excluded. You can move a message back to the active list by right clicking on it and selecting "Mark as Active" from the popup menu. You can also do this by double clicking on the message and selecting the Unexclude button on the "Message Details" dialog.
You have two ways to remove a message from the Active list after you have resolved the issue. You can right click on the message and select "Mark as Absent" from the popup menu or you just run another analysis and FxCop detects that the issue has been resolved and moves it to the Absent list for you. Don't forget to recompile your code as FxCop doesn't look at the source code but rather the compiled assembly. I consider it good practice to add some notes to the message after it has been moved to the Absent list. This provides a historical view and allows to go back and understand why you have made certain changes. Clicking on the Absent button on top of the message pane shows you all the resolved issues.
Saving your FxCop project only saves active and excluded messages but not the Absent ones. So if you want to keep a historical view go to menu "Project | Options" and on the "Project Options" dialog, on the Standard tab, select the Absent checkbox in the "Save Messages" group. This changes the option only for the current project. Going to "Tools | Settings" menu shows the Settings dialog. On the "Project defaults" tab select the Absent checkbox under the "Save Messages" group. This setting takes then effect for all future FxCop projects you create. I recommend keeping the historical view because I always find it useful to be able to go back and understand what has been changed on a project over time, especially if you suddenly run into un-explainable exceptions, crashes or other difficulties with your product.
Diving into the Properties Pane
When you use FxCop the first time the properties pane might be hidden. In this case move the mouse to the bottom of your FxCop window. You will see that the mouse cursor changes to the common "arrow up and down" which allows you do resize that pane and thereby making the properties pane visible. A minor but rather annoying issue if you don't know that there is another useful pane hiding there. This pane has two tabs. The properties tab shows information about the selected assembly, namespace, type, type member, group of rules, rule or message. We have covered that already in detail. The Output tab shows informational, warning and error messages generated by the rules. These messages only appear if the TraceGeneral trace-switch in the FxCop.exe .config file is enabled.
A Look at the Reporting Features
You can also generate reports with FxCop. Click on the menu "File | Save Report As" and provide a name and location for the report. The report is stored as an XML document which references a XSLT document -
FxCopReport.xsl. If you send the report to someone by email make sure that the recipient has also FxCop installed or include also the referenced XSLT document. Opening the report with your browser uses the referenced XSLT document to render a HTML report (see below). The report allows you to drill down to all messages on the assembly level, type level and type member level.
The report, by default, only includes active messages. So, if you want to keep a historical view go to menu "Project | Options" and on the "Project Options" dialog, on the Standard tab, select the Excluded and Absent checkbox in the "Save Messages" group (for reporting). This changes the option only for the current project. Going to "Tools | Settings" menu shows the Settings dialog. On the "Project defaults" tab select the Excluded and Absent checkbox under the "Save Messages" group (for reporting). This setting takes then effect for all future FxCop projects you create. I recommend including the historical view in the generated reports because I always find it useful to be able to go back and understand what has been changed on a project over time.
FxCop from the Command Line - and Visual Studio .NET Integration
The command line version of FxCop allows to integrate FxCop into the IDE of Visual Studio or to add FxCop to your build process. There are a number of command line options which are described very well in the help file which comes with it. The most important options are as follows:
/p:� Allows you to specify an existing FxCop project file. Follow the
/p:with the full path to the FxCop project file. If your path has spaces put it within double quotes. When you specify a project file the command line options
/fwill be ignored, because all that information is readily available in the project file itself. Here is an example:
fxcopcmd /p:"c:\documents and settings\klaus\retrievesecurity.fxcop"
/c� Directs the output to the console. This can be very useful when you call FxCopCmd from your build tool or from the Visual Studio IDE. In the later case it redirects the output to the "Output Window" in the Visual Studio IDE.
/o:� Directs the output to the specified file. The file generated is in XML format. It quite actually generates the same XML file as when you generate a report in FxCop. So make sure you use the XML extension in your file name so you can easily view it in a browser. If your path has spaces then put it again within double quotes. Here is an example:
fxcopcmd /o:"c:\documents and settings\klaus\retrievesecurity.xml"
/t:� A comma separated list of types which you want to be analyzed. All other types in the assembly will be ignored. Here is an example:
/f:� The assembly you want to be analyzed. If your path has spaces then put it again within double quotes. If you have multiple assemblies you want to be analyzed then repeat the
/foption for each assembly. Here is an example:
fxcopcmd /f:"c:\documents and settings\retrievesecurity\retrievesecurity.dll"
/u:� This option only works if you specify a project file with the
/poption. It tells FxCopCmd not to display the result on a console or output into a file, but rather store the result in the project file, like as you run FxCop. This way you can open up the project file and then work with it as if you had clicked "Analyze" in FxCop.
Using FxCopCmd and the command line options described above allows you to integrate it into the Visual Studio IDE itself. Open up Visual Studio, go to the menu "Tools | External Tools". This dialog lists all the current external tools configured in the Visual Studio IDE and by clicking the Add button allows you to add a new external tool. Give it a title like "FxCop 1.30". Then enter the following options:
- Command � Enter the full path to the FxCopCmd tool. By default this is as follows:
c:\program files\microsoft fxcop 1.30\fxcopcmd.exe
- Initial Directory � Enter the initial directory when the tool is run. Point it to the directory where FxCop is installed, which is by default as follows:
c:\program files\microsoft fxcop 1.30
- Arguments � Enter the following:
/c /f:$(TargetPath). This tells FxCopCmd the path to the compiled assembly plus to output to the console.
- Use Output Window � Select that checkbox so the output will get redirected to the "Output Window" of the Visual Studio IDE.
Click Ok to save this new external tool. Now open an existing Visual Studio project, compile it and then select the menu "Tools | FxCop 1.30". Now FxCopCmd will run and Visual Studio shows the result in the "Output Window". You can double click on a message and the Visual Studio jumps right to that line of your source code. This lets you use FxCop in the same environment you use to write your code and makes it very convenient. The only disadvantage is that it doesn't allow you to analyze multiple assemblies at the same time and that you don't keep the historical view because no project file is used for that. But it is the most convenient way to use FxCop.
Using FxCop in the Real-World...
We have now seem how FxCop works, how to use the tool, and the purpose of the most germane UI components. To illustrate the utility of FxCop, let's examine a real life example of using FxCop. Assume we are creating a custom exception class so we can throw custom exceptions from the classes used within the bowels of an ASP.NET Web application. This class, then, would be derived from the
ApplicationExceptionbase class. The code might look as follows:
The code appears to be fine. No errors or warnings are created when compiling the class. But when we run it through FxCop it shows us two messages. The first one is a design rule error saying exceptions need multiple constructors. The second message is a usage rule error saying public exceptions should be marked serializable.
Neither warning implies that your code won't run, but you are creating a public type which might be used by other team members or, worse, other development customers. And the code might break because they will expect that an exception is serializable across "Application Domains". They will also expect that every exception they use has four common constructors � one constructor with no arguments, one constructor which takes a string message, one which takes a string message and inner exception and one protected constructor which will be used for de-serializing an exception of this type. See the corrected sample below which will not show any further messages in FxCop.
As this simple example illustrated, using FxCop can help make your code conform to the approved .NET Framework standards outlined by Microsoft. By ensuring that your code adheres to these standards, other teams or customers using your code base will have a much easier time understanding and utilizing your code and assemblies.
Products are increasingly more complex and sophisticated. At the same time your customers expect better quality, more flexibility, and less cost. This means that quality and flexibility need to be addressed as early in the development cycle as possible. FxCop is a tool which helps you to achieve that. The tool is easy to use, integrates well into your development environment and enables you to quickly improve the quality of your code. FxCop has about 200 rules which help you to improve the code in a number of areas including performance, security and globalization/localization which are key features of today's applications.
I encourage everyone involved in current or upcoming .NET projects to start using FxCop. At the end of the day it will reduce the number bugs reported by your testing group and enable you to concentrate on more exiting things. If you have any comments to this article, please contact me. I want to hear if you learned something new and useful or thought you wasted your time reading it. Contact me if you have questions about this topic or article.
About the Author
Klaus Salchner has worked for 14 years in the industry, nine years in Europe and another five years in North America. As a Senior Enterprise Architect with solid experience in enterprise software development, Klaus spends considerable time on performance, scalability, availability, maintainability, globalization/localization and security. The projects he has been involved in are used by more than a million users in 50 countries on three continents.
Klaus calls Vancouver, British Columbia his home at the moment. His next big goal is doing the New York marathon in 2005. Klaus is interested in guest speaking opportunities or as an author for .NET magazines or Web sites. He can be contacted at firstname.lastname@example.org.