Wednesday, September 26, 2007

I had enough!!!

I knew something is wrong with Vista since I began working with background threads in my ASP.NET web applications. The thing I was working on was a Web Service that was executing some work in a background using ThreadPool.QueueUserWorkItem and returning immediately. All was working fine as long as the executed background method was working correctly. Sometimes though it was throwing exceptions.

In such cases, when an exception was thrown not on a main thread of a web application, Windows Vista was happy to assist me (behind the scenes), by writing a special report file so I could send a report to Microsoft. Fine? NO!!!

I have nothing against sending error reports to Microsoft. No. That is not the problem. The problem is, that before I even realized, my system has run out of disk space! Every exception that was thrown in my application, caused Windows Error Reporting to write some files for a total of few hundreds MB (yes mega bytes). But that's not all. To write this report file a special process DW20.exe was started. It ususally takes 50% of my CPU (since I'm running dual core).

As it is common for Microsoft's ideas, they wanted something good and useful, but it came out as usual: a thing that you just can live with (same holds true for Vista's UAP and Internet Explorer's Internet Explorer Enhanced Security). So I've searched google, for a solution to my problems and found this article that describes how to change Windows Error Reporting Options. (in XP this option was somewhere else, now it is hidden a bit better).

To check if you suffer from the same problem I did, check your "c:\Users\All Users\Microsoft\Windows\WER" directory. Just don't be surprised.

Wednesday, September 26, 2007 11:09:10 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 
 Saturday, September 22, 2007

In my last article on opening a file in Visual Studio, I have described how to write a macro to open a file given it's name. Now we can take it one step further. How about opening a file containing a type name.

Unlike opening a file, where there is a built in function, searching for a type is a complicated task. We need to loop over all projects, all items, and then analyze the code. Other than that, the code is rather simple if you understand recursion:

Sub OpenType()
    Dim typeName As String = InputBox("Type name:").ToLower()
    If String.IsNullOrEmpty(typeName) Then
        Return
    End If
    Dim str As String
    Dim projectItemContainingType As ProjectItem
    For Each project As Project In DTE.Solution.Projects
        projectItemContainingType = FindProjectItemWithType(typeName, project.ProjectItems)
        If projectItemContainingType IsNot Nothing Then
            projectItemContainingType.Open()
            projectItemContainingType.Document.Activate()
            Return
        End If
    Next
    MsgBox("Type not found", MsgBoxStyle.Information)
End Sub
Private Function FindProjectItemWithType(ByVal typeName As String, ByVal projectItems As ProjectItems) As ProjectItem
    Dim projectItemContainingType As ProjectItem
    For Each projectItem As ProjectItem In projectItems
        Dim codeModel As FileCodeModel = projectItem.FileCodeModel
        If codeModel IsNot Nothing Then
            projectItemContainingType = FindProjectItemWithType(typeName, codeModel.CodeElements)
        End If
        If projectItemContainingType Is Nothing AndAlso projectItem.ProjectItems IsNot Nothing AndAlso projectItem.ProjectItems.Count > 0 Then
            projectItemContainingType = FindProjectItemWithType(typeName, projectItem.ProjectItems)
        End If
        If projectItemContainingType IsNot Nothing Then
            Return projectItemContainingType
        End If
    Next
    Return Nothing
End Function
Private Function FindProjectItemWithType(ByVal typeName As String, ByVal codeElements As CodeElements) As ProjectItem
    Dim projectItem As ProjectItem
    For Each codeElement As CodeElement In codeElements
        If codeElement.Kind = vsCMElement.vsCMElementClass OrElse codeElement.Kind = vsCMElement.vsCMElementEnum Then
            If codeElement.Name.ToLower() = typeName Then
                projectItem = codeElement.ProjectItem
                Return projectItem
            End If
            Continue For
        End If
        If (codeElement.Children.Count > 0) Then
            projectItem = FindProjectItemWithType(typeName, codeElement.Children)
            If projectItem IsNot Nothing Then
                Return projectItem
            End If
        End If
    Next
    Return Nothing
End Function

Some shortcut to OpenType procedure (like Ctrl + o, Ctrl + t) and you are ready (I describe how to assign a shortcut in my previous article). It finds top level classes and enums. I have found that searching for inner types - like class or enum defined inside another class, makes it run very slowly - but that is nothing we cannot handle.

If you think you like working with macros and need a bit of practice, there is one thing that can be done that will speed things a lot: instead of searching the whole solution for a type, every time, just build a dictionary of typename/projectitem. That will make your searches lightning fast. Just keep in mind that you have to update this cache every time someone adds or removes types. This is not an easy thing to do! Not by far!s

kick it on DotNetKicks.com

Saturday, September 22, 2007 6:24:14 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  | 

The one thing that I always miss in Visual Studio is a quick way to open a file that is part of a solution. By "quick" I mean not requiring a mouse action and with minimal amount of typing. It came out that it is indeed possible to do so without any addon's. As Robert Prouse shows in his blog entry on Quickly Find/Open a File in Visual Studio, we can do so by using the Find drop down (usually it can be found on a toolbar). You get there by pressing Ctrl + d or Ctrl + /. Also in the comments to his post, there is a hint, that we can use a Ctrl + Alt + a to open a command window where you can issue commands like "open [filename]". Cool things indeed. But there is more to it!

I decided to play a bit with Visual Studio Macros and see what I can come up with.

The first thing was writing a macro for opening a file. Functionality similar to the above mentioned, but implemented using macros (later I will write why I decided to reinvent the wheel). As it came out, the documentation on the subject is not very good. It hardly exists! So I had to experiment a bit and after a while I had it working.

To make a Macro step by step:

  1. Run Visual Studio
  2. Press Alt + F11 (or go to Tools->Macros->Macro IDE... menu item)
  3. Under MyMacros, add a new Module.

In the newly added module, start writing code. It is VB.NET so it may cause a little discomfort for a C# developer, but it shouldn't be a major problem.

My procedure for opening files looks as follows:

Sub OpenFile()
    Dim fileName As String = InputBox("Open file:")
    If String.IsNullOrEmpty(fileName) Then
        Return
    End If
    Dim item As EnvDTE.ProjectItem = DTE.Solution.FindProjectItem(fileName)
    If item Is Nothing Then
        MsgBox("File not found", MsgBoxStyle.Exclamation)
        Return
    End If
    item.Open()
    item.Document.Activate()
End Sub

When the code is ready, save it and return to the main instance of Visual Studio and set up a shortcut key.

Setting up a shortcut is easy:

  1. Go to Tools->Options->Environment->Keyboard
  2. Find your macro
  3. Set up a shortcut key (such as Ctrl + o, Ctrl + f) and press assign

From now on, you can use the macro.

The drawback of using this method for opening files is that it does not provide intellisense support, but we can do more with macros than just opening files... Just read my next post.

kick it on DotNetKicks.com

Saturday, September 22, 2007 6:06:32 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 
 Wednesday, September 05, 2007

While working with any kind of configuration files in .NET, be it Web.config in case of ASP.NET or App.config in case of Windows Forms applications, we very often use the appSettings section. We use it to store all kinds of simple configuration options. Options, that are too simple for us to implement a completely new SectionHandler type. There is however one problem with appSettings section not very expressive.

What does it mean to be expressive? Consider the following example:

<appSettings>
  <add key="source1user" value="user" />
<add key="source1password" value="pass" /> </
appSettings>

In simple cases, it may be acceptable, but what happens if more user keys are required for some reason? Maybe we need to access few different locations, each of which requires a username and password? We may use some kind of prefix for each key like in the above example, but that is not very elegant. What is key and value anyway?

Another obvious problem here is when we need to have 2 parameters associated with a single logical functionality in the application, we need 2 entries in appSettings section.

If we still don't want to implement a new SectionHandler type, we have very nice option left: SingleTagSectionHandler.

MSDN describes it as: "Handles configuration sections that are represented by a single XML tag in the .config file". And that's about it. Unfortunately, (as usual) MSDN provides no example of how to use it. Fortunately it is quite simple:

<configSections>
  <section name="remoteDataSource" type="System.Configuration.SingleTagSectionHandler" />
</configSections>

<remoteDataSource username="user" password="pass" url="http://remote/" />

Using the newly declared section from the code is also easy:

Hashtable remoteDataSource = 
(Hashtable)WebConfigurationManager.GetSection("remoteDataSource");
string username = (string)remoteDataSource["username"];
string password = (string)remoteDataSource["password"];
string url = (string)remoteDataSource["url"];

Simple, yet useful.

kick it on DotNetKicks.com

Wednesday, September 05, 2007 8:45:40 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [6]  |