Coolbits Tips

.NET and general software development tips and articles I've written.

Go Back
  • IIS 6.0 and ASP.NET

    ASP.NET security has bitten me again. This time, I got to fight with Windows Server 2003/IIS 6 and ASP.NET. Here are a few things that you might find useful when you are configuring this yourself.

     

    • Remember how classic ASP used to run under the context of the SYSTEM account by default, and how annoying it was when you switched to ASP.NET and nothing worked until you learned your app was running under the ASPNET account instead? With IIS 6, instead of using the ASPNET account by default, it now uses another account. I don't know if this changes depending on the type of Windows Server 2003 install, but in my case it was the Network Service account. Giving this account NTFS rights to the physical folder for my virtual directory solved my particular problem (which was a 404 error.)
    • A google search described several other causes of a 404 error for Windows 2003 server, including ASP.NET not installed correctly, the extension mappings not set-up properly, the default page not set properly, issues with IIS 5 compatibility mode, and issues if your app requires version 1.0 of the framework. I'm not going to post solutions for all of these items, because there seemed to be plenty of good info out there via google, but it is something worth mentioning.

    I was just relieved I figured out my particular problem. I don't really understand why the default account for ASP.NET had to change (yet again), but I hope that it stays the same for a while (like at least until Longhorn!)

     

    [Update on 01/30/04]: Based on Julia's report, it appears that the Network Service account is also used if Windows 2003 is a domain controller. That's a good thing to know - I would have expected it to be the IWAM account.

    Full story

    Comments (0)

  • Dynamic DataGrid Templates with VB.NET

    One of the features of my product Mister Lister is that it creates a list of the records in the database. I do this with an ASP.NET datagrid control. Since users can select which columns should display in the list, the columns are dynamically generated at run-time. Up until now, I had been doing this with a bound column.

    However, while Graham was doing some testing yesterday, he discovered that he could generate errors with certain key combinations. As it turns out, I had left the ValidateRequest attribute set to True, and ASP.NET was warning me that some of the data looked a little like script and was potentially dangerous.

    At first, I looked at ways of using a regular expression to trap this within a validation control instead of generating an error. But I'm no regular expression expert, and the examples I found were too limiting on regular input (in my opinion). I realized that the problem isn't really when I save the data, it is when I redisplay it to users. I just need to make sure that I am using HTMLEncode when displaying the data in certain controls like labels or literals.

    No problem, I thought. I only do this on the list page. However, this meant that I couldn't dynamically generate my datagrid with bound columns, because I couldn't format the data with a bound column. So, I needed to change to a dynamically generated template column.

    Most of the examples I found for this were in C#, or didn't seem to work properly. (I even had trouble with the example in MSDN.) So, I've included my code for doing this here. Hopefully this will help some other person who is trying to do this in VB.NET.

    First, I created a separate class that implements ITemplate. In my case, I used a constructor that would pass in the column name and column type, since I need to do different formatting depending on the type of data.

    By implementing the ITemplate interface, you need to override this method. Within ITemplate, I used AddHandler to hook the DataBinding method of the container to subroutine within the class to format the data. Here's what this new class looks like:

    Public Class DataGridTemplate
      Implements ITemplate
      Private columnName As String
      Private ColumnType As String
      Private WithEvents lbl As Label

      '========================================================================================
      'Sub New - Constructor
    '========================================================================================
      Public Sub New(ByVal ColName As String, ByVal ColType As String)
        Me.columnName = ColName
        Me.ColumnType = ColType
      End Sub

      '========================================================================================
      'Sub InstantiateIn - From ITemplate
    '========================================================================================
      Private Sub InstantiateIn(ByVal container As Control) Implements ITemplate.InstantiateIn

        lbl = New Label
        AddHandler container.DataBinding, AddressOf BindDataCtrl
        container.Controls.Add(lbl)

      End Sub

      '========================================================================================
      'Sub BindDataCtrl - Data binding event
    '========================================================================================
      Private Sub BindDataCtrl(ByVal sender As Object, ByVal e As EventArgs) Handles lbl.DataBinding
        Dim container As DataGridItem = CType(lbl.NamingContainer, DataGridItem)
        Dim str As String = (CType(container.DataItem, DataRowView))(columnName).ToString()

        Select Case ColumnType
          Case "System.DateTime"
            If str <> "" Then
              lbl.Text = Format(CDate(str), "MM/dd/yyyy")
            Else
              lbl.Text = ""
            End If
          Case Else
            lbl.Text = HttpContext.Current.Server.HtmlEncode(str)
        End Select

      End Sub
    End Class

     

    In my code for binding the datagrid, I create a new instance of this class for every column I want to add. Here's a snippet:

    'For each column the the display fields...
    For i = 0 To mdvDisplayFields.Count - 1

      dr = mdvDisplayFields.Item(i).Row

      tc = New TemplateColumn
      tc.ItemTemplate = New DataGridTemplate(dr.Item("ColumnName"), dv.Table.Columns(CStr(dr.Item("ColumnName"))).DataType.ToString)
      tc.HeaderText = CStr(dr.Item("Caption"))
      tc.HeaderStyle.Width = System.Web.UI.WebControls.Unit.Pixel(CInt(dr.Item("ListWidth")))

      grdData.Columns.Add(tc)

    Next

    That's it! Of course, if I needed to do more than just HTMLEncode the text (like if I wanted to add controls, etc.), then this could be more complicated. But this should get you started!

    Full story

    Comments (20)

  • IIS Launch Condition with Setup Project

    As I prepare to release version 1.0 of Mister Lister, I have discovered yet another limitation of the setup projects you can generate within Visual Studio .NET. I'll admit, however, that this particular limitation may be related to the MSI format itself. 

    I am creating a standard setup project (for a WinForm application). However, since I know Mister Lister will require IIS, I am also adding IIS as a launch condition within the setup. This should prevent anyone from installing Mister Lister unless IIS is on the machine. By default, a launch condition is already established for the .NET Framework.

    I would prefer that my setup check for IIS BEFORE it checks for the .NET Framework. That way, IIS will be installed BEFORE the .NET Framework is installed, which will (hopefully) limit support issues related to .NET not being registered with IIS properly.

    However, the .NET Framework condition seems to always be checked FIRST. At first, I thought this might be related to the order they are displayed within the Launch Condition view within VS.NET. So, I tried renaming my condition so that the .NET condition would show up after the IIS condition. However, this did not solve the problem.

    I did discover a message via a google search that identifies the problem. If the InstallURL property is empty, the condition will be checked AFTER all the conditions where the InstallURL is not empty. I thought perhaps I could use Orca to manually change the order. However, I have discovered something. If the InstallURL property is not blank, the launch condition shows up in the VsdLaunchCondition table. However, if the InstallURL property is blank, then the condition ends up in the LaunchCondition table. Editing the VsdLaunchCondition table will not help, because the Url value in that table cannot be NULL anyway (which is probably why it gets moved by VS.NET).

    So, anyway - what can you do? Well, one possibility is to add a URL that points to the Microsoft site with information about IIS (something like: http://www.microsoft.com/iis/). Keep in mind that you'll want to adjust your message so that it tells the user what to do. For these launch conditions that have a URL, the dialog box is a Yes/No box, with the Yes prompt loading the URL. So, your message could add something like: "Click Yes to read more about IIS on Microsoft's website."

    This was the approach I was going to use at first, but I have come up with something that I think is better. Instead, I have changed the InstallURL property to read: "control.exe". This loads the control panel when the user clicks on "Yes", which is closer to what they want, since they will need to use Add/Remove Programs to install IIS. (Anyone know how to directly load Add/Remove Programs? If so, I'll switch to that!)

    Full story

    Comments (2)

  • ASP.NET Forms Authentication Tip

    A lot of applications I build require roles based authentication. Also, I generally utilize forms authentication for my applications. (Not always, but frequently.) When implementing forms authentication in ASP.NET, sometimes the role implementations are simple and sometimes they are complex. For simple implementations (just a few roles), I like to use the UserData property of the FormsAuthenticationTicket object to store a simple list of the role values. Then, later on in my code I can just retrieve the ticket with the user data, decrypt it, and see if the specific role I am testing for exists in the UserData.

     

    This works great, as long as you do NOT use the RedirectFromLoginPage method. This method does not use the cookie created from the custom ticket, and so the UserData is lost. Instead, always retrieve the redirect URL from Request("ReturnURL"), and then manually redirect to the appropriate URL.

     

    For an official Microsoft example, look here.

    Full story

    Comments (0)

  • Organizing Your .NET Code

    If you are using Visual Studio .NET, are you using code regions? This is a feature that is easy to forget about, but really offers some great functionality for organizing your code within a class or module.

    To use, simply wrap the desired code as follows:

    #Region "Database Routines"

    ...Code here

    #End Region

    Now this code can be expanded or collapsed as desired. One section of code for me ended up looking like this:

    Now I can also work with this code more easily. (I also look forward to the additional code outlining features provided in Whidbey!) 

    Full story

    Comments (0)

  • External Component Referencing in VB.NET

    Something that can be a bit confusing in VB.NET (especially to those who upgraded from VB6) is the use of the Imports statement. I'm often asked - when should I use Imports? Isn't adding a reference enough?

    Actually, these two tasks perform different but related functions. Adding a reference to DLL in your project makes the objects in that DLL available for use within your project. So if you have built a component for use within all your projects of your commonly used functions, then the best way to access your component is to add a reference to it from within a project. 

    Using the Imports statement has more to do with ease of use. If you merely add a reference to a component, and don't use Imports, you can still utilize the objects within the component, but you'll need to enter a fully qualified name for each object. So, for example, my function to fix quotation marks in strings so that the database will store them properly must be referenced like this in code:

    CodePoet.Common.StringHandler.FixQt(strVal)

    As you can imagine, this can be a bit of typing after awhile.

    However, if I choose instead to use the Imports statement, like this:

    Imports CodePoet.Common.StringHandler

    From within my code, I merely have to type the function name, like this:

    FixQt(strVal)

    Also, if it appears that there will be overlap in the function names of the assemblies I am referencing, I can utilize aliases to separate things. To do this, I indicate the alias in the Imports statement like this:

    Imports CP = CodePoet.Common.StringHandler

    Then, form within the code, I reference the function name like this:

    CP.FixQt(strVal)

     

     

    Full story

    Comments (0)

  • An Excellent Character- "~"

    The other day I was working on on the web setup project for Mister Lister. I was having a problem with some of the application settings in the web.config. These settings indicate path information for files necessary for Peter's Date Package and Professional Validation and More. The files are installed with Mister Lister in sub-folders of the main application. In the web.config, I was referencing the folders like this:

     

      <add key="PDP_ScriptVirtualPath" value="/Lister/DatePicker/Scripts/" />

    Of course, that's great until the user installs Mister List in a virtual directory other than "Lister". I spent most of today thinking about adding a custom action to my setup that would change the entries in the web.config (ick!)

     

    It just dawned on me that instead, I can use the tilde (~) character. This represents the application virtual directory. Now, my entries look like this:

     

      <add key="PDP_ScriptVirtualPath" value="~/DatePicker/Scripts/" />

    It doesn't matter which virtual directory they are installed in, the web.config is fine. Or, I can even install Mister Lister in the application root. Yippy! Problem solved.

     

    Full story

    Comments (0)

  • Server Debugging Tip

    Lorenzo Barbieri points out that the IIS Lockdown tool is important for IIS security, and it should really be executed on all servers (production, staging and development). However, this tool will prevent your staging and development servers from utilizing debugging mode. He also suggests the symptoms and cure. Thanks!

    Full story

    Comments (0)

  • Web Setup Projects Can Install to Root Folder

    One of the things I always struggle with are how to handle installations for web applications. I like using the web setup project template available in VS.NET, and for simple things I've had some success. But one thing that frustrated me was that during the installation process, it always asked the user to indicate the virtual directory I want to install to. That's great, but what if I want to install into the root folder of my website?

    No problem, because all you need to do is remove the text in the textbox. You are allowed to have the virtual directory name to be blank, which will automatically put your installation in the root folder for the website. Here are few other facts:

    • If you have multiple websites defined on your webserver, the install will not prompt you to select the website - it just selects one for you. If you stop all but the correct site, it will install into the running site, so I use this trick to get my installation into the correct website.
    • If you like to define a different location for your website or virtual directory, I recommend that you create the site or virtual directory first, pointing it to the desired physical location. During installation, your files will then be located in the correct place.

    Full story

    Comments (10)

  • ASP Disabled on Windows 2003

    Dave Burke's blog mentions something that was unknown to me - classic ASP is disabled by default in Windows Server 2003. If you search to an ASP page, you'll get a generic 404 error, unless you enable it within the Web Service Extensions in IIS. 

     

    Definitely something to keep in mind if you are migrating more than ASP.NET applications to Windows Server 2003.

    Full story

    Comments (0)

  • Is Authorization Required?

    Recently I was having trouble configuring a new virtual directory on a production server. The virtual directory would contain an ASP.NET application that worked on other computers. However, on this server the application would always provide a Windows log-in prompt, which never accepted any entries - even the admin for the machine!

    I had changed all the NTFS permissions about a million times (providing, I'm sorry to say, tons of rights that should have been unnecessary), and still the site was inaccessible. I had also repeatedly checked the IIS directory security settings, but these appeared to be fine. Finally, I checked the web.config for the application, and noticed that there was no authorization section. I added the following section (allowing snonymous access), and everything was fine from there.
    <samp>
     <authorization>
      <allow users="?" />
     </authorization>
    </samp>
    I'm not sure why it is required in some situations and not in others, but if you are having trouble with security for a site that should allow anonymous access, give this a try!

    --The Code Poet

    Full story

    Comments (0)