Scott Hanselman

XmlPreprocess is an elegant hack - or - "The hack is in the eye of the beholder"

December 16, 2004 Comment on this post [6] Posted in ASP.NET | XML | Bugs
Sponsored By

Hack? Brilliance? Sloppy? Simple? Here's when I know something is good:

  • It's easily explained.
  • It's not ambiguous.
  • There aren't many files.
  • Self-containment.
  • It's simple

Seeing a pattern? Loren has come up with an interesting and elegant hack called XmlPreprocess to deal with the deployment of config files to multiple environments without the need for multiple copies. It would be EASILY integrated into a Continuous Integration environment like ours.

The idea is bone simple:

<configuration>
<system.web>
<!-- ifdef ${production} -->
<!-- <compilation defaultLanguage="c#" debug="false"/> -->
<!-- else -->
<compilation defaultLanguage="c#" debug="true"/>
<!-- endif -->
</system.web>
</configuration>

gets loaded into "XmlPreprocess.exe /d production" and you get:

<configuration>
<system.web>
<compilation defaultLanguage="c#" debug="false"/>
</system.web>
</configuration>

Cool, eh? Now, some may thing preprocessor/if statements "tunneled" in XML Comments are gross, but to the nay-sayers I say show me one man's extensibility hack and I'll show you xs:annotation. Would you feel better if the "language" was hidden in other XML elements? Or another namespace? Sure, it's possible, but the goal isn't a new language (if statement = new language) it's #ifdef for XML for a specific purpose.

Reasons it's cool:

  • The files are well-formed and legit XML before and after the process. No processing is needed to use them as found in Source Control.
  • The files are self-describing. Just look at them, it's clear what's up.
  • As Loren says, Get and Go.

Charlie says in comments on Jon Galloway's blog that it's a hack that this is "definitely not the way most natural way of dealing with XML" and he'd prefer XSLT. He does point out that XSLT isn't for newbies. However, for this small task I say, Wow, no way.

Using XSLT to change a config file in such a small way seems like hammering a screw. It's possible, but overkill that WILL get messy. Look at the previous requirements and apply XSLT. XSLT is great for transforming Infosets into other totally different Infosets, and while I'm deep into XSLT, I'm not a fan of using XSLT for small "pruning" changes.

I think XmlPreprocess is clever thing that's good for exactly what it's good for. Check it out!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

An IP Address Blocking HttpModule for ASP.NET in 9 minutes

December 15, 2004 Comment on this post [11] Posted in ASP.NET | HttpModule | Bugs
Sponsored By

I'm sure this has been done before, but it was faster to write it than to google for it. There's some IP Addresses that have been bothering me and I don't have access to a firewall or IIS at my ISP, so...

I can upload a text file called blockedips.txt to my site and the changes happen immediately.

    9 namespace YourModuleNameHere
   10 {
   11     public class IPBlackList : IHttpModule
   12     {
   13         private EventHandler onBeginRequest;
   14 
   15         public IPBlackList()
   16         {
   17             onBeginRequest = new EventHandler(this.HandleBeginRequest);
   18         }
   19 
   20         void IHttpModule.Dispose()
   21         {
   22         }
   23 
   24         void IHttpModule.Init(HttpApplication context)
   25         {
   26             context.BeginRequest += onBeginRequest;
   27         }
   28 
   29         const string BLOCKEDIPSKEY = "blockedips";
   30         const string BLOCKEDIPSFILE = "SiteConfig/blockedips.config";
   31 
   32         public static StringDictionary GetBlockedIPs(HttpContext context)
   33         {
   34             StringDictionary ips = (StringDictionary)context.Cache[BLOCKEDIPSKEY ];
   35             if (ips == null)
   36             {
   37                 ips = GetBlockedIPs(GetBlockedIPsFilePathFromCurrentContext(context));
   38                 context.Cache.Insert(BLOCKEDIPSKEY , ips, new CacheDependency(GetBlockedIPsFilePathFromCurrentContext(context)));
   39             }
   40             return ips;
   41         }
   42 
   43         private static string BlockedIPFileName = null;
   44         private static object blockedIPFileNameObject = new object();
   45         public static string GetBlockedIPsFilePathFromCurrentContext(HttpContext context)
   46         {
   47             if (BlockedIPFileName != null)
   48                 return BlockedIPFileName;
   49             lock(blockedIPFileNameObject)
   50             {
   51                 if (BlockedIPFileName == null)
   52                 {
   53                     BlockedIPFileName = context.Server.MapPath(BLOCKEDIPSFILE);
   54                 }
   55             }
   56             return BlockedIPFileName;
   57         }
   58 
   59         public static StringDictionary GetBlockedIPs(string configPath)
   60         {
   61             StringDictionary retval = new StringDictionary();
   62             using (StreamReader sr = new StreamReader(configPath))
   63             {
   64                 String line;
   65                 while ((line = sr.ReadLine()) != null)
   66                 {
   67                     line = line.Trim();
   68                     if (line.Length != 0)
   69                     {
   70                         retval.Add(line, null);
   71                     }
   72                 }
   73             }
   74             return retval;
   75         }
   76 
   77         private void HandleBeginRequest( object sender, EventArgs evargs )
   78         {
   79             HttpApplication app = sender as HttpApplication;
   80 
   81             if ( app != null )
   82             {
   83                 string IPAddr = app.Context.Request.ServerVariables["REMOTE_ADDR"];
   84                 if (IPAddr == null || IPAddr.Length == 0)
   85                 {
   86                     return;
   87                 }
   88 
   89                 StringDictionary badIPs = GetBlockedIPs(app.Context);
   90                 if (badIPs != null && badIPs.ContainsKey(IPAddr))
   91                 {
   92                     app.Context.Response.StatusCode = 404;
   93                     app.Context.Response.SuppressContent = true;
   94                     app.Context.Response.End();
   95                     return;
   96                 }
   97             }
   98         }
   99     }
  100 }

And in your web.config:

   42 <system.web>
   43    <httpModules>
   44         <add type="YourModuleNameHere.IPBlackList, YourAssemblyNameHere"
   45             name="IPBlackList" />
   46    </httpModules>
   47 </system.web>

No warrenty, express or implied. If it sucks or has bugs/security holes, let me know as it's 9 minutes work.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

ServiceProcessInstaller HelpText has very little documentation

December 15, 2004 Comment on this post [0] Posted in Programming
Sponsored By

I googled around for info on the ServiceProcessInstaller, hoping to change the HelpText. It's a getter, so you have derive and override, rather than setting it. No biggie, but I was surprised. I'm used to googling for .NET documentation and finding MASSIVE amounts of stuff...lot's of articles, etc. Apparently the ServiceProcessInstaller just isn't sexy enough to write about.

This overloaded getter will show help text when someone calls "InstallUtil.exe myserviceassembly.exe /?" on my service exe.

Anyway, I did this. It's not rocket science, but I'd have perferred a setter. Maybe I'm old fashioned.

   88 internal class MySpecialServiceProcessInstaller : System.ServiceProcess.ServiceProcessInstaller
   89 {
   90     public override string HelpText
   91     {
   92         get
   93         {
   94             return @"Enter the Username and Password for the Whatever account in the form \\domain\username. For machines not in a domain, enter hostname\username.";
   95         }
   96     }
   97 }

Then I change the InitializeComponent section - you know, the section you're never supposed to change?

   64 private void InitializeComponent()
   65 {
   66     this.serviceProcessInstaller1 = new MySpecialServiceProcessInstaller();
   67     this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();

Maybe when I google for it again, I'll find my own site. If I can touch the life of just one child who's also using System.ServiceProcess.ServiceProcessInstaller...

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

The Desktop Search battle continues - yawn? Inside MSN Desktop Search

December 14, 2004 Comment on this post [2] Posted in Programming | Tools
Sponsored By

PerfmonsearchWell, I'm deep into the MSN Desktop Search. It's apparently more appropriate to refer to it as the "MSN Toolbar Suite."

Sriram Krishnan has even more technical forensic details on how Google Desktop Search works, but here's what I've gleaned about MSN Desktop Search.

  • The footprint is about 5 megs installed - this changes after you index,
  • The search is per user and everything is in "C:\documents and settings\[username]\local settings\application data\msn toolbar suite\"
    • There's lots of temp files with "Microsoft Search Gatherer Transaction Log. Format Version 4.9" inside them.
  • There's an interesting video with the team up on Channel 9.
  • They don't index your whole harddrive by default - they start with the "My*" files and Outlook* Applications.
  • They are using Microsoft Indexing IFilters - This makes me wonder if I should go download a bunch of new IFilters to get better searching.
  • They index MP3s and Music...
  • When you search, the UI lives in Explorer or Internet Explorer. They've got their own namespace.
  • There are a CRAPLOAD of Perfmon Counters added under "RS Search*" in Perfmon.

MSN Desktop Search vs. Google Desktop Search

MSN: Toolbar pointing you to msn.com
Google: Toolbar pointing you to google.com

MSN: Interface is very shiny and Windowsy and lives in Explorer
Google: Interface is very plain and HTMLy and lives in any browser

MSN: Indexes any IFilter-able thing ala Index Server (this has potential)
Google: Indexes Outlook and a number of text files. (has a hacked .NET plugin API)

MSN: Type-ahead autocomplete ala X1.com
Google: Nope, but I suspect it's coming very soon...

Winner (for now): The user, as now two large companies realize it's hard to find files on your hard drive!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Microsoft Desktop Search...quietly released. Look at me! I can code one too!

December 14, 2004 Comment on this post [4] Posted in XML | Tools
Sponsored By

Certainly comparisions will be made to Google Desktop Search, which I'm already a fan of, but here's YATB (yet another toolbar) for IE. Rather than building it into the operating system or XP SP2, it's a viral MSN toolbar. Here it is, Microsoft's Desktop Search.

I've installed it, and I'm currently digging into how it works. Doesn't support Firefox, though. ;)

It has an X1-style type-ahead feature and rather than browser integration it's an Explorer Toolbar (I needed more things in my tray! Woohoo!)

Currently Indexing...if it indexes .CS files, I'm all in.

UPDATE: It DOES index source and XML! Yum.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.