C# and WPF every day, keeps the doctor away! RSS 2.0
 Friday, July 23, 2010

Today I ran into a strange error while trying to delete a build-definition from TFS (using Visual Studio 2008 with Team Foundation Server 2005). When I right-clicked the build-definition I wanted to delete (in this case 'Usecase_Sync') in the Team Explorer:

And choose "Delete", I got the following messagebox:

TFS205005: Team Foundation Server is unable to locate one or more of the installed error messages. Please repair the installation from Programs and Features in Control Panel to fix the problem. For more information, see 'How to: Repair Team Foundation Server' in the Team Foundation Server Installation Guide that can be downloaded from the Microsoft Web site (http://go.microsoft.com/fwlink/?LinkID=82562). Detailed information :Error 900028, severity 16, state 1 was raised, but no message with that error number was found in sys.messages. If error is larger than 50000, make sure the user-defined message is added using sp_addmessage.

Now I know the installation of TFS didn't run perfectly when we installed it almost a year ago, but we haven't seen errors like this before. And since a lot of people rely on TFS running correctly I wasn't planning on running a repair-installation from Programs and Features (not during working-hours at least!). So we had to find out what the error message should have been for error 900028. Usually Google provides the answer within milliseconds for those kind of questions, but nobody on the whole internet seems to have encoutered this error?! Or at least, nobody has written a text about it (until now!). Luckily for us the error wasn't that hard to figure out, it turns out you need to delete all build(logs) for a build-definition before deleting the build-definition itself:

Pretty easy when you know where to look. Now that the build-definition is gone, I'm gonna look at the mentioned installation guide and try to figure out which step went wrong during the installation of TFS (one year ago!). Any suggestions anyone?

Friday, July 23, 2010 2:00:38 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0] -
VS2008 | TFS
 Friday, July 09, 2010

Time for another C# quiz question, which was inspired by a discussion I had with a fellow developer while we were solving a puzzle during Geocaching. Look at the following code of this simple console application, which rounds two numbers:

    1 static void Main(string[] args)

    2 {

    3     double number1 = 1.5;

    4     double number2 = 2.5;

    5 

    6     Console.WriteLine("Number1 rounded: {0}", Math.Round(number1));

    7     Console.WriteLine("Number2 rounded: {0}", Math.Round(number2));

    8 }


What do you expect the output to be?

Friday, July 09, 2010 12:03:17 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [4] -
C# | Quiz
 Wednesday, May 05, 2010

For a while now I've been meaning to look into a weird error a colleague of mine ran into using CodeAnalysis. In the project we are working on, we run CodeAnalysis during our nightly build. So every developer needs to run CodeAnalysis on their own machine before checking-in a new peace of code. Every developer is used to this routine by now, but one night the build failed on a CodeAnalysis error. When I looked at the build-log I thought the developer had forgotten to run CodeAnalysis on his machine, but when I ran the same build on my local machine to reproduce the error, it didn't appear!?! After some experimenting we found out there was a difference between a debug-build and a release-build. On our local dev-machines we usually build a debug-build, while the build-server always runs the release-build. To show you what went wrong, I've written the following simple console application:

    1 using System;

    2 

    3 namespace CodeAnalysisRule1806

    4 {

    5     class Program

    6     {

    7         static void Main(string[] args)

    8         {

    9             Test test = new Test();

   10 

   11             Console.WriteLine("Test {0}", args.ToString());

   12         }

   13     }

   14 

   15     class Test

   16     {

   17         public Test()

   18         {

   19         }

   20     }

   21 }

   22 

If you run CodeAnalysis on a debug-build, you will get the following warning/error:

CA1804 : Microsoft.Performance : 'Program.Main(string[])' declares a variable, 'test', of type 'Test', which is never used or is only assigned to. Use this variable or remove it.

The error is pretty obvious, but what happens when you run CodeAnalysis on the same code using a release build? The above error (CA1804) disappears, but you get the following error instead:

CA1806 : Microsoft.Usage : 'Program.Main(string[])' creates a new instance of 'Test' which is never used. Pass the instance as an argument to another method, assign the instance to a variable, or remove the object creation if it is unnecessary.

Both errors point out the same problem, but why do they use different error-codes? To understand that, you have to know the difference between a debug-build and a release-build. Most developers know there is some compiler-optimization involved during a release-build. To see if this had anything to do with the problem, I disabled the option "optimize code" in the build-options of the release-build:
Disable optimize code in Visual Studio

As I had hoped, running CodeAnalysis on this build resulted in the same error as a debug-build. So what is the compiler optimizing that results in different CodeAnalysis errors? Let's take a look at the generated assembly using Reflector. If I dissasemble the Main-method from the debug-build it looks like this:

    1         private static void Main(string[] args)

    2         {

    3             Test test = new Test();

    4             Console.WriteLine("Test {0}", args.ToString());

    5         }

    6 

If I dissasemble the Main-method from the release-build it looks like this:

    1         private static void Main(string[] args)

    2         {

    3             new Test();

    4             Console.WriteLine("Test {0}", args.ToString());

    5         }

    6 

Do you see the difference on line 3? The compiler sees that the variable "test" isn't used and optimizes it away in the release-build! This explains the different results in CodeAnalysis, because CodeAnalysis examines the compiled assembly and sees that the instance of Test is never used in the release-build, while in the debug-build the variable "test" is never used.

So what have we learned from all this? If you don't want the nightly build to break, run a release-build on your own machine before checking-in (if that is what the build-server is building). And don't suppress CA1804 or CA1806, because the suppression of one rule will still result in an error in a different build (debug/release). So just remove the unused variable or class-instantiation from your source to fix both CodeAnalysis-errors and clean up your code at the same time!

ps. Thanks to Corey Roth for showing me how to get my favorite Visual Studio plugin "Copy Source As HTML" working in Visual Studio 2010!

Wednesday, May 05, 2010 3:53:25 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1] -
C# | CodeAnalysis | VS2008 | VS2010
 Sunday, March 14, 2010

Last week I posted a question about Enums, which contained the following code:

    1 enum MyColor

    2 {

    3     White,

    4     Red = 0,

    5     Green = 1,

    6     Blue = 1,

    7 }

    8 

    9 static void Main(string[] args)

   10 {

   11     Console.WriteLine(MyColor.White);

   12     Console.WriteLine(MyColor.Red);

   13     Console.WriteLine(MyColor.Green);

   14     Console.WriteLine(MyColor.Blue);

   15 }

 

There are two things you should have noticed about this code:
1) The values in the enum contain duplicates: White and Red both are 0, and Green and Blue both are 1.
2) The Console.WriteLine statements execute a ToString-method on the enums, which prints the text of the enum instead of its value.

If you thought a ToString on an enum outputs the value, the answer would be "0, 0, 1, 1". You can check this by modifying the "Console.WriteLine(MyColor...)" to "Console.WriteLine((int)MyColor...)".
If you knew the ToString outputs the name of the enum, the answer is a bit more of a guess. If you combined the "0, 0, 1, 1" with the enum-names, you would expect the answer to be "White, White, Green, Green", because the "WriteLine(MyColor.Red)" is going to write the same enum-value as "WriteLine(MyColor.White)" and White is the first value in the enum. But this isn't the correct answer:

What happened here? I'll explain in a moment, but first let me show you the result can get stranger by changing the code a tiny bit. Let's add one more name (Black) to the MyColor-enumeration:

    1 enum MyColor

    2 {

    3     White,

    4     Red = 0,

    5     Green = 1,

    6     Blue = 1,

    7     Black = 2

    8 }

    9 

   10 static void Main(string[] args)

   11 {

   12     Console.WriteLine(MyColor.White);

   13     Console.WriteLine(MyColor.Red);

   14     Console.WriteLine(MyColor.Green);

   15     Console.WriteLine(MyColor.Blue);

   16 }

 

The rest of the code hasn't changed. You would expect the output to be the same as before, right?! Well it's not:

What happened here? At first I played around with the enum and the Console.WriteLine statement to see if I could figure out what was happening. But this didn't help me much. So I started Reflector and looked at the Enum.ToString-method:

    1 public override string ToString()

    2 {

    3     Type type = base.GetType();

    4     object obj2 = ((RtFieldInfo)GetValueField(type)).InternalGetValue(this, false);

    5     return InternalFormat(type, obj2);

    6 }

 

It calls the InternalFormat-method to do most of the work:

    1 private static string InternalFormat(Type eT, object value)

    2 {

    3     if (eT.IsDefined(typeof(FlagsAttribute), false))

    4     {

    5         return InternalFlagsFormat(eT, value);

    6     }

    7     string valueAsString = InternalGetValueAsString(eT, value);

    8     if (valueAsString == null)

    9     {

   10         return value.ToString();

   11     }

   12     return valueAsString;

   13 }

 

This method in turn calls InternalGetValueAsString:

    1 private static string InternalGetValueAsString(Type enumType, object value)

    2 {

    3     HashEntry hashEntry = GetHashEntry(enumType);

    4     Type underlyingType = GetUnderlyingType(enumType);

    5     if ((((underlyingType == intType)

    6         || (underlyingType == typeof(short)))

    7         || ((underlyingType == typeof(long))

    8         || (underlyingType == typeof(ushort))))

    9         || (((underlyingType == typeof(byte))

   10         || (underlyingType == typeof(sbyte)))

   11         || ((underlyingType == typeof(uint))

   12         || (underlyingType == typeof(ulong)))))

   13     {

   14         ulong num = ToUInt64(value);

   15         int index = BinarySearch(hashEntry.values, num);

   16         if (index >= 0)

   17         {

   18             return hashEntry.names[index];

   19         }

   20     }

   21     return null;

   22 }

 

And this is where things start to get interesting. See the call to BinarySearch on line 15? This method is also implemented in the Enum-class:

    1 private static int BinarySearch(ulong[] array, ulong value)

    2 {

    3     int num = 0;

    4     int num2 = array.Length - 1;

    5     while (num <= num2)

    6     {

    7         int index = (num + num2) >> 1;

    8         ulong num4 = array[index];

    9         if (value == num4)

   10         {

   11             return index;

   12         }

   13         if (num4 < value)

   14         {

   15             num = index + 1;

   16         }

   17         else

   18         {

   19             num2 = index - 1;

   20         }

   21     }

   22     return ~num;

   23 }

It provides a very simple implementation of an BinarySearch-algorithm. All this algorithm does is: take an ascending sorted array, look at the value in the middle and compare that to the item you're looking for. If the value you are looking for is smaller, repeat the same process for the first part of the array, if the value is higher repeat the same process for the second part of the array. In the end you will either have found your item, or you can say the item isn't in the array.
Now, lets go back to our enumeration MyColor. If you put all values into an array, you will get "0, 0, 1, 1, 2". If you use the BinarySearch-algorithm to find the value 1, you would get the first "1" in the array (Green), because it's in the middle. If you search for the value "0", you would end up with the first "0" in the array (White), because the first run of the BinarySearch determines the value you are looking for is in "0, 0", because "0" is smaller than the middle-value "1". Line 7 ("int index = (num + num2) >> 1;") in the BinarySearch then translates to 0 ((0+1)>>1 == 0), so the first "0" and it's corresponding name "White" is returned.
Now apply this knowledge to the enumeration at the top of this post, containing 4 values: "0, 0, 1, 1". If you search for "1", you will get the first "1" (Green), because "(0 + 3) >> 1" = 1, and "(2 + 3) >> 1" = 2 (look at line 7 of the BinarySearch algoritme again if I'm going to fast here). Now if you were to search for the value "0" in the same array, you would get the second "0" (Red), because "(0 + 3) >> 1" = 1, and this index immediately compares to the value we are looking for!

So the number of elements in the enumeration determines the name returned by Enum.ToString if there are duplicate values in the enumeration!
My advise, don't use duplicate values in your enumerations! Next week I'll post a custom Code Analysis-rule I've build to check this.

Sunday, March 14, 2010 3:09:32 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [1] -
C# | Quiz
 Tuesday, March 09, 2010

Thanks to a colleague (Marcel) we found another fun C# quiz question for you.
What do you think about the following code? Will it compile (is it valid C#)? And if so, what will it write to the console output?

    1 enum MyColor

    2 {

    3     White,

    4     Red = 0,

    5     Green = 1,

    6     Blue = 1

    7 }

    8 

    9 static void Main(string[] args)

   10 {

   11     Console.WriteLine(MyColor.White);

   12     Console.WriteLine(MyColor.Red);

   13     Console.WriteLine(MyColor.Green);

   14     Console.WriteLine(MyColor.Blue);

   15 }

 

Looks easy? Right?! Let me know what you think.

Tuesday, March 09, 2010 9:59:02 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [5] -
C# | Quiz
 Thursday, March 04, 2010

Monday I posted a question about the output of a simple C# console app:

    1 static void Main(string[] args)

    2 {

    3     int? number = 123;

    4 

    5     bool isInt = number is int;

    6     bool isNullableInt = number is int?;

    7 

    8     bool typeIsNullableInt =

    9         (number.GetType() == typeof(int?));

   10 

   11     Console.WriteLine( isInt.ToString() );

   12     Console.WriteLine( isNullableInt.ToString() );

   13     Console.WriteLine( typeIsNullableInt.ToString() );

   14 }

I thought the answer would be very simple: "false, true, true". Too bad! Only one out of three is correct, because the output is "true, true, false"!

Let me explain why:
Lines 5 and 6 are both (the same) special case according to the C# ECMA spec 14.9.10 is operator, because a wrapping conversion exists from "int?" to "int", the result of the statement "number is int" is the same as "number != null"! (first bullet in the ECMA spec, second sub-bullet). If you use Reflector on this simple console-app you'll even see that the C#-compiler optimizes the is-operator on lines 5 and 6 to "number.HasValue"!

Line 8/9 has a different 'problem'. Calling the method GetType on a nullable value-type causes a Boxing conversion, which converts "int?" to an "int". So the compare on line 9 fails because the left hand of the ==-operator is converted to "int" while the right hand side stays a "int?"!

Not exactly what you expected to see, right?! The MSDN even has a small article on this topic: How to: Identify a Nullable Type (C# Programming Guide). The only thing this article doesn't mention is that it is impossible to determine if a variable is a nullable value-type, because a Boxing Conversion will always take place the moment you call the GetType-method.

Hope you learned something from this blogpost!

Thursday, March 04, 2010 11:05:58 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0] -
C# | Quiz
 Monday, March 01, 2010

How well do you know nullable-types in C#? I thought they had no secrets for me, but today I learned something new. Lets see how many of you can predict the outcome of the following console-app:

    1 static void Main(string[] args)

    2 {

    3     int? number = 123;

    4 

    5     bool isInt = number is int;

    6     bool isNullableInt = number is int?;

    7 

    8     bool typeIsNullableInt =

    9         (number.GetType() == typeof(int?));

   10 

   11     Console.WriteLine( isInt.ToString() );

   12     Console.WriteLine( isNullableInt.ToString() );

   13     Console.WriteLine( typeIsNullableInt.ToString() );

   14 }

I'll post the output and an explanation in a few days.

Monday, March 01, 2010 10:06:20 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [3] -
C# | Quiz
 Monday, February 01, 2010

Microsoft has one very usefull document in the MSDN if you're trying to write valid HTML which renders nicely in Outlook 2007:
Word 2007 HTML and CSS Rendering Capabilities in Outlook 2007 (Part 1 of 2)
Word 2007 HTML and CSS Rendering Capabilities in Outlook 2007 (Part 2 of 2)

In the first part of this document they specify which HTML-elements are supported and (more importantly) which attributes are supported. In the second part of this document they link to a tool called 2007 Office System Tool: Outlook HTML and CSS Validator which integrates into Office SharePoint Designer 2007, Microsoft Expression Web, Microsoft Visual Studio 2005, Macromedia Dreamweaver MX 2004 and Macromedia Dreamweaver 8 to help you highlight errors in your HTML. This sounded really nice until I realised that I'm using Visual Studio 2008 and not 2005! You can still install the tool without an error, but it won't show up in Visual Studio 2008. Not without a few tweaks that is!

What you've got to do to get things working is change the registry-keys they specify in the second part of the document to the following:

old key1: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\{1B437D20-F8FE-11D2-A6AE-00104BCC7269}\Schemas
new key1: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Packages\{1B437D20-F8FE-11D2-A6AE-00104BCC7269}\Schemas

old key2: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\{A764E895-518D-11d2-9A89-00C04F79EFC3}\Schemas
new key2: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Packages\{A764E895-518D-11d2-9A89-00C04F79EFC3}\Schemas

And then follow the rest of the MSDN-steps.
After you're finished, copy the following files from the Visual Studio 2005 to the Visual Studio 2008 directory:

C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\1033\schemas\CSS\WordCSS.xml
to
C:\Program Files\Microsoft Visual Studio 9.0\Common7\Packages\1033\schemas\CSS\WordCSS.xml

and
C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\schemas\html\WordHTML.XSD
to
C:\Program Files\Microsoft Visual Studio 9.0\Common7\Packages\schemas\html\WordHTML.XSD

BTW: If you are running Visual Studio on a 64-bit OS, change "HKEY_LOCAL_MACHINE\SOFTWARE\" to "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\" and "C:\Program Files\" to "C:\Program Files (x86)\".

Now restart Visual Studio 2008 and you should be able to choose "Word 2007" from the "Target Schema for Validation" dropdown box in the "HTML source Editing"-toolbar:

Now Intellisense will show you all your 'mistakes':

Hope you found this post usefull. I also added the contents of this blog-post to the second part of the MSDN article as Community Content, so everyone should be able to find this post for future reference.

Monday, February 01, 2010 9:50:15 PM (W. Europe Standard Time, UTC+01:00)  #    Comments [0] -
VS2008 | HTML
Categories
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Rogier Reedijk
Sign In
Statistics
Total Posts: 13
This Year: 0
This Month: 0
This Week: 0
Comments: 25
All Content © 2012, Rogier Reedijk
DasBlog theme 'Business' created by Christoph De Baene (delarou)