Custom FxCop rules

by ingvar 3. november 2010 09:41

FxCop is a good way of ensuring the health and quality of your code. FxCop ships with a bunch predefined rules. But sometimes these rules are not enough. Fortunately there is a SDK for creating you own rules for FxCop. It's pretty simple to create your own rules and in the following I will describe a step-by-step guide to create custom FxCop rules.

Step 1: Creating a Visual Studio project

You start by creating a new class library project (call it MyFxCopRules) and then add the three following references:

  • FxCopSdk.dll
  • FxCopCommon.dll
  • Microsoft.Cci.dll

These can be found in the FxCop install directory (c:\Program Files\Microsoft FxCop 1.36).

 

Step 3: Adding a new cs class file

Add a new cs class file and here I will name this class DoNotCallXDocumentLoadWithPath.

 

Step 4: Implementing the rule

Here is the implementation of the rule. There is one important detail to the code (See below).

namespace MyFxCopRules
{
    public class DoNotUseMethodWithStringBaseRule : BaseIntrospectionRule
    {
        protected string ClassName { get; set; }

        public DoNotUseMethodWithStringBaseRule(string name, string className)
            : base(name,
                   "MyFxCopRules.FxCopRules",
                   typeof(DotNotUseStreamWriterClass).Assembly)
        {
            this.ClassName = className;
            this.MethodToExclude = new List<string>();
        }


        public List<string> MethodToExclude { get; set; }


        public override ProblemCollection Check(Member member)
        {
            Method method = member as Method;
            if (method == null) return this.Problems;

            for (int i = 0; i < method.Instructions.Count; i++)
            {
                Instruction instruction = method.Instructions[i];

                if (instruction.Value == null) continue;

                string value = instruction.Value.ToString();

                if (!value.Contains(this.ClassName)) continue;

                if (this.MethodToExclude.Where(f => value.Contains(f)).Any()) continue;

                Method calledMethod = instruction.Value as Method;
                if (calledMethod == null) continue;

                if (calledMethod.Parameters.Count == 0) continue;

                Parameter parameter = calledMethod.Parameters[0];

                if (parameter.Type.FullName != typeof(string).FullName) continue;

                Resolution resolution = GetResolution(new string[] { method.ToString() });
                this.Problems.Add(new Problem(resolution));
            }

            return this.Problems;
        }
    }
}


DoNotCallXDocumentLoadWithPath inherits the class BaseIntrospectionRule. BaseIntrospectionRule constructor takes three arguments. Getting one of these wrong and the rule will not show up in the FxCop UI or FxCop will refuse to load the assembly.

  • name: This must be unique among all your rules and it's used in the XML rule description file. See step 6.
  • resourceName: This string is used by the SDK to locate an embedded xml resource file, by calling GetManifestResourceStream on the assembly given as the third argument. The string should have the following composition: (default)namespace.filename. In this example the string is “MyNamespace.FxCopRules”. It's optional to append the string with ".xml" like this "MyNamespace.FxCopRules.xml".
  • resourceAssembly: This should point to the assembly where the embedded xml resource file is located (named in the second argument - resourceName).


Step 5: Adding a XML rule description file

Each rule needs to have a XML description in order to work. If a rule is missing its description or there is an error in the description, the rule wont turn up in the FxCop UI. So for step 5 you need to do the following:

  • Add a new empty XML file to your project. Here I name this file FxCopRules.xml
  • Change the build action to: Embedded Resource. Important!


Step 6: Writing the rule description

Here is an example of how to write the rule description:


<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="My Custom IO Rules">
  <Rule TypeName="DoNotCallXDocumentLoadWithPath"
        Category="MyCategory"
        CheckId="MyCategory.DoNotCallXDocumentLoadWithPath">
    <Name>Dot not call System.Linq.Xml.XDocument.Load(string uri, ...)</Name>
    <Description>Dot not call System.Linq.Xml.XDocument.Load(string uri, ...)</Description>
    <Owner></Owner>
    <Url></Url>
    <Resolution>Use the stream version of System.Linq.Xml.XDocument.Load</Resolution>
    <Email></Email>
    <MessageLevel Certainty="100">Warning</MessageLevel>
    <FixCategories>Breaking</FixCategories>
  </Rule>
</Rules>

A few notes on some of the values in this description:

  • TypeName: This should be the same as the first argument to the BaseIntrospectionRule constructor. See step 4.
  • CheckId: This should be unique among all your rules in the same category

 

Step 7: All done!

Now you can compile your assembly and load it in the FxCop UI. Enjoy!

Tags:

.NET | C#

Reading IntelliTrace files (.iTrace)

by ingvar 2. november 2010 12:07

IntelliTrace is a really cool new feature in Visual Studio 2010. This feature gives you a lot of information that was not available before, like file I/O. But if you enable IntelliTrace and the file event, then you pretty quickly end up with a huge amount of events to look through in the IDE. But there is a pretty nice API for reading .iTrace files, so data mining these huge files is pretty easy.

Here is a rather simple method that reads the .iTrace file and goes through all diagnostic events, prints its category id and the stack trace including module name.

IntelliTraceFile file = new IntelliTraceFile("Sample.iTrace");

foreach (IntelliTraceProcess process in file.Processes)
{
    DiagnosticStreamChain chain = process.CreateProcessChain<DiagnosticStreamChain>();

    EventToken eventToken = chain.FirstValidToken;
    while (eventToken != chain.AfterLastToken)
    {
        IntelliTraceEvent intelliTraceEvent = chain.GetEvent(eventToken);

        DiagnosticEvent traceEvent = intelliTraceEvent as DiagnosticEvent;
        if (traceEvent != null)
        {
            ResolvedDiagnosticEvent diagnosticEvent =
                new ResolvedDiagnosticEvent(process, traceEvent);                       

            Console.WriteLine("Event: " + diagnosticEvent.CategoryId);

            var stackFrames = diagnosticEvent.DiagnosticEvent.StackFrames.Reverse();
            foreach (IntelliTraceStackFrame traceStackFrame in stackFrames)
            {
                ResolvedStackFrame stackFrame =
                    new ResolvedStackFrame(process, traceStackFrame);

                IntelliTraceModule module =
                    process.Modules.
                    Where(f => f.Mvid == stackFrame.Module.Mvid).
                    SingleOrDefault();

                string parameterNames = "";
                foreach (string parameterName in stackFrame.Method.ParameterNames)
                {
                    if (parameterNames.Length > 0)
                    {
                        parameterNames += ", ";
                    }

                    parameterNames += parameterName;
                }

                string stackFramePrint =
                    stackFrame.Method.ContainingTypeName + "." +
                    stackFrame.Method.MethodName + "(" + parameterNames + ")";

                if (module != null)
                {
                    stackFramePrint += " @ " + Path.GetFileName(module.FileName);
                }

                Console.WriteLine(stackFramePrint);
            }                        
        }

        eventToken = chain.GetNextToken(eventToken);
    }
}

I have not looked through all different data related to a diagnostic event (DiagnosticEvent.DataBytes). But here is an example of how to get the file name of the file that has been touched in a “file.access” diagnostic event.

if (diagnosticEvent.CategoryId == "file.access")
{
    UnicodeEncoding enc = new UnicodeEncoding();
    string filename = enc.GetString(diagnosticEvent.DiagnosticEvent.DataBytes.Skip(5).ToArray());
}

Tags:

.NET | C#

IL disassembly in C#

by ingvar 14. august 2010 18:30

In this post I'm going to give an example of how to get the IL instructions (op-codes and operands) from any given .NET method body. I used this in a large project to get an overview of inter namespace referencing. Example: Finding references from the namespace A.B to the namespace A.C. There is tons of other uses, like finding what methods another method calls and so on. So stay tuned!

It all starts with the class MethodBody and the method GetILAsByteArray. To obtain a MethodBody you need to get a MethodInfo/MethodBase and then call the GetMethodBody method. GetILAsByteArray returns an array of IL bytes for the given method body. In code its done like this:


/* Find the MethodInfo of interest */
MethodInfo methodInfo =
   typeof(Program).
   GetMethods(BindingFlags.NonPublic | BindingFlags.Static).
   Where(f => f.Name == "Main").
   First();

MethodBody methodBody = methodInfo.GetMethodBody();
byte[] ilBytes = methodBody.GetILAsByteArray();


So now we need to process this byte array. The array contains a series of operation pairs: (operation code, operand). These pairs vary in size. Some operation codes are 1 byte and some 2 bytes. Operands also vary in size, these sizes could be 0, 1, 4, 8 or variable number of bytes.
First lets find the operation code (System.Reflection.Emit.OpCode):


int offset = 0; /* this is used to keep track of our position in the ilBytes buffer. */

while (offset < ilBytes.Length)
{
  short code = (short)ilBytes[offset++];
  if (code == 0xfe)
  {
     code = (short)(ilBytes[offset++] | 0xfe00);
  }

  OpCode opCode =
     typeof(OpCodes).GetFields().
     Where(f => f.GetValue(null) == value).
     Single();

  /* Handle operand and update offset correctly. */
}


So now we have the operation code (or op-code for short). Using the op-code we can find the size of the operand and its meaning. In this post i skip most of the op-codes. Actually I only look at the one regarding method calls (OperandType.InlineMethod). The rest of the op-code cases only updates the offset variable. Note that newing up a new instanse of a type mean "calling" its constructor. In other words a OperandType.InlineMethod instruction. So by focusing on OperandType.InlineMethod I capture all type references whether its method calling or newing up a new instance.


/* This is the implementation for: Handle operand and update offset correctly. */
switch (opCode.OpCode.OperandType)
{
  case OperandType.InlineMethod:
     int metaDataToken = bytes.GetInt32(offset);

     Type[] genericMethodArguments = null;
     if (methodBase.IsGenericMethod == true)
     {
         genericMethodArguments = methodBase.GetGenericArguments();
     }

     instruction.Data =
        methodBase.Module.ResolveMethod(
           metaDataToken,
           methodBase.DeclaringType.GetGenericArguments(),
           genericMethodArguments);
     offset += 4;
     break;

  case OperandType.InlineNone:
     break;

  case OperandType.ShortInlineBrTarget:
  case OperandType.ShortInlineI:
  case OperandType.ShortInlineVar:
     offset += 1;
     break;                    
                   
  case OperandType.InlineVar:
     offset += 2;
     break;

  case OperandType.InlineBrTarget:
  case OperandType.InlineField:
  case OperandType.InlineI:
  case OperandType.InlineSig:
  case OperandType.InlineString:
  case OperandType.InlineTok:
  case OperandType.InlineType:
  case OperandType.ShortInlineR:
     offset += 4;
     break;


  case OperandType.InlineI8:
  case OperandType.InlineR:
     offset += 8;
     break;

  case OperandType.InlineSwitch:
     int count = bytes.GetInt32(offset) + 1;
     offset += 4 * count;
     break;

  default:
     throw new NotImplementedException();
}


There we go! Now the only thing left is to iterate through all types and all methods and collect inter-namespace references. You can download a demo project below. The zip also contains a lot of other goodies!

 

ILTest.zip (7.17 kb)

Tags: , , ,

.NET | C#

Beware when comparing CultureInfo instances

by ingvar 2. juli 2010 10:04

Because CultureInfo is a class and not a struct, the usage of == and != will almost always yield non expecting results. Please take a look at the following code:

CultureInfo cultureInfo1 = CultureInfo.CreateSpecificCulture("en-US");
CultureInfo cultureInfo2 = CultureInfo.CreateSpecificCulture("en-US");
 
bool result1 = cultureInfo1 == cultureInfo2; /* false!!! */
bool result2 = cultureInfo1.Equals(cultureInfo2); /* true, as expected. */

Unlike many other type in the runtime (string, int, DateTime, TimeSpan, Guid etc) CultureInfo is a class and not a struct. And CultureInfo does not have any overloads for ==/!=, so the usage of == and != will do a reference comparison and NOT a value comparison.

The morale of this is; always use the CultureInfo.Equals method when comparing CultureInfo instances. And yes, if you really mean to do a reference comparison, then you of cause use the ==/!= operators.

Tags: ,

C#

About the author

Martin Ingvar Kofoed Jensen

Architect and Senior Developer at Composite on the open source project Composite C1 - C#/4.0, LINQ, Azure, Parallel and much more!

Follow me on Twitter

Read more about me here.

Read press and buzz about my work and me here.

Stack Overflow

Month List