Creating IIS 6 Directories by C#

I’ve been writing this for a while today and couldn’t figure out why I couldn’t get the application to create within IIS. The virtual directory created without problems, but after that nothing happened. It turns out that the msdn library was incorrect!

Here is a working IIS 6 virtual directory creator. The only paramaters that need to be changed are in the // Set comment.

// IIS 6 Virtual Directory Builder and Configuration
// http://r-dunn.co.uk/ieatpenguin/computing/software/962/
// Please feel free to use and distribute

// Set variables: Site_Name,Path_to_wwwroot,Virtual_Directory_Name,Path_to_virtual_dir
// Change the port number after "ServerBindings" if you wish to use a different port.

using System;
using System.IO;
using System.DirectoryServices;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections;

namespace System_DirectoryServices_DirectoryEntry_ConfigIIS
{
  class Program
  {
    static void Main(string[] args)
    {
        CreateSite("IIS://Localhost/W3SVC", "555", "Site_Name", "Path_to_wwwroot");
        SetSingleProperty("IIS://Localhost/W3SVC/555", "ServerBindings", ":8080:");
        CreateVDir("IIS://Localhost/W3SVC/1/Root", "Virtual_Directory_Name", "Path_to_virtual_dir");
    }

    static void CreateSite(string metabasePath, string siteID, string siteName, string physicalPath)
    {
      //  metabasePath is of the form "IIS://<servername>/<service>"
      //    for example "IIS://localhost/W3SVC" 
      //  siteID is of the form "<number>", for example "555"
      Console.WriteLine("\nCreating site {0}/{1}, mapping the Root application to {2}:", 
          metabasePath, siteID, physicalPath);

      try
      {
        DirectoryEntry service = new DirectoryEntry(metabasePath);
        string className = service.SchemaClassName.ToString();
        if (className.EndsWith("Service"))
        {
          DirectoryEntries sites = service.Children;
          DirectoryEntry newSite = sites.Add(siteID, (className.Replace("Service", "Server")));
          newSite.Properties["ServerComment"][0] = siteName;
          newSite.CommitChanges();

          DirectoryEntry newRoot;
          newRoot = newSite.Children.Add("Root", "IIsWebVirtualDir");
          newRoot.Properties["Path"][0] = physicalPath;
          newRoot.Properties["AccessScript"][0] = true;
          newRoot.CommitChanges();

          Console.WriteLine(" Done. Your site will not start until you set the ServerBindings or SecureBindings property.");
        }
        else
          Console.WriteLine(" Failed. A site can only be created in a service node.");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Failed in CreateSite with the following exception: \n{0}", ex.Message);
      }
    }

    static void SetSingleProperty(string metabasePath, string propertyName, object newValue)
    {
      //  metabasePath is of the form "IIS://<servername>/<path>"
      //    for example "IIS://localhost/W3SVC/1" 
      Console.WriteLine("\nSetting single property at {0}/{1} to {2} ({3}):",
          metabasePath, propertyName, newValue, newValue.GetType().ToString());

      try
      {
          DirectoryEntry path = new DirectoryEntry(metabasePath);
          PropertyValueCollection propValues = path.Properties[propertyName];
          string oldType = propValues.Value.GetType().ToString();
          string newType = newValue.GetType().ToString();
          Console.WriteLine(" Old value of {0} is {1} ({2})", propertyName, propValues.Value, oldType);
          if (newType == oldType)
          {
              path.Properties[propertyName][0] = newValue;
              path.CommitChanges();
              Console.WriteLine("Done");
          }
          else
              Console.WriteLine(" Failed in SetSingleProperty; type of new value does not match property");
      }
      catch (Exception ex)
      {
          if ("HRESULT 0x80005006" == ex.Message)
              Console.WriteLine(" Property {0} does not exist at {1}", propertyName, metabasePath);
          else
              Console.WriteLine("Failed in SetSingleProperty with the following exception: \n{0}", ex.Message);
      }
    }

    static void CreateVDir(string metabasePath, string vDirName, string physicalPath)
    {
      //  metabasePath is of the form "IIS://<servername>/<service>/<siteID>/Root[/<vdir>]"
      //    for example "IIS://localhost/W3SVC/1/Root" 
      Console.WriteLine("\nCreating virtual directory {0}/{1}, mapping the Root application to {2}:",
          metabasePath, vDirName, physicalPath);

      try
      {
        DirectoryEntry site = new DirectoryEntry(metabasePath);
        string className = site.SchemaClassName.ToString();
        if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
        {
          DirectoryEntries vdirs = site.Children;
          DirectoryEntry newVDir = vdirs.Add(vDirName, (className.Replace("Service", "VirtualDir")));
          newVDir.Properties["Path"][0] = physicalPath;
          newVDir.Properties["AccessScript"][0] = true;

          // Create Application, set appropriate documents and permissions
          newVDir.Invoke("AppCreate", new object[1] { 1 });
          newVDir.Properties["AppFriendlyName"].Value = vDirName;
          newVDir.Properties["EnableDirBrowsing"][0] = false;
          newVDir.Properties["AccessRead"][0] = true;
          newVDir.Properties["AccessExecute"][0] = true;
          newVDir.Properties["AccessWrite"][0] = false;
          newVDir.Properties["EnableDefaultDoc"][0] = true;
          newVDir.Properties["DefaultDoc"][0] = "ReportsIsapi.dll?Login,ReportsIsapi.dll";
          newVDir.Properties["AspEnableParentPaths"][0] = true;
          newVDir.CommitChanges();

          Console.WriteLine(" Done.");
        }
        else
          Console.WriteLine(" Failed. A virtual directory can only be created in a site or virtual directory node.");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Failed in CreateVDir with the following exception: \n{0}", ex.Message);
      }
    }
  }
}

Don’t forget to use a double ‘\\’ in your pathnames to avoid the compiler interpreting them as an escape sequence. I certainly did.

Backing up The Latest File – Name Unknown

It’s a common enough issue for me that I want to be able to backup the latest backup of a database, and using variables to set date and time I never quite know what the filename of the backup will be.

::copy latest file to network server
set LF=
for /F %%i in ('dir /Od /b *.sql') DO set LF=%%i
:
echo%TIME% on %DATE%: Latest Backup is %LF% >> Backup.file
:
xcopy /Y %LF% \\networkserver\bugzilla

My solution is to sort the backup directory by date, and create a variable based on the filename of the most recent file.

I then echo the time and date to a file (Backup.file) so that I have a log of the backup process that I can check should I need to in the future.

Finally, the script copies the latest backup to a network location.

This was used in conjunction with my bugzilla backup script, hence the directory names, but it can of course be used for anything.

Automated Backup of Bugzilla Database on Windows

Bugzilla isn’t really designed to be used on Windows, but once set up its fairly easy to administer.

One obviously important thing is backing up the database. Using the niftily built in mysqldump I used the following solution. Note this assumes default settings were used in the installation of Bugzilla.

First, add mysql to the windows path if it hasn’t been done so already, by running up a command prompt:

PATH=%PATH%;C:\Program Files\Bugzilla\mysql\bin\

I then use a command file to create the backup dump, and then append the date and time to the file name. Note I have also created a user (backup) on the bugzilla database with a limited set of privileges (Select, Lock Table, Show Databases, Event).

rem commmand to dump the database to file
mysqldump -ubackup -pbackup Bugs > c:\bugsbackup\bz.sql

rem set date and time into useable file format
set _my_datetime=%date%_%time%
set _my_datetime=%_my_datetime: =_%
set _my_datetime=%_my_datetime::=%
set _my_datetime=%_my_datetime:/=_%
set _my_datetime=%_my_datetime:.=_%

rem rename file
ren "c:\bugsbackup\bz.sql" bz_%_my_datetime%.sql

This command file can of course be added to scheduled tasks and run as frequently as you want.

Event 41 Kernel-Power

Just a quick heads up for people that have this problem as it took me a while to figure out. This was my problem on a Windows 7 64bit Service Pack 1 install on an i5 CPU over-clocked to 4.0GhZ, with a Corsair CX650W. I attempted to put a GTX 580 into my computer, but when I did kept getting this problem. Despite my PSU being rated high enough (on the 12V rail) to supply power, it isn’t sadly.

These are the new “budget” range from Corsair, so perhaps I shouldn’t have been surprised, but it’s a bit irritating given that the specifications of the PSU show it being more than capable. Oh well!

SecurityTools

This is a nasty little program that masquerades as a security program that has found lots of viruses on your PC, and will clean them if you unlock it – eg if you hand over your credit card details.

The easiest fix I’ve found is using ComboFix, and then deleting the [random letter/numbers] folders found under ‘\Documents And Settings\All Users\’.

Windows Home Server v2 (Vail) is dead in the water

I’m a big fan of windows home server. They failed to deliver on some things, such as Media Centre integration, but that doesn’t really bother me. It’s a decent piece of server kit with a nice UI built over the top of Server 2008.

The best feature of it has to be Drive Extender, a disk management tool that allows you to pool your drives, of any size, and provides one click duplication, making installing new disks and taking backups a breeze for the home user. The clue is in the title: Windows Home Server.

Vail has been in the works for a while, and a recent post on the WHS blog has announced that the number one feature has been stripped. I’m fortunate enough, I guess, in that I understand how RAIDs work, and know how to set them up. However, I use WHS because I don’t have too. I spend enough time at work with computers that I don’t want to at home.

Absolutely unbelievable. Microsoft manage to alienate their core customer base (of this product) in one fell swoop.

Still, at least it saves me the cost of the upgrade.

Userenv Error 1500/1505/1508 – Profile Unable to Login

Windows cannot log you on because your profile cannot be loaded. Check that you are connected to the network, or that your network is functioning correctly. If this problem persists, contact your network administrator.

This annoying error message plagued me for quite a long time until I eventually found the solution. The problem appears to be with the pagefile not being able to perform user profile allocation properly.

After a fair bit of searching and tinkering, I eventually hit upon the following solution:

  • First set the pagefile to by managed by the system.
  • Load regedit, and find: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management
  • Create a new DWORD called PoolUsageMaximum. Set this to 60 (Decimal).
  • Create/Modify PagedPoolSize (DWORD) to ffffffff (Hex).
  • Reboot.

What this does is simply force the Memory Manager in windows to attempt to trim the pagefile when it is 60% full, rather than its default 80%. This therefore starts the pagefile trim earlier, hopefully enabling the computer to cope with surges in memory demand more efficiently.

Rooting Your Desire (and installing a custom rom)

This is a guide for the GSM Desire only.

Follow this entirely at your own risk. It is possible, however unlikely that trying to root your phone can brick it.

Things to download:

  • Download Unrevoked 3.21 (current version) from here.
  • Download HTC Sync from here, install it, then uninstall it. This sounds odd, but it will remove the HTC software (a necessary step), whilst leaving the ADB Drivers on your computer.
  • Download a recovery image, I’m using clockwork which you can get here, but you can use others such as AmonRa.
  • Download a custom rom, and a radio rom. I am using Defrost and the latest radio, available here. Note: This step is not actually necessary, you can run a rooted phone with the original rom.
  • This is the actual rooting part. Run Unrevoked (3.21). Select the recovery image you downloaded above.
  • Plug your Desire in to your PC, sit back, follow the on screen instructions, and then wait a few minutes.
  • Note: It is a good idea at this point to perform a Nandroid backup, available from the Clockwork Recovery Mod Menu.
  • When Unrevoked has finished, reboot into your phone. Load Rom Manager, flash Clockwork to the latest version, and take a backup.
  • Partition your SD card (menu option in Rom Manager). I went for a 512mb ext2 partition, a 64mb swap, and the rest as fat32. Copy your custom rom and Radio image to your SD card.
  • From within Rom Manager, select Install Rom from SD Card and select your custom rom. Let it work its magic. You can then install the radio image from within the Clockwork Recovery menu.
  • Bear in mind if you are going from a Sense based rom to a non-Sense based rom, you will need to select Wipe Data/Settings from within Clockwork in order to be able to flash the custom rom. This should be the only time you need to do it.
  • When the phone reboots, you will be presented with an animated ‘X’, which is really rather pleasant. This is the DeFrost boot screen, and will last for a while. Don’t panic, let it run its course, and it will boot eventually.