Jan Schreuder on .Net

.Net code samples, experiences, observations

View my professional profile on LinkedIn

Recent Posts

Tags

News

  • Inappropriate comments will be deleted at my discretion.

    The information and code samples in this weblog is provided "AS IS" without warranty of any kind, either expressed or implied, including but not limited to the merchantability and/or fitness for a particular purpose.

Community

Email Notifications

Tool suppliers

Tools

General

Microsoft

Favorite blogs

Archives

August 2006 - Posts

Running FxCop on VB.net can be very annoying

I built a very small application which only shows information from a table in a standard DataGrid. Nothing special, other than I had to do it in VB.Net 1.1. I have just finished testing it, and am now running FxCop on the code to see what problems I still have to fix before checking it into source safe.

The code contains a DataGridTextBoxColumn class which inherits from System.Windows.Forms.DataGridTextBoxColumn. Again, nothing particularly special. I override the Edit method to make sure no one gets to edit the line like this:

Protected Overloads Overrides Sub Edit(ByVal source As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds As System.Drawing.Rectangle, _
                ByVal isReadOnly As Boolean, ByVal instantText As String, ByVal cellIsVisible As Boolean)
    isReadOnly = True
End Sub

FxCop tells me the following about this method:

"Change parameter name 'isReadOnly' of method DataGridTextBoxColumn.Edit(CurrencyManager, Int32, Rectangle, Boolean, String, Boolean):Void to 'readOnly' in order to match the identifier as it has been declared in DataGridColumnStyle.Edit(CurrencyManager, Int32, Rectangle, Boolean, String, Boolean):Void."

Now I'm used to doing that in C#. No problem there. But when you do that in VB.Net, guess what:

C:\Projects\LogViewer\1.0\Helpers\DataGridTextBoxColumn.vb(22) : 
         error BC30284: sub 'Edit' cannot be declared 'Overrides' because it does not override a sub in a base class.
C:\Projects\LogViewer\1.0\Helpers\DataGridTextBoxColumn.vb(23) : 
         error BC30181: Specifiers valid only at the beginning of a declaration.
C:\Projects\LogViewer\1.0\Helpers\DataGridTextBoxColumn.vb(24) : 
         error BC30203: Identifier expected.
 

Great huh, three compiler errors for the price of one! Makes sense of course, as ReadOnly is a VB.Net keyword to indicate a property is read-only and VB.Net is case insensitive. But it's behavior like this that can stop people from using FxCop. And that's a pity, because on other issues the remarks made by FxCop are valid.

New Business Objects Support site

Business Objects have introduced a new web site, Diamond.businessobjects.com. Well not entirely new. But it has been up since 18 July 2006 so it's not old. The new support site is setup to provide information to developers on the Business Objects reporting solutions, such as Crystal Reports and Business Objects XI.

The information on this web site is divided into 3 main topics:

Each of these sections has an Overview section. From there you can reach the Forums, News and Media sections for these 3 main topics. The Develop and Integrate topic also has a Code Samples and Developer Library section. I have already searched the site for solutions to some of my problems (such as the one in this post) and so far always found a solution.

So check it out if you have a problem with a Business Objects product, or simply if you want more information about any of them.

Crystal Reports for .Net fails on Citrix / Terminal Server

Yesterday I received an email from a colleague whom I worked with on a Windows Forms project a few months ago. We had implemented Crystal Reports in that application to generate reports and Word documents. The application has since been deployed several times without problems. But in his email yesterday, he mentioned that he couldn't run any of the reports. Every time he started a report, he got the error message "Load Report Failed".

The problem is caused by the fact that Crystal Reports stores some locations in the system registry. And normally, all applications are installed to the C: drive. But in most cases, a user that will access the application in a Citrix or Terminal Server environment will never have access to the C: drive. To solve it, you need to change a few registry settings using RegEdit. I found the solution to this problem here at the Crystal Reports Support Site.

Sandcastle August 2006 CTP is available

OK, so maybe you've seen it already. But with the demise of NDoc for .Net 2.0 there is, to me at least, no other alternative to generate documentation for .Net 2.0 class libraries. So I will continue to try and keep you up-to-date.

New CTP released

A few days ago, the Sandcastle team have released the August 2006 CTP. For a full list of new features, and a list of bugfixes, please follow this link. The new CTP can be downloaded from this location.

New Sandcastle web site

Information about Sandcastle can also be found at a new web site: www.sandcastledocs.com. On this web site, you can find links to the following information:

I think it's a better start for information about Sandcastle then the already available web log. Although I did find a lot of information there too. And the links at the new web site link to the web log again.

The return of NDoc?

No, Sandcastle cannot be compared to NDoc. But Eric Woodruff has created an NDoc like GUI application that works really well. It has the same look and feel. You can create projects, just like NDoc. If you want, you can convert old NDoc projects or create a new one based on an existing Visual Studio solution. I tried on several of my projects and works really well.

There are others about which I blogged before, but this one made me feel right at home. If you want to use this tool to generate your documentation, download the setup or the sources here at CodeProject.

What's next?

We'll have to wait and see. Anand Raman, the Microsoft group manager for this product promised a GUI and Visual Studio Add-in for Sandcastle In earlier posts at the Sandcastle web log. I'm really curious what these applications will offer, especially since there's a lot of community effort going on to do just that.

Other items will include an easier way to generate hsx files that will allow you to integrate the help documentation with the "normal" Visual Studio help files.

Comment spam on this weblog

As Dennis already mentioned in this post, all bloggers on BloggingAbout.net have problems with comment spam. I tried all the tips Dennis gave me, and I know our administrator, Dennis, is doing what he can to help us stop comment spam. But I still get the impression that it's only getting worse.

Because of that I am now forced to disallow anonymous comments until it is possible to stop all comment spam. And I really hate that I have to resort to this. Hopefully, a future version of Community Server will improve on blocking comment spam. But until then, if you feel the need to comment on my weblog, please register yourself on BloggingAbout.Net. Or use the email option in the navigation bar.

Tweak the XP search option

This morning I needed to find a file that contains a certain value. I knew it was an XSD, so I started the XP search option and entered the *.xsd as the filter for the file name and correlation_id as the text I needed to find.

To my surprise, it didn't find the file. Changing the filter to *.* didn't help. I tried another text of which I know it is in all XSD files (xmlns), and still it found nothing.

I googled the Microsoft part of the Internet (http://www.Google.com/Microsoft) for an explanation and soon discovered that the standard XP search option will only search files that are registered as being text files. All other files are simply ignored when you try to search for text in a file.

Fortunately, I also found the solution to my problem. The registry contains a setting which you can use to override this standard behavior.

Windows Registry Editor Version 5.00
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex]
"FilterFilesWithUnknownExtensions"=dword:00000001
 

Changing the setting to the above value will search all files for the text you enter. You can of course use RegEdit to do this, or download this registry patch to change this setting.

More registry tweaks can be found here.

Test your bandwidth

There's a number of places on the web where you can test how fast (or slow) your broadband Internet connection actually is. Speedtest.Net is one of the coolest I've seen in a long time. Couldn't resist blogging it.

Dynamics of Software Development

Maybe it's that time of the year, or just me being busy with code reviews (see this post or this one). But Microsoft have just re-released a book written by Jim McCarthy, Dynamics of Software Development. The original book was published in 1996, but I think it's still a good read as it gives you an idea on how Microsoft developed software at that time.

Jim McCarthy is the author of "21 Rules of Thumb for Shipping Great Software on Time". These 21 rules still apply to software development. With regards to my previous posts on a few rules jump out:

1. Don’t know what you don’t know.

It is essential not to profess to know, or seem to know, or accept that someone else knows, that which is unknown.

9. Low tech is good.

A smaller effort is almost always more desirable than a larger one. Shipping great software on time requires that we value an understood solution much higher than one fraught with unknowns. Keep in mind that customers would almost always rather see progress than promises.

I guess most of you have read these rules already. If you haven't read them, then I recommend that you read this post. It's a good read, a lot of it will definitely seem familiar.

Posted: Thu, Aug 17 2006 9:21 AM by Jan Schreuder | with no comments |
Filed under:
Visual Studio .Net 2003 SP1

It's here. Almost 9 months after Microsoft have released Visual Studio 2005, they bring us Service Pack 1 for Visual Studio 2003. You can download it here.

This is the link to the knowledge base article which contains a list of fixes included in this service pack. The fixes have been categorised into a number of groups. The groups of interest to me were:

  • Microsoft ASP.NET and Web Forms
  • Microsoft Visual Basic .NET
  • Microsoft Visual C# .NET
  • Microsoft Visual Studio .NET

I'm downloading it now because it fixes a few "stops responding" issues in the IDE which I have encountered on some occasions.

Reflector Add-Ins

After my rants on the VB.Net code I'm working on, here's something really useful!

In my search for a metrics analysis tool for VB.Net (don't ask...), I stumbled upon Reflector.CodeMetrics. This little add-in contains a metrics analysis tools that works in MSIL rather than your code. This means that you can run the same metrics on assemblies written in VB.Net, C#, Delphi, Managed C++ or any other .Net language.

And you can find more interesting add-ins here at the Reflector web site, such as:

  • Reflector.Diff. Shows the difference between two versions of an assembly.
  • Reflector.FileDisassembler. Generates the project file and the code for an assembly. (has been a life saver already for me!)
  • Reflector.Deblector. An add-in that allows you to debug the assembly from within reflactor, so you don't need the code ;-)
Posted: Wed, Aug 16 2006 1:14 PM by Jan Schreuder | with no comments
Filed under:
This thing is huge, refactoring

I thought I'd give some insight in the huge method I was telling you about in my previous post, and rant some more about software development in general. The code samples used have been edited so that they won't give information about my current assignment.

Insight

The method gets an XML document as input and needs to store the information into a couple of data stores, all using Sql server stored procedures. At some point you need to retrieve the data from the XML and create SqlParameter objects. In total, the method needs to supply over 80 Sql parameters for the various stored procedures. It is done using the following code:

Dim xmlValue As XmlNode = xml.XPath(ConfigurationService.GetConfigurationSetting("ClientId"))
 
sqlParameter = New sqlParameter("@ClientId", SqlDbType.VarChar)
sqlParameter.Direction = ParameterDirection.Input
sqlParameter.Value = xmlValue.InnerText
sqlParameters.Add(sqlParameter)

That explains partly why the method has about 1900 lines of code. The same code is copied over and over again just to get data from the XML and create a SqlParameter object.

What if I create a method to get the value from the XML document?

Private Function GetXmlValue(ByVal xmlAanvraag As XmlData, ByVal xpathName As String) As Object
    Dim node As XmlNode = xmlAanvraag.XPath(ConfigurationService.GetConfigurationSetting(xpathName))
 
    If (Not node Is Nothing) Then
        Return node.InnerText.Trim
    End If
 
    Return DBNull.Value
End Function

And we need a method to create a SqlParameter object which we can add to the command object:

Private Function GetParameter(ByVal parameterName As String, ByVal dbType As SqlDbType, ByVal value As Object, Optional ByVal direction As ParameterDirection = ParameterDirection.Input) As SqlParameter
    Dim newParameter As SqlParameter = New SqlParameter(parameterName, dbType)
 
    newParameter.Direction = direction
    newParameter.Value = value
 
    Return newParameter
End Function

By creating these two methods I can do this:

myCommand.Parameters.Add( _
    GetParameter("@ClientId", SqlDbType.VarChar, _
    GetXmlValue(xmlAanvraag, "ClientId")))

The line is split up over three lines, to make clear what is happening. But in the actual code this will be one line. I've done some estimates and only this action will reduce the number of lines with 800. Making it easier to read, better to maintain.

Another thing is that the method calls a number of stored procedures. If each stored procedure would be handled in a separate method, than the methods themselves would be really simple to read and a lot smaller than 1900 lines...

I ran a metric analysis on the code using a Reflector Add-in I found here. The Cyclomatic Complexity for this particular method is 140! Now have a look at this table:

Cyclomatic complexity

Risk evaluation

1 - 10 Simple, without much risk
11 - 20 More complex, moderate risk
20 - 50 Complex, high risk program
Greater than 50 Untestable program (very high risk)

This table displays value ranges for Cyclomatic complexity and their meaning, the source of which can be found here. Most sources

I've written a proposal to the technical lead here and will leave it at that. This is obviously an action that needs a thorough review once you've made all the changes. And as far as I'm concerned, regression tests after such a major change are mandatory. So this action will need to be carefully planned.

The rant

Why won't developers run FxCop on their code? Why don't they try and split the code into smaller, readable, understandable methods? Every developer I know is talking about OO development and yet every time I'm assigned to a new project and do a review, I find huge methods as the ones above. If you write methods with 1900 lines, then you must have missed the whole concept of modular development. OO development is just a term you use, not something you practice. Methods of that size are prone to errors. Try fixing a bug in a method this size, or even try debugging that method! You take an enormous risk of introducing new ones.

Oh, and before people start telling me that this is because of the class being written in VB.Net. That's not the reason for code like this. I've seen it done in C# code as well. It's not the language that is responsible for s**t like this, it's the (lack of) development skills by the developer.

There, I've said it. It's of my chest. Back to work.

This thing is huge

I've been doing code inspections on various projects, and I have seen things I never believed possible. Last year I wrote an article about how I feel about software development. In that post, I explained why I try to keep methods as small as possible. The reason for that is pretty obvious: It keeps your source code readable. At the time of writing that article, I had just reviewed (and refactored!!!) a method that consisted of 900 lines. I never thought that figure could be beaten, but today I found a method that was even worse.

I was running FxCop on a class library. The library, written in VB.Net, consists of a number of objects which are called by a workflow management application. Each object is responsible for a certain piece of functionality in a number of workflow's. I then saw a message I had never seen when running FxCop: Avoid excessive locals.

Let's first look at what the FxCop documentation at MSDN Wiki says about this message:

A method contains more than 64 local variables, some of which might be compiler generated.

And why is this a bad thing? Well:

A common performance optimization is to store a value in a processor register instead of memory, which is referred to as "enregistering" the value. The common language runtime considers up to 64 local variables for enregistration. Variables that are not enregistered are placed on the stack and must be moved to a register before manipulation. To allow the possibility that all local variables get enregistered, limit the number of local variables to 64.

That's sounds pretty reasonable right? So why was FxCop telling me this? Well, the offending method has 144 local variables, 5 of which were generated by the compiler! I immediately opened the code to see why there were this many local variables. I was shocked to see a single method starting at line 67 and ending at line 2020. A single method of more than 1900 lines! Oh, and the entire class is 2060 lines long...

Obviously, the culprit has left the building. So I'll have to consider what to do. Especially since FxCop found another method with similar problems in the same library: 90 local variables, 2 of which were generated by the compiler, method starts at line 31 and ends at 1093. The class itself is 1101 lines line. 

I wonder, is there anyone out there that has encountered even longer methods?

Windows Live Writer

Remember Bloggie, written by Robert Jan van Holland? Bloggie allowed you to write a blog post locally and post them to your weblog.

Microsoft have now introduced Windows Live Writer. This new application allows you to do something similar. It supports, among others, the following blog engines:

This post is written using WIndows Live Writer and it seems to work just fine, even though it's still a beta version. Unfortunately, I haven't been able to upload images using this tool. It tells me that the weblog doesn't support the remote uploading of pictures. It that's the case, I might just stick to editing my posts in Community server. But maybe our administrator could tell us more about it.

Sandcastle, a documentation tool for the .Net 2.0 Framework

Sandcastle.JPGI promised to inform you about my experiences with Sandcastle, the new tool from Microsoft to help you generate documentation for .Net 2.0 components. In this post I would like to share some of my experiences with the first CTP of Sandcastle.

There will be more than one CTP before we reach Sandcastle 1.0 RTW. The next CTP, which according to the Sandcastle Team blog will be available somewhere in the next 2 weeks, will solve a number of issues that have been mentioned at the blog and at the Microsoft Developer Documentation and Help System forum.

At the end of this post, you will find a list of links to valuable information and downloads. 

The test case

Before making a start with Sandcastle, I needed a good test case. Something for which I needed a help file and that would give me a good impression as to what the current Sandcastle CTP is capable of. For this purpuse, I used the set of class libraries I distribute amongst my colleagues at LogicaCMG, my current employer. For the .Net 1.* version of this library, I used NDOC which was a great tool. But NDoc never made it to a stable working version for .Net 2.0.

The full set of class libraries consists of 6 assemblies. The assemblies have references to .Net framework components. One assembly also contains references to a few Crystal Reports assemblies.

Installation experiences

Before you can install Sandcastle, you will have to make sure that the following has been installed on your machine:

  • .Net Framework Version 2.0
  • HTML Help Workshop

Once you have done that, it is time to run the setup for Sandcastle. The setup of Sandcastle is straight forward, as you would expect from a Microsoft product. I did not encounter any problems during the setup.

Sandcastle currently consists of a number of console applications, XSL transform files and data files. The following is a list of the Sandcastle applications:

  • MRefBuilder: Reflects over managed assemblies to produce an XML-formatted reflection information file.
  • BuildAssembler: Collates information from comment files and reflection information files into a set of HTML pages.
  • XslTransform: Applies XSL transforms to XML files. This executable is used to generate and manipulate auxiliary data files.

Creating a help file for one assembly

At the Sandcastle Teamblog you can find a very comprehensive quickstart for creating a CHM using Sandcastle. Sandcastle relies on an XML configuration file which contains all the information required to build the help file. You also need to run the console applications in the correct order. It's obviously a lot easier to have a batchfile to do this, or possibly even an application.

If you only have references to .Net framework assemblies, then you do not really have a problem generating a CHM using the steps mentioned at the Sandcastle Teamblog. But when you have references to third party components, such as Crystal reports, then you can run into problems. As long as the components are installed somewhere in a folder on your system, then you can add the missing information in the MRefbuilder step by specifying the location of the components with a /Dep: flag. For example: 

MRefbuilder 
     MyClassLib.dll 
     /dep:"C:\Projects\Output\DLL\ThirdParty.dll"
     /out:reflection.org

Sandcastle at present does not support components that are installed in the GAC. It simply cannot find them. The 2nd CTP that will be released is going to address that problem. But until that CTP is released, you will need to place the GAC components somewhere on your system. I currently do this using the GetGACAssemblies application I blogged about.

A number of developers have created applications that automate the steps you need to take to create CHM file. Some examples:

At the moment, I use the GUI application created by Ashley van Gerven. It creates a batch file and a Sandcastle.config file. The batch file will to perform all the necessary steps to create a CHM file. It also supports creating a help file for multiple assemblies. Having a batch file is actually really good, because it allows you to solve problems with third-party dependencies quite easily. 

I haven't had the time to see what the VS add-in by Frank Kroondijk can do, but that will happen soon. Other developers have built similar tools but for the moment I'll stick to these two. Especially since a GUI and VS Add-in have already been announced by the Sandcastle team, so I'm assuming that I will not need these tools for very long.

Creating the help file for all assemblies

When you need to document more than one assembly, you need to get deeper into the Sandcastle configuration file. This is an XML file that contains the steps needed by the Buildassembler application to build the topics for your help file. More details can be found here.

Fortunately, the GUI application created by Ashley van Gerven already takes care of this. Once the batch file and Sandcastle.config file was created by that GUI application, my help file was generated in a matter of seconds. As there is not that much documentation yet on Sandcastle, I really recommend looking at the various tools created by others on the internet when you want to start using Sandcastle.

The image below shows a screen shot of the help file for the CommandBuilder component I blogged about recently. The CHM file for the CommandBuilder component, wich I created using Sandcastle, can be downloaded here.

sandcastle2.JPG

Flexibility

The look-and-feel of the generated help files are almost identical to the MSDN help files we all know. This is accomplished by a CSS stylesheet, a number of XSL files and some javascript. The way this is all setup allows you to influence the way your help files look. You can change the CSS to change the way the pages look. The XML files used for generating the HTML files that eventually get compiled into the CHM are modified using the XSL files. By changing these, or adding new files, you get even more control over the way information is presented.

What's still to come?

At the moment, the Sandcastle team is busy addressing issues mentioned by people who've used the first CTP. The next CTP, which is already announced, will solve a number of these issues. Somethings that are stll being developed before Before Sandcastle reaches version 1.0 RTM, according to information on the blog and forums:

  • A GUI application to make creating help files easier.
  • Improved HXS support to help integrate your help files in Visual Studio.
  • Support for MSBuild.
  • A Visual Studio Add-In
  • Additional XSL and styles.

Conclusion

For what it's worth, Sandcastle has managed to make a good impression on me. I can create help files for my library again and the look-and-feel of the help file is that of the MSDN library. This obviously looks very familiar to everyone, so I'm quite happy with that.  It's not that easy to work with at this moment, but I'm confident this will be solved once Sandcastle reaches 1.0 RTM. The features that are still to come, and the dedication of Microsoft to this project, I'm sure this will all work out.

Sandcastle resources

There is already lot's of information on Sandcastle available on the internet. Below is a list of resources that I found to be useful when you want to do something with Sandcastle:

Getting a copy of a DLL in the GAC

A few weeks ago, Patrick Wellink blogged about how you would get a copy of a DLL when it's in the GAC. Today I had the same problem but after reading Patrick's blog post and the comments, I decided to write a small console application to make that process somewhat easier.

This application takes at least two arguments. The first argument is the physical path to the GAC on the system. The second is the path where the DLL needs to be copied. The following example copies all Crystal components in the GAC to a backup folder:

GetGACAssemblies C:\Windows\Assembly C:\Projects\GACBackup Crystal*.dll

If you just need a backup of all GAC assemblies, simply do something like this:

GetGACAssemblies C:\Windows\Assembly C:\Projects\GACBackup

To build the application, I used the ScanDirectory class I blogged about earlier. The code for this console application is really simple:

using System;
using System.IO;
 
namespace GetGACAssemblies
{
    class Program
    {
        private static string _targetPath;
 
        static void Main(string[] args)
        {
            // You need at least two arguments
            if (args.Length >= 2)
            {
                // Get the target path and make sure it exists
                _targetPath = args[1];
                if (!_targetPath.EndsWith(@"\"))
                {
                    _targetPath += @"\";
                }
 
                if (!Directory.Exists(_targetPath))
                {
                    Directory.CreateDirectory(_targetPath);
                }
 
                // Setup the directory scanner object
                ScanDirectory scanner = new ScanDirectory();
 
                scanner.FileEvent += new ScanDirectory.FileEventHandler(scanner_FileEvent);
                if (args.Length == 2)
                {
                    scanner.SearchPattern = "*.dll";
                }
                else
                {
                    scanner.SearchPattern = args[2];
                }
 
                // Start the scan
                scanner.WalkDirectory(args[0]);
            }
        }
 
        static void scanner_FileEvent(object sender, FileEventArgs e)
        {
            string newFile = _targetPath + e.Info.Name;
 
            if (File.Exists(newFile))
            {
                File.Delete(newFile);
            }
            File.Copy(e.Info.FullName, newFile);
        }
    }
}

You can download the code for the console application here.

More Posts Next page »