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 [0]  | 
 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 [0]  | 

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 [1]  | 
 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 [4]  | 
 Wednesday, August 15, 2007

A while back, I've posted about the problem with Visual Studio 2005 complaining about the site being configured for use with ASP.NET 1.1. I've lived with this problem since then, but today I had enough...

Here is the solution that works for me:

  1. Open Internet Information Services (IIS) Manager
  2. Select the root node
  3. Open Handler Mappings
  4. Remove each entry that points to ASP.NET 1.1

The entries from poing 4. are the handlers for files such as .aspx, .asmx and so on. Notice that there exist an entry for each of those files that maps to ASP.NET 2.0.

I'm not an IIS expert so I don't know if those deleted entries were necessary, but I haven't noticed any negative impact of not having them.

Before doing any changes to IIS I would suggest making a backup of all its configuration files, unless you are not afraid - like me (hence no screen shots and no concrete names for entries to delete :-) ).

Wednesday, August 15, 2007 7:46:45 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  | 
 Thursday, August 09, 2007

OnClientClick is a useful property introduced in ASP.NET 2.0 that allows us to add some client-side behavior to button control. Using is as simple as providing the script to be called when a button is clicked by a user:

<asp:Button runat="server" ID="Save" 
    OnClientClick="return confirm('Are you sure?');" />

The problem is that if you use it like that, client-side validation won't fire. Looking at the rendered HTML quickly explains the situation:

onclick="return confirm('Are you sure?');WebForm_DoPostBackWithOptions(...)"

As you can see, the validation doesn't even have a chance to fire (which happens when WebForm_DoPostBackWithOptions is called).

Solving the issue is simple (or not). All that has to be done is a little change in our OnClientClick script (a piece of code found somewhere on the Internet):

<asp:Button runat="server" ID="Save" 
    OnClientClick="if (!confirm('Are you sure?')) return false;" />

Now we only return false (preventing the submit) in case a user didn't confirm the action, otherwise, the rest of the script will be called thus firing validation.

The reason I said that it may not be a simple issue is the fact, that the validation happens AFTER the confirmation, which is not the best thing in my opinion. Why ask the user about saving his data if there are still errors on the form, of which we will inform him after he confirms that he wants to save it?

After analyzing a bit, the code responsible for dealing with OnClientScript, I have come to a conclusion, that solving this problem is not an easy task. It would require some dirty hacks on the server side to make it pretty or calling validation routines on the client, before displaying the confirmation dialog (keeping in mind that checking if there are validation routines present at all is necessary in this case).

I've just left it as it is. After all it's only a minor inconvenience - at least in my case.

Thursday, August 09, 2007 10:38:51 AM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  | 
 Tuesday, August 07, 2007

ASP.NET Ajax comes with a nice way to attach to a load event of a page. To do it just call Sys.Application.add_load and pass it a function as an argument. Done. Everything works as expected... WRONG!

It comes out that add_load fires on each callback and not only when the page loads for the first time. That is of course a problem if you want something to happen on page load only.

From what I've been able to learn, this behavior is by design. Although it is never mentioned in a strigh forward way in a description:

"Raised after all scripts have been loaded and after the objects in the application have been created and initialized."

There are some places in the documentation, that suggests that this is a way it was intended to be. First of all, ASP.NET Ajax tries to mimic that ASP.NET Page life cycle and as such, fires the load event every time there is a callback. The other thing that makes me believe, that load's behavior is there by design is this:

"You can use the event arguments to determine whether the page is being refreshed as a result of a partial-page update and what components were created since the previous load event was raised."

So now I know, but I still cannot accept the naming convention. Sys.Application plainly suggests, it's a kind of singleton, and not a per load thingy.

Tuesday, August 07, 2007 12:18:19 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 
 Friday, July 20, 2007

For a couple of months now I've been running Windows Vista and Visual Studio 2005 without major problems (other than the commonly known ones). Since the first day of this setup I have been working on solutions that inlcude from one to many ASP.NET web applications. All of them are configured to use IIS rather than the built in web server. For all that time, everything was working fine until recently...

One of those days, while opening one of the solutions in Visual Studio, I was presented with a message telling me that my site is configured to use ASP.NET 1.1:

---------------------------
Microsoft Visual Studio
---------------------------
The site 'http://localhost/WebSite' is currently configured for use with ASP.NET 1.1.4322.573. Microsoft Visual Studio has been designed for use with ASP.NET 2.0; if not configured some features may make incorrect assumptions, and pages designed with the tool may not render correctly.

Would you like the site to be configured for use with ASP.NET 2.0?
---------------------------
Yes   No   Cancel   Help  
---------------------------

Needles to say that there were no changes made to IIS configuration of Application Pools or the pool the site is running on, but just to be sure, I've checked IIS Manager. The pool for the site was ASP.NET V2.0. I have agreed and allowed Visual Studio to make the "necessary" change. Needles to say, that nothing was changed in scope of application pools by Visual Studio. Fortunately, my solution kept working after that... for few days.

After few days I've got the very same message. Again, nothing was changed in IIS. This time I decided to leave my supposedly ASP.NET 1.1 in place. Of course everything worked after that also. Including debugging! But then I have the same message every time I open any of the solutions :-(.

I have found that other people also had this problem, and "aspnet_regiis -i" helped them, but it doesn't work for me :-(

Today I noticed that it gets worse. Now I get the message every time I open any of my solutions that include web sites :-(

Friday, July 20, 2007 2:05:07 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [3]  | 
 Sunday, July 15, 2007

ASP.NET despite its many nice features, has also a lot of nasty ones. Some of them are critical - like a design flaw in the way controls are rendered - by the controls themselves (only in version ASP.NET 2.0 Microsoft introduced the concept of Adapters, but this thing is beyond repair. There is just to much logic already in Render methods of all the existing controls to make using adapters easy). Others are just plain annoying, but we mostly we can live with them and even find workarounds.

Today I simply had enough of the style="border-width:0px;" attribute that ASP.NET adds to every img tag it renders from the Image control. There is strightforward way (that I know of) to remove this thing! Try as you might, there will always be a border-width style attribute on you images with either 0px, or whatever value you assign in you ASPX file. But there is a workaround.

To make the border style gone, we have to create our own Image control (inheriting controls is usually a good thing). In this control we have to resort to a trick of overriding the BorderWidth property by returning anything other than Unit.Empty:

public override Unit BorderWidth
{
    get { return Unit.Pixel(0); }
    set { ; }
}

This will cause the border style not to be rendered at all. Additional side effect which is obvious when looking at the above code is that now it is impossible to set BorderWidth property in any way. The value will alwyas be Unit.Pixel(0). This is also a good thing since we really shouldn't set any of the style properties inline but rather use a css style sheets for this purpose.

Sunday, July 15, 2007 2:39:59 PM (Central European Standard Time, UTC+01:00)  #    Disclaimer  |  Comments [6]  |