Posts
  • CarlosAg Blog

    IIS Reports for IIS Manager 7

    • 8 Comments

    I have just uploaded a new application that extends IIS Manager 7 for Windows Vista and Windows Longhorn Server that adds a new Reports option that gives you a few reports of the server and site activity. Its features include:

    • Reports are scoped on the object selected in IIS Manager, so if server is selected you get a server report including all Sites information, if a site is selected you only get information related to that specific site
    • Export to HTML
    • Printing support
    • Different chart options: Pie, Columns and Lines
    • Built-in Reports:
      • Status Code, Hits Per Url, Hits by Hour, User Agent, File Extensions
      • Users, Time Taken, Win32 Errors, Client Machine, Http Method

    Click Here to go to the Download Page

    IIS Reports

    I'm working on a second version that will allow you to create your own queries and configure more options like Chart settings, and ore.

    If you have any suggestions on reports that would be useful feel free to add them as comment to the post.

  • CarlosAg Blog

    Recent Tasks in IIS Manager 7

    • 0 Comments

    Talking to a good friend of mine (Daniel) about random geeky stuff, suddenly he showed me one of the features he considered very useful from the Windows Vista Control Panel. Basically they have a "Recent Tasks" at the bottom where they keep track of all the features you have used sorted by most recently used. He then suggested that we should do something similar inside IIS Manager 7 for those of us that have "task-amnesia" or are just to busy to search for it in the Control Panel.

    I immediately thought that was a great idea and that it was also a good sample material to show off the complete extensibility we have built into the new IIS Manager inside Windows Vista and the next release of Windows Server.

    So I decided to create the sample and share it for the IIS community including the MSI for end-users, source code and setup project for developers.

    Download

    To install the MRU IIS Manager Module click here.

    To download source code click here.

    After installing the MRUModule.msi whenever you use a feature inside IIS Manager it will keep track of the Most-Recently-Used tasks you navigate to, keeping a history per-object type (Server, Site, Application, Virtual Directory, Folder and File).

    To use it just open InetMgr and navigate to any feature. Click Back in the navigation bar and you will see that the feature has been added to the list of recently used tasks in the Actions Panel.

    Note: If you don't see the tasks, see the Event Viewer application log to see what could have gone wrong.

    Drilling into the source code

    The source code includes a Visual Studio Solution that includes three Projects:

    1. MRUModule. This project is a typical class library project that includes the actual IIS Manager extension and will be added to the GAC
    2. SetupHelper. This project is a helper library to register the ModuleProvider in IIS administration.config.
    3. Setup. This is a Setup project that builds the MSI

    The way this module is implemented is very simple; IIS Manager exposes several services that give you access to the internal stuff happening inside. One of those services is the INavigationService. This service gives you all the information you need to interact with navigation, including the current item, the history and most importantly an event that notifies you whenever navigation occurs passing you the connection information as well as the page information we are navigating to.

    With this event now is just as simple as keeping a simple Dictionary where we index the pages that have been visited by the ConfigurationPathType.

    Finally the module also implements the IHomepageTaskListProvider interface. This interface allows developers to inject their own tasks to any home page shown inside IIS Manager. In my case I just return a set of Tasks that give a link to each of the pages.

    I hope this gives you an idea on how much infrastructure and work we have invested in the new IIS Manager to make sure it offers not only a nice UI but an entire platform that gives developers the power to bake in their features and offer their users an integrated solution, including remote management over SSL and many more cool features.

  • CarlosAg Blog

    Extending the TreeView in IIS 7 in Windows Vista

    • 8 Comments

    Extending the Hierarchy Tree View in InetMgr

    InetMgr exposes several extensibility points that developers can use to plug-in their own features and make them look and feel just as the built-in functionality. One of those extensibility features is the hierarchy tree view and is exposed mainly through three classes:

    1. HierarchyService. This class is the class that handles the entire hierarchy and an instance is provided by the UI and you can get a reference to it through a ServiceProvider. It is used to manipulate the tree view programmatically, exposing methods to perform actions such as Select, Delete, Refresh, etc.
    2. HierarchyInfo. This abstract class represents a node in the tree view, for example the Web Sites node, the Default Web Site's node, the connections node are examples of instances of HierarchyInfo’s. This class has properties like Text, Image and allows you to react to actions such as selection, deletion, etc. Developers extending the tree view will need to create their own derived classes to implement the behavior as explained below.
    3. c) HierarchyProvider. This abstract class is the base class for all the features that want to extend the tree view. HierarchyService will query each of the registered providers to create the treeview. Developers that wish to add their own nodes should register a HierarchyProvider through the IExtensibilityManager interface.

    To extend the Tree view to add your own set of nodes or context menu tasks, developers need to perform the following actions:

    1. Create a class that derives from HierarchyProvider and handles the GetChildren and/or GetTaskItems to provide any nodes or tasks as needed.
    2. Register the HierarchyProvider using the IExtensibilityManager, this is typically done during the Module initialization phase.
    3. 3) Handle navigation and syncronization as needed.

     

    Tasks illustrated in this walkthrough include:

    • Creating a HierarchyProvider and creating HierarchyInfo classes
    • Registering a HierarchyProvider
    • Testing the new feature
    Note:
    This walkthrough is a continuation from the previous three walkthroughs you can find on how to extend InetMgr.
    You can find the first three at:

    Task 1: Creating a HierarchyProvider

    HierarchyProvider is the base class that developers need to inherit from in order to get calls from the UI whenever a node needs to be loaded. This way they can choose to add nodes or tasks to the HierarchyInfo node that is passed as an argument.

     

    To create a HierarchyProvider
    1. Back in Microsoft Visual Studio 2005 in the ExtensibilityDemo solution, select the option Add New Item from the Project Menu. In the Add New Item dialog select the Class template and type DemoHierarchyProvider.cs as the name for the file.
    2. Change the code so that it looks as follows:
      using System;
      using 
      Microsoft.Web.Management.Client;

      namespace 
      ExtensibilityDemo {

          
      internal class DemoHierarchyProvider : HierarchyProvider {

              
      public DemoHierarchyProvider(IServiceProvider serviceProvider)
                  : 
      base(serviceProvider) {
              }

              
      public override HierarchyInfo[] GetChildren(HierarchyInfo item) {
                  
      if (item.NodeType == HierarchyInfo.ServerConnection) {
                      
      return new HierarchyInfo[] { new DemoHierarchyInfo(this) };
                  
      }

                  
      return null;
              
      }


              
      internal class DemoHierarchyInfo : HierarchyInfo {

                  
      public DemoHierarchyInfo(IServiceProvider serviceProvider)
                      : 
      base(serviceProvider) {
                  }

                  
      public override string NodeType {
                      
      get {
                          
      return "DemoHierarchyInfo";
                      
      }
                  }

                  
      public override bool SupportsChildren {
                      
      get {
                          
      return false;
                      
      }
                  }

                  
      public override string Text {
                      
      get {
                          
      return "Demo Page";
                      
      }
                  }

                  
      protected override bool OnSelected() {
                      
      return Navigate(typeof(DemoPage));
                  
      }
              }
          }
      }

    The code above creates a class derived from HierarchyProvider that implements the base GetChildren method verifying that the node that is being expanded is a ServerConnection; if that is the case it returns an instance of a DemoHierarchyInfo node that will be added to that connection. The class DemoHierarchyInfo simply specifies its NodeType (a non-localized string that identifies the type of this node), SupportsChildren (false so that the + sign is not offered in tree view) and Text (the localized text that will be displayed in the tree view). Finally it overrides the OnSelected method and performs navigation to the DemoPage as needed.

    Task 2: Registering the HierarchyProvider

    In this task we will register the hierarchy provider created in the previous task so that the HierarchyService starts calling this type to extend the tree view.

    To register the provider
    1. Back in Microsoft Visual Studio 2005, open the file DemoModule.cs, and add the following code at the end of the method to register the provider:
      IExtensibilityManager extensibilityManager =
          
      (IExtensibilityManager)GetService(typeof(IExtensibilityManager));

      extensibilityManager.RegisterExtension(
          
      typeof(HierarchyProvider),
          
      new DemoHierarchyProvider(serviceProvider));
    2. The entire code should look as follows:
      protected override void Initialize(IServiceProvider serviceProvider,
                                         ModuleInfo moduleInfo) {
          
      base.Initialize(serviceProvider, moduleInfo);

          
      IControlPanel controlPanel =
              
      (IControlPanel)GetService(typeof(IControlPanel));

          
      ModulePageInfo modulePageInfo =
           new 
      ModulePageInfo(thistypeof(DemoPage), "Demo""Demo Page");

          
      controlPanel.RegisterPage(modulePageInfo);

          
      IExtensibilityManager extensibilityManager =
              
      (IExtensibilityManager)GetService(typeof(IExtensibilityManager));

          
      extensibilityManager.RegisterExtension(
              
      typeof(HierarchyProvider),
              
      new DemoHierarchyProvider(serviceProvider));
      }

    Task 3: Testing the new feature

    To test the feature

    1. Compile everything using Build Solution from the Build Menu and run InetMgr.exe from the <Windows>\System32\InetSrv directory.
    2. Connect to localhost using the TreeView and expand the server connection node.
    3. This will show the new node underneath the connection. When you click on it, it will navigate to the demo page just as expected:

       

    4. Furthermore, the breadcrumb at the top of the UI will automatically discover it and you will be able to navigate to the page by clicking on it, as well as using the text editing and intellisense feature it provides.

       

       

     

    Next Steps

    In this lab, you learned how to extend the tree view to customize any node on it and add your own nodes to it. You can also override the GetTasks method to provide context menu tasks for existing nodes, and you can also override the SyncSelection method to customize the way synchronization of navigation works.

  • CarlosAg Blog

    See you at TechEd

    Tomorrow I'll be leaving for Boston to attend TechEd 2006. This time I will be showing off the new user interface we have been creating for IIS 7, including its tons of new features as well as the new extensibility API that we have created for developers using managed code.

    I'll be giving the following chalk talks:
    WEBTLC05 Introducing the New and Improved IIS Manager in IIS 7.0
    6/12/2006 5:00PM-6:15PM
    This talk shows off many of the new features of IIS 7's new administration UI. It includes demos of the new navigation model, as well as how the tool allows non-Windows users to configure IIS/ASP.NET settings. In addition, it shows how administrators can limit the set of features users can see/modify per site and per application. Finally, the talk demonstrates how the new tool diagnostic features provide greater request processing visibility for easier troubleshooting.

    WEBTLC18 Extending the IIS Manager Tool in IIS 7.0 
    6/15/2006 1:00PM-2:15PM
    IIS 7.0 delivers a rewritten version of the IIS Manager tool that is fully extensible. This talk demonstratees how to make the most of this new extensibility platform for extending IIS UI.

    See you there!

  • CarlosAg Blog

    http://www.IIS.net is live!

    • 0 Comments

    Today we launched our first public team web site. http://www.IIS.net 
    In this web site we are publishing a lot of interesting content, from walkthroughs to videos targeting IT professionals, Web Hosters and of course my favorite… developers.

    Some of my favorites are:

    Configuration Overview
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=944

    IIS7 Module Overview
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=930

    ASP.NET Integration with IIS7
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=928

    Overview of Runtime Status and Control Data
    http://www.iis.net/918/ItemPermalink.ashx

    How to Use Microsoft.Web.Administration
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=952

    And the ones I worked at:

    IIS 7 Admin Tool Extensibility
    http://www.iis.net/971/ItemPermalink.ashx

    Creating a Simple UI Module
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=962&p=1

    Creating a Module Page for IIS7 Administration Tool
    http://www.iis.net/963/ItemPermalink.ashx

    Adding Configuration Functionality to IIS7 Admin Tool Extensions
    http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=964

    Take a look and provide us with any feedback of the content and any ideas you have for the site. We really want to engage all the community, developers, IT professionals, Web Hosters so that we can create a better product for all of you.

     

  • CarlosAg Blog

    The new Configuration System in IIS 7

    • 19 Comments
    Today I was planning on talking about the configuration classes that I purposedly skipped in my last post, but I realized it would be better to explain a little bit more about the new configuration system used in IIS 7.

    First of all, many of you (as me) will be extremely happy to know that the old "monolithic-centralized-admin only" metabase is dead, we have got rid of it for a much better configuration store. Now, before you feel panic, let me assure you that we haven’t just killed it and forget about the thousands of lines of scripts or custom tools built using the old metabase API’s (such as ABO), for that we have created something we called ABOMapper which will allow all of those applications to keep running transparently, since it will auto-magically translate the old calls to the metabase to actually modify the new configuration system.

    So what is this new configuration system? Well for those of you who have been working with ASP.NET for the past years, you will feel right at home and happy to know that we are moving to used the exact same concept as ASP.NET does .config files.

    ApplicationHost.config

    At the root level we have a file called ApplicationHost.config that lives in the same directory of IIS (typically <windows>\System32\InetSrv\ directory). This is the main configuration file for IIS, this is where we store things like the list of sites, applications, virtual directories, general settings, logging, caching, etc.

    This file has two main groups of settings:
    • system.applicationHost: Contains all the settings for the activation service, basically things like the list of application pools, the logging settings, the listeners and the sites. These settings are centralized and can only be defined within applicationHost.config.
    • system.webServer: Contains all the settings for the Web server, such as the list of modules and isapi filters, asp, cgi and others. These settings can be set in applicationHost.config as well as any web.config (provided the Override Mode settings are set to allow)
    ApplicationHost.config

    Administration.config

    This is also a file located in the IIS directory where we store delegation settings for the UI, including the list of modules (think of it as a UI Add-in) available, and other things like administrators.
    Administration.config

    Web.config

    Finally the same old web.config from asp.net has gotten smarter and now you will be able to include server settings along with your asp.net settings.


    Why is this important?

    Well, as I said at the beginning the old metabase could only be accessed by administrators, so in order for someone to change a settings as simple as the default document for a specific application (say you want to change it to be index.aspx), you would need to be an administrator or call the administrator to do the changes.
    With this new distributed configuration system I can now safely modify the web.config within my application and have it my own way without disturbing anyone else. Furthermore, since it lives in my own web.config along with the content of my application I can safely XCopy the whole application and now even the web server settings are ready. No longer the case of going to InetMgr and start setting everything manually or creating a bunch of scripts to do that.

    So how does this actually looks like:


    In applicationHost.config my Sites section looks as follows:
        <sites>
            
    <site name="Default Web Site" id="1">
                
    <application path="/" applicationPool="DefaultAppPool">
                    
    <virtualDirectory path="/" physicalPath="c:\inetpub\wwwroot" />
                </
    application>
                
    <bindings>
                    
    <binding protocol="HTTP" bindingInformation="*:80:" />
                </
    bindings>
             
    </site>
        
    </sites>
    This basically defines a site that has a root application with a virtual directory that points to \inetpub\wwwroot. This site is listening on any IP address on port 80.
    Say I wanted to add a new application and make it listen also in port 8080.
        <sites>
            
    <site name="Default Web Site" id="1">
                
    <application path="/" applicationPool="DefaultAppPool">
                    
    <virtualDirectory path="/" physicalPath="c:\inetpub\wwwroot" />
                </
    application>
                
    <application path="/MyApp" applicationPool="DefaultAppPool">
                   
    <virtualDirectory path="/" physicalPath="d:\MyApp" />
                </
    application>

                
    <bindings>
                    
    <binding protocol="HTTP" bindingInformation="*:80:" />
                    <
    binding protocol="HTTP" bindingInformation="*:8080:" />
                </
    bindings>
             
    </site>
        
    </sites>
    Just by adding the previous markup, I can now browse to http://localhost:8080/MyApp
    IIS Settings in web.config
    More interesting I can now add a file called web.config to c:\MyApp\web.config, and set the content to be:
    <configuration>
        
    <system.webServer>
            
    <defaultDocument>
                
    <files>
                    
    <clear />
                    <
    add value="Index.aspx" />
                </
    files>
            
    </defaultDocument>
        
    </system.webServer>
    </configuration>
    And with this change, my application now will respond using index.aspx whenever /MyApp is requested.
    You can extrapolate from this that all the IIS settings for your application including authentication, authorization, asp and cgi settings, the list of modules, custom errors, etc can be configured within your web.config and never have to request changes to administrators again.

    Of course this brings the question, isn’t this insecure? The answer is no, by default all the IIS sections (except DefaultDocuments) is locked at the applicationHost.config, meaning no one can change them within their web.config unless explicitly changed by the administrator. The cool thing is that the administrator can change it and customize it per application allowing certain apps to change settings while preventing others from doing it. All this can be done through plain config using Notepad or using the very cool NEW InetMgr (which I will blog about it later)
    Finally, the following image shows the hierarchy of config files for each url. config hierarchy

    Now that I have shown a high level overview of how configuration works in IIS 7, I will finally blog about the API to actually change this settings programmatically using Managed code and Microsoft.Web.Administration.dll
  • CarlosAg Blog

    Microsoft.Web.Administration in IIS 7

    • 57 Comments
    While creating the new administration stack in IIS 7, we were looking into the different ways users could manipulate the server configuration as well as the new runtime information available in IIS 7 (Internally we call this RSCA-Runtime State and Control API) from managed code, and we realized we needed to provide a simpler and more straight forward API that developers could consume from managed code. Microsoft.Web.Administration is the answer to this problem. This API is designed to be simple to code against in an “intellisense-driven” sort of way. At the root level a class called ServerManager exposes all the functionality you will need.

    To show the power and simplicity of this API, let’s look at some samples below. To try this samples just create a new Console Application in Visual Studio and add a reference to Microsoft.Web.Administration.dll that can be found at IIS directory (%WinDir%\System32\InetSrv).
    Please note that the following code is based on Windows Vista Beta 2 code and will likely change for the release candidate versions of Windows Vista since we have planned several enhancements to simplify the API and expose more features into it.
    The following picture shows the main objects (excluding Configuration related classes).
     
    Microsoft.Web.Administration
    Creating a Site
     
    ServerManager iisManager = new ServerManager();
    iisManager.Sites.Add("NewSite""http""*:8080:""d:\\MySite");
    iisManager.Update();
    This basically creates an instance of the ServerManager class and uses the Add method in the Sites collection to create a new site named "NewSite" listening at port 8080 using http protocol and content files are at d:\MySite.
    One thing to note is that calling Update is a requirement since that is the moment when we persist the changes to the configuration store.
    After running this code you have now a site that you can browse using http://localhost:8080

    Adding an Application to a site
    ServerManager iisManager = new ServerManager();
    iisManager.Sites["NewSite"].Applications.Add("/Sales""d:\\MyApp");
    iisManager.Update();


    This sample uses the Sites collection Indexer to get NewSite site and uses the Applications collection to add a new http://localhost:8080/Sales application.

    Creating a Virtual Directory
    ServerManager iisManager = new ServerManager();
    Application app iisManager.Sites["NewSite"].Applications["/Sales"];
    app.VirtualDirectories.Add("/VDir""d:\\MyVDir");
    iisManager.Update();


    Runtime State and Control

    Now, moving on to the new Runtime state and control information we also expose in this objects information about their current state as well as the ability to modify them. For example, we expose the list of W3WP processes running (Worker processes) and what I think is really cool, we even expose the list of requests currently running. Stopping a Web Site
    ServerManager iisManager = new ServerManager();
    iisManager.Sites["NewSite"].Stop();

    Recyciling an Application Pool
    ServerManager iisManager = new ServerManager();
    iisManager.ApplicationPools["DefaultAppPool"].Recycle();

    Getting the list of executing requests
    ServerManager iisManager = new ServerManager();
    foreach
    (WorkerProcess w3wp in iisManager.WorkerProcesses) {
        Console.WriteLine(
    "W3WP ({0})", w3wp.ProcessId);
                
        foreach 
    (Request request in w3wp.GetRequests(0)) {
            Console.WriteLine(
    "{0} - {1},{2},{3}",
                        request.Url,
                        request.ClientIPAddr,
                        request.TimeElapsed,
                        request.TimeInState)
    ;
        
    }
    }
    Another big thing on this API is the ability to edit the “.config” files using a simple API, this includes the ability of modifying the main applicationHost.config file from IIS, web.config files from asp.net as well as machine.config and other config files (such as administration.config). However I will talk about them in a future post.
  • CarlosAg Blog

    The evil WinForms Splitter

    • 1 Comments
    Beware of SplitPosition.
    Today I spent quite some time debugging an issue in the new product I am working on.
    Well, to summarize what I was seeing in our UI is that for some reason certain information that I was expecting to be there when a TreeNode was expanded, it just wasn’t there. It was completely surprising to me, since in that particular code path, we do not start multiple threads or use Application.DoEvents nor anything like that, basically all we do is a simple assignment in the TreeView after select event, something like:

            private void OnTreeViewAfterSelect(object sender, TreeViewEventArgs e) {
                _myObject = DoSomeProcessing();
            }
     
    However, for some reason in another event handler of our TreeView, _myObject was not set. How can this be?
     
    Well, after quite some interesting time with VS 2005 (which rocks!), the problem was due to an interesting raise condition caused by (believe it or not) a WinForms Splitter. What was happening is that DoSomeProcessing changed some properties, that caused the UI to perform a layout and inside that code, we set the SplitterPosition property of the Splitter. Well, surprise-surprise, Splitter calls Application.DoEvents in its property setter!!!.
    What DoEvents does is basically lets Windows pop the next message from the windows message pump and process it, so the next event was actually fired, and _myObject ended up not being set.
     
    To illustrate the problem with a simple sample, try this code:
    (Just copy the code and paste it into notepad.
    Save it as TestApp.cs and compile it using “csc.exe /target:winexe TestApp.cs”
    )
     
    using System;
    using 
    System.Drawing;
    using 
    System.Threading;
    using 
    System.Windows.Forms;

    namespace 
    TestApp {
        
    public class Form1 : Form {

            
    private TreeView _treeView;
            private 
    Label _label;
            private 
    Splitter _splitter;
            private 
    Button _someButton;
            
            
    [STAThread]
            
    static void Main() {
                Application.Run(
    new Form1());
            
    }

            
    public Form1() {
                InitializeComponent()
    ;

                
    // Just add some nodes...
                
    TreeNode node _treeView.Nodes.Add("Node 1");
                
    node.Nodes.Add("Node 1.1");
                
    node.Nodes.Add("Node 1.2");
                
    _treeView.Nodes.Add("Node 2");
            
    }

            
    private void InitializeComponent() {
                _treeView 
    = new TreeView();
                
    _splitter = new Splitter();
                
    _label = new Label();
                
    _someButton = new Button();

                
    SuspendLayout();

                
    // treeView1
                
    _treeView.Dock DockStyle.Left;
                
    _treeView.Location = new Point(528);
                
    _treeView.TabIndex 1;
                
    _treeView.AfterSelect += new TreeViewEventHandler(OnTreeViewAfterSelect);
                
    _treeView.BeforeSelect += new TreeViewCancelEventHandler(OnTreeViewBeforeSelect);
                
                
    // splitter
                
    _splitter.Location = new Point(12628);
                
    _splitter.TabIndex 1;
                
    _splitter.TabStop = false;
                
                
    // label1
                
    _label.BackColor SystemColors.Window;
                
    _label.BorderStyle BorderStyle.Fixed3D;
                
    _label.Dock DockStyle.Fill;
                
    _label.Location = new Point(12928);
                
    _label.TabIndex 2;
                
                
    // button1
                
    _someButton.Dock DockStyle.Top;
                
    _someButton.Location = new Point(55);
                
    _someButton.TabIndex 0;
                
                
    // Form 
                
    ClientSize = new Size(500400);
                
    Controls.Add(_label);
                
    Controls.Add(_splitter);
                
    Controls.Add(_treeView);
                
    Controls.Add(_someButton);
                
    ResumeLayout(false);
            
    }

            
    private void OnTreeViewAfterSelect(object sender, TreeViewEventArgs e) {
                _label.Text 
    "Node selected:" + e.Node.Text;
            
    }

            
    private void OnTreeViewBeforeSelect(object sender, TreeViewCancelEventArgs e) {
                
    // Just sleep 500ms to simulate some work
                
    Thread.Sleep(500);

                
    // Now update the SplitPosition
                
    _splitter.SplitPosition 100;

                
    // simulate 500ms of more work ...
                
    Thread.Sleep(500);
            
    }
        }
    }

    Colorized by: CarlosAg.CodeColorizer  


     
    Run it and select the TreeView, notice how ugly everything works.
    Basically every time you select a different node you will get an ugly flickering, getting to see how selection jumps from the newly selected node to the last selected node, and then back to the new selected node.
     
    Well, luckily in Visual Studio 2005, there is a new class called SplitContainer that simplifies everything.
    It even adds new features, such as letting you set a MaxSize for both the left panel and the right panel, and many more features. Best of all, there is no Application.DoEvents in their code, so you can have code that behaves deterministically.
    Bottom line, you do want to use SplitContainer if at all possible.  
  • CarlosAg Blog

    See you at PDC

    • 6 Comments

    Well, I’m really excited to be heading to PDC’05, it sure promise to be an interesting one where we can finally show off so many new technologies we have been working so hard during this past few months to create.

    I will be there along with many of my teammates from IIS trying to show off the really cool new features that we have built into the next version of IIS which I'm sure will rock your mind if you are a Web lover like me.

    So I really hope to meet a lot of you guys out there and try to answer any questions you might have.

     

  • CarlosAg Blog

    Generating Excel Workooks without Excel

    • 53 Comments

    Why I wrote Excel Xml Writer
    One day I found myself having to build a Web Application that one of the requirements involved generating a nice Excel Workbook that users could then play with. This required quite some styling and several other features that you can only do with Excel (like setting printer options and document properties).
     
    Well, coming from Consulting, this requirement was no strange at all to me, and I had to dealt with this problem many times years ago. However then, it was a different story, Managed code and Xml were not even created, and COM was the only option I had. The only solution was using Excel Automation to build Workbooks that supported all the features I required. Yes, I know HTML could do the trick to just generate a table and set the content type to an Excel Application but this certainly leaves you lacking of control in several Excel features like document properties (Author, custom properties, etc), printer settings and more.
     
    Excel Automation
    If you ever worked with Excel Automation you know that it is an extremely powerful (and complicated) object model. However, this power does not come for free, every time you create an Excel.Application, you are essentially running a new Excel.exe instance which is nothing but cheap, and for that reason (and many more) you certainly do not want to do that in a Web Application where thousands of users might run the nice “Export to Excel” link and you end up with thousand of processes being created and destroyed.
     
    Just to illustrate my point, I created the following sample.
     
    C#
    using System;
    using 
    Excel Microsoft.Office.Interop.Excel;
    using 
    System.Runtime.InteropServices;
    using 
    Missing System.Reflection.Missing;

    static class 
    Program {

        
    static void Main() {
            
    int tick Environment.TickCount;
            
    // Create the Excel Application
            
    Excel.Application excel = new Excel.Application();

            try 
    {
                
    // make it visible for demostration purposes
                
    excel.Visible = true;

                
    // Add a Workbook
                
    Excel.Workbook workbook excel.Workbooks.Add(Missing.Value);

                
    // Set the author
                
    workbook.Author "CarlosAg";

                
    // Create a Style
                
    Excel.Style style workbook.Styles.Add("Style1", Missing.Value);
                
    style.Font.Bold = true;

                
    // Add a new Worksheet
                
    Excel.Worksheet sheet =
                    
    (Excel.Worksheet)workbook.Worksheets.Add(Missing.Value, Missing.Value, Missing.Value, Missing.Value);

                
    // Set some text to a cell
                
    Excel.Range range ((Excel.Range)sheet.Cells[11]);
                
    range.Style style;
                
    range.Value2 "Hello World";

                
    workbook.SaveAs(@"c:\test.xls", Missing.Value, Missing.Value, Missing.Value,
                                Missing.Value, Missing.Value, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive,
                                Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value)
    ;

                
    // Finally close the Workbook and save it
                
    workbook.Close(false, Missing.Value, Missing.Value);

                
    // Close Excel
                
    excel.Quit();

            
    }
            
    finally {
                
    // Make sure we release the reference to the underlying COM object
                
    Marshal.ReleaseComObject(excel);
            
    }

            Console.WriteLine(
    "Time: {0}", Environment.TickCount - tick);
        
    }
    }

    Colorized by: CarlosAg.CodeColorizer Note: If you want to run the application you need to add a Reference to Microsoft Excel COM library.
     
    Well, I ran this really simple application cold in my machine that has Office 2003, and running it took almost 3000 milliseconds. Of course if you run it again it runs in about 1 second. But this solution will just not scale in a Web Application.
     
    Another big problem with this approach is the code itself, if you take a close look I had to type almost 20 references to Missing.Value.
     
    Solution
    Luckily ever since Office XP, Excel supports a new file format called Xml Workbook (or something like that), this allow you to create an Xml document that follows a certain schema and Excel will work as if it were the binary XLS format (though not all features at supported like Charts).
     
    Now I have new options, I could just generate the Xml using an XmlDocument or even better using an XmlWriter; but doing so it is quite cumbersome, since you need to understand a lot of Xml, Schemas, and Namespaces and it is quite probably that you will mess up something like closing an element incorrectly or adding the wrong namespace, or prefix, etc.
     
    For that reason I thought to build a lightweight fast wrapper to the Excel Xml Workbook schema. This way my application manipulates an object model that looks similar to Excel Automation OM but it is lightweight, 100% managed, and that in the end serialize itself into an Xml using an XmlWriter.
     
    This is exactly what Excel Xml Writer is, just a simple object model that generates Xml following the Excel Xml Workbook schema. After almost done with it, I thought I could add the ability to load the Xml as well, so I added that feature. This turned out to be extremely useful when loading Excel worksheets from the Office Web Components, and really cool usage, so that you can embed workbooks in your page, and then use AJAX like technology to post the XMLData property back and load it in the server side to do the actual processing in your Database, etc.
     
    You can download it for free at http://www.carlosag.net/Tools/ExcelXmlWriter/Default.aspx.
     
    Now, the code to generate the same workbook we just did using my library the code looks like:
     
     
    using System;
    using 
    CarlosAg.ExcelXmlWriter;

    class 
    Program {
        
    static void Main(string[] args) {
            
    int ticks Environment.TickCount;

            
    // Create the workbook
            
    Workbook book = new Workbook();
            
    // Set the author
            
    book.Properties.Author "CarlosAg";

            
    // Add some style
            
    WorksheetStyle style book.Styles.Add("style1");
            
    style.Font.Bold = true;

            
    Worksheet sheet book.Worksheets.Add("SampleSheet");

            
    WorksheetRow Row0 sheet.Table.Rows.Add();
            
    // Add a cell
            
    Row0.Cells.Add("Hello World", DataType.String, "style1");

            
    // Save it
            
    book.Save(@"c:\test.xls");

            
    Console.WriteLine("Time:{0}", Environment.TickCount - ticks);
        
    }
    }

    Colorized by: CarlosAg.CodeColorizer

    Several differences:
    1)      You don’t actually need Excel installed in your server to run this program since it does not uses Excel at all, just Xml.
    2)      Working set of your application is way smaller than using the Interop libraries
    3)      This is more than 100 times faster to run.
    4)      Code looks much more simpler.
    5)      Since it generates Xml, you can actually stream it directly in an ASP.NET application to the Response.OutputStream without ever saving it into the file system.
    6)      This solution will scale to thousands of users since it does not require any creation of processes.
     
    Now, even better I decided to write a code generator tool so that you don’t need to write all the styling code and superficial stuff and just focus on the actual data. This tool allows you to open an Excel Xml Workbook that you have created in Excel, and it will generate the C# or VB.NET code that you can use to generate it. This means that you can create the skeleton with all the formatting options in Excel and then just generate the code.
     
     
    Conclusion
    Don’t get me wrong Excel team did an awesome job with the Automation support for all Office products, however this has been around for several years, but it definitely lacks of support for Web based applications (asside from Office Web Components). Luckily they are addressing this in the next release of Office where they will have some awesome support for server side applications and many exciting stuff.
     
    In the mean time you might find really exciting working with Xml in Office since they have great support and it will only get better with time.
Page 9 of 10 (91 items) «678910