NullifyNetwork

The blog and home page of Simon Soanes
Skip to content
[ Log On ]

Archive - Historical Articles

You are viewing records from 01/20/2008 13:24:37 to 10/29/2008 18:08:44. I'll be adding support for selecting a date range in future.

Microsoft just announced Windows Azure at the PDC this year, a cloud hosting system!  This is cool because it has the ability to host both ASP.NET web sites (or WCF web services) and back-end compute components (UI less programs that run based on a queue system) with zero up-front infrastructure requirements and the ability to increase the number of systems serving it just by changing a single number!

The scaling is suddenly Microsoft’s problem as long as you write it to be queue based and to use the SDS storage system for your data, which is really very cool.

Combining this with the release of Silverlight 2.0 and you suddenly have the ability to make very-very rich applications available to end clients without the upfront investments you’d normally have to provide.

The only thing I have noticed that is a bit strange is that Microsoft have chosen to only allow developers to use the URL myapp.cloudapp.net rather than their own domain (maybe that is just the PDC account being signed up to use cloudapp.net!).

I tried it offline using the SDK (available at the link above) and the development experience is really quite good but I'm still waiting for access to the real thing to give it a proper test.

Permalink 

So I've been occupied as the schools came back over September and am just starting to catch back up, amongst the things to have snuck up on me are Silverlight and a lovely new report designer!

The Silverlight 2.0 SDK tools are here. They are badly missing any mouse interactivity on the design surface, which makes it seem like this is a big ploy to sell lots of copies of expression Blend to developers since that DOES support mouse control and addition of components.  Since Blend has one of the worst UI's I've ever come across (looks pretty but works like drawing in tar) I think I'll wait till this is fixed up or just edit the XML.  The ability to run C# code in silverlight and debug it properly almost lets Microsoft off but not quite.

However saying that Microsoft have released a new report designer for SQL 2008 Reporting Services.  It uses a slightly different schema for its RDL files than the 2005 report viewer but is an amazing standalone tool.  Hopefully after some work to support the format better (we work fine with 2005) work will be able to point clients at this so they can make their own reports more easily!

Permalink 

I just noticed that the PXE bootloader supplied in the syslinux package (in the gpxe folder) is now based on gPXE (link is to a video google have recorded where they show it off) which supports booting the host operating system over a really wide variety of TCP based protocols!  gPXE is based on etherboot (or is it renamed now as they merged the projects after forking them).

Previously stage 2 PXE bootloaders (like pxelinux) only loaded things using TFTP, but with the addition of a full TCP/IP stack you can now boot your entire computer over HTTP from a standard webserver located anywhere on the planet.

You can even boot a full copy of Windows using iSCSI without a hardware host bus adapter!

You may wonder why this is relevant to someone that does .NET pretty much all the time like me, but I like to boot servers and workstations off the network for backup/restore purposes, rapidly build test systems in virtual PC and to run new .NET based operating systems that are starting to show up.

Edit: I have noticed a lot of people coming to this article so think it's worth elaborating on the whole PXE bootloader process quickly, basically you do the following:-

Configure your DHCP server (or download one, there's a freeware DHCP server for windows if you don't have a proper server, or you can always apt-get install dhcpd and edit the config file) to serve up your normal subnet plus the addition of a bootfile name called (lets assume you're using PXELinux with gPXE!) gpxelinux.0 (see the syslinux package above and extract this file) and the IP or hostname of your boot server.

Your boot server needs to run a TFTP server, in the root of the TFTP server you put a copy of gpxelinux.0 and a config file for it.  For the moment just boot a virtual machine or something and see if it shows the PXE bootloader on startup.

Once you have it loading and erroring about a lack of configuration file, it's time to make a config; for gpxelinux.0 this is just a text file, and there's tons of examples in the syslinux package - but you can just put this in there to get started:-

default local
prompt 1

label local
 MENU label ^Local Boot
 LOCALBOOT 0
 timeout 10

label dsl
 MENU LABEL Linu^x
 linux linux24.bin
 append init=/etc/init lang=us apm=power-off vga=791 ramdisk_size=100000 acpi=off quiet nomce noapic BOOT_IMAGE=knoppix
 initrd minirt24.gz

And stick a copy of linux24.bin and minirt24.gz from DSL or your favourite small linux system in your TFTP server.  I have had great success making those links - so http://yourserver/linux24.bin instead works wonderfully with gPXE.

Permalink 

(It's very rare that I post during work hours, but my excuse is I need to pass the links around between machines and this is the easiest way)

Microsoft have released Visual Studio 2008 Service Pack 1!  Hopefully there's many good fixes in it, I haven't had time to beta test it so am really looking forward to it.

Finally it's also now possible to install SQL Server 2008...

The release is available along with .NET 3.5 SP1 in the form of an ISO image or the web installer!  The release notes (contains details of both updates) are also available.

Permalink  2 Comments 

Just a quick post with the ports for SQL Server as I keep muddling up the browser and the db engine:-

The database engine is port 1433
 
SQL Browser is on port 1434
 
And finally analysis services is on port 2383

Permalink 

Okay so to make up for not having posted a single codeblog entry in several months I have a mammoth one here that has taken over 6 hours to figure out and write.

The following is a class that implements near-full support for the WMI interface to the MS DNS Server on Windows Server 2003 (also should work on Windows Server 2008 and if you install an add-on you can probably use it under Server 2000 but I have only briefly tried server 2008).  I wrote this out of frustration when I googled for an easy way to create a DNS record programmatically in Microsoft DNS Server and there was a distinct absence of actually useful information (there were just a couple of poor examples and no really complete solutions).  You can use this to allow you to modify a DNS zone in C# (dynamic DNS hosting under Windows anyone?) or for running your webhosting businesses automation from ASP.NET.  Note that I haven't tested deleting!

This class requires both a reference to and a using statement for System.Management from .NET 2.0.  I included the entire thing as if it were a single file for the ease of demonstration - you probably want to remove the DNSServer class and its helper classes (which are included within it) and put them into a seperate file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;

namespace DNSManager
{
    #region Example usage code
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Connecting to the DNS Server...");
            DNSServer d = new DNSServer("vex.nullify.net"); //my internal DNS Server, change to yours.
                                                            //You will need to be able to get to it using WMI.
            Console.WriteLine("Connected to the DNS Server");

            Console.Write("Creating a new zone as a test...");
            try
            {
                d.CreateNewZone("testzone.uk.nullify.net.", DNSServer.NewZoneType.Primary);
                Console.WriteLine("OK");
            }
            catch (Exception)
            {
                Console.WriteLine("Failed to create a new zone, it probably exists.");
            }

            Console.Write("Creating a DNS record as a test...");
            try
            {
                d.CreateDNSRecord("testzone.uk.nullify.net.", "test1.testzone.uk.nullify.net. IN CNAME xerxes.nullify.net.");
                Console.WriteLine("OK");
            }
            catch (Exception)
            {
                Console.WriteLine("Failed to create a new resource record, it probably exists");
            }

            Console.WriteLine("Getting a list of domains:");
            foreach (DNSServer.DNSDomain domain in d.GetListOfDomains())
            {
                Console.WriteLine("\t"+domain.Name+" ("+domain.ZoneType+")");
                //and a list of all the records in the domain:-
                foreach (DNSServer.DNSRecord record in d.GetRecordsForDomain(domain.Name))
                {
                    Console.WriteLine("\t\t"+record);
                    //any domains we are primary for we could go and edit the record now!
                }
            }

            Console.WriteLine("Fetching existing named entry (can be really slow, read the warning):-");
            DNSServer.DNSRecord[] records = d.GetExistingDNSRecords("test1.testzone.uk.nullify.net.");
            foreach (DNSServer.DNSRecord record in records)
            {
                Console.WriteLine("\t\t" + record);
                record.Target = "shodan.nullify.net.";
                record.SaveChanges();
            }
        }
    }
    #endregion

    #region Real code

    /// <summary>
    /// A Microsoft DNS Server class that abstracts out calls to WMI for MS DNS Server
    /// </summary>
    /// <remarks>
    /// WMI Documentation: 
    /// http://msdn.microsoft.com/en-us/library/ms682123(VS.85).aspx
    /// System.Management Documentation: 
    /// http://msdn.microsoft.com/en-us/library/system.management.managementobjectcollection%28VS.71%29.aspx
    /// </remarks>
    /// <c>(c) 2008 Simon Soanes, All Rights Reserved.  No warranties express or implied.
    /// DO NOT redistribute this source code publically without a link to the origin and this copyright
    /// notice fully intact, also please send any modifications back to me at simon@nullifynetwork.com
    /// Including in your software is fine although attribution would be nice.</c>
    public class DNSServer
    {

        #region Supporting classes
        /// <summary>
        /// Different types of DNS zone in MS DNS Server
        /// </summary>
        public enum ZoneType
        {
            DSIntegrated,
            Primary,
            Secondary
        }

        /// <summary>
        /// Different types of DNS zone in MS DNS Server
        /// </summary>
        /// <remarks>For creation of new zones the list is different</remarks>
        public enum NewZoneType
        {
            Primary,
            Secondary,
            /// <remarks>Server 2003+ only</remarks>
            Stub,
            /// <remarks>Server 2003+ only</remarks>
            Forwarder
        }

        /// <summary>
        /// A zone in MS DNS Server
        /// </summary>
        public class DNSDomain
        {        
            /// <summary>
            /// Create a DNS zone
            /// </summary>
            /// <param name="name">The name of the DNS zone</param>
            /// <param name="wmiObject">The object that represents it in MS DNS server</param>
            /// <param name="server">The DNS Server it is to be managed by</param>
            public DNSDomain(string name, ManagementBaseObject wmiObject, DNSServer server)
            {
                _name = name;
                _wmiObject = wmiObject;
                _server = server;
            }

            private DNSServer _server = null;

            private string _name = "";

            /// <summary>
            /// The name of the DNS zone
            /// </summary>
            public string Name
            {
                get { return _name; }
                set { _name = value; }
            }

            /// <summary>
            /// The zone type
            /// </summary>
            public ZoneType ZoneType
            {
                get
                {
                    //_wmiObject["ZoneType"].ToString()
                    return (ZoneType)Convert.ToInt32(_wmiObject["ZoneType"]);
                }
            }

            /// <summary>
            /// Is this a reverse DNS zone?
            /// </summary>
            public bool ReverseZone
            {
                get
                {
                    if (_wmiObject["Reverse"].ToString() == "1")
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }

            private ManagementBaseObject _wmiObject = null;

            /// <summary>
            /// Get a list of all objects at the base of this zone
            /// </summary>
            /// <returns>A list of <see cref="DNSRecord"/></returns>
            public DNSRecord[] GetAllRecords()
            {
                return _server.GetRecordsForDomain(_name);
            }

            /// <summary>
            /// Create a new DNS host record
            /// </summary>
            /// <param name="record">The record to create</param>
            public void CreateDNSRecord(DNSRecord record)
            {
                _server.CreateDNSRecord(_name, record.ToString());
            }

            public override string ToString()
            {
                return _name;
            }
        }

        /// <summary>
        /// An entry in a zone
        /// </summary>
        public class DNSRecord
        {
            /// <summary>
            /// Create an class wrapping a DNS record
            /// Defaults to 1 hour TTL
            /// </summary>
            /// <param name="domain"></param>
            /// <param name="recordType"></param>
            /// <param name="target"></param>
            public DNSRecord(string domain, DNSRecordType recordType, string target) : 
                this(domain, recordType, target, new TimeSpan(1, 0, 0))
            {
            }

            /// <summary>
            /// Create an class wrapping a DNS record
            /// </summary>
            /// <param name="domain"></param>
            /// <param name="recordType"></param>
            /// <param name="target"></param>
            /// <param name="ttl"></param>
            public DNSRecord(string domain, DNSRecordType recordType, string target, TimeSpan ttl)
            {
                _host = domain;
                _ttl = ttl;
                _target = target;
                _recordType = recordType;
            }

            /// <summary>
            /// Create an class wrapping a DNS record
            /// </summary>
            /// <param name="wmiObject"></param>
            public DNSRecord(ManagementObject wmiObject)
            {
                _wmiObject = wmiObject;
                _host = wmiObject["OwnerName"].ToString();
                _target = wmiObject["RecordData"].ToString();
                string[] recordParts = wmiObject["TextRepresentation"].ToString().Split(' ', '\t');
                if (recordParts.Length > 2)
                {
                    //the third offset is the location in the textual version of the data where the record type is.
                    //counting from zero that is location 2 in the array.
                    _recordType = new DNSRecordType(recordParts[2]); 
                }
                _ttl = new TimeSpan(0, 0, Convert.ToInt32(wmiObject["TTL"]));
            }

            private ManagementObject _wmiObject = null;

            private string _target = "";

            /// <summary>
            /// The value of the target is what is written to DNS as the value of a record
            /// </summary>
            /// <remarks>For MX records include the priority as a number with a space or tab between it and the actual target</remarks>
            public string Target
            {
                get { return _target; }
                set { _target = value; }
            }

            /// <summary>
            /// Save the changes to the resource record
            /// </summary>
            public void SaveChanges()
            {
                //We can call modify and if we have the method available it will work as the sub-class may have it!!
                //Some types DO NOT implement it or implement it differently

                ManagementBaseObject parameters = _wmiObject.GetMethodParameters("Modify");
                bool supported = false;

                //This is a cludge based on the various types that are implemented by MS as they didn't stick to a simple value

                //To add more, please refer to 
                if (RecordType.TextRepresentation == "A")
                {
                    parameters["IPAddress"] = _target;
                    parameters["TTL"] = _ttl.TotalSeconds;
                    supported = true;
                }

                if (RecordType.TextRepresentation == "AAAA")
                {
                    parameters["IPv6Address"] = _target;
                    parameters["TTL"] = _ttl.TotalSeconds;
                    supported = true;
                }

                if (RecordType.TextRepresentation == "CNAME")
                {
                    parameters["PrimaryName"] = _target;
                    parameters["TTL"] = _ttl.TotalSeconds;
                    supported = true;
                }

                if (RecordType.TextRepresentation == "TXT")
                {
                    parameters["DescriptiveText"] = _target;
                    parameters["TTL"] = _ttl.TotalSeconds;
                    supported = true;
                }

                if (RecordType.TextRepresentation == "MX")
                {
                    string[] components = _target.Trim().Split(' ', '\t');
                    if (components.Length > 1)
                    {
                        parameters["Preference"] = Convert.ToUInt16(components[0]); //the preference is a UINT16 in MS DNS Server
                        parameters["MailExchange"] = components[1]; //the actual host name
                        //NOT SUPPORTED BY MX ACCORDING TO THE DOCUMENTATION!? parameters["TTL"] = _ttl;
                        supported = true;
                    }
                }

                Exception temporaryException = null;
                try
                {
                    //This supports improving this classes implementation of this method and adding 
                    ManagementBaseObject lastDitchParameters = OnSaveChanges(parameters);
                    if (lastDitchParameters != null)
                    {
                        parameters = lastDitchParameters;
                        supported = true;
                    }
                }
                catch (Exception ex) //catch all as we do not know what someone will modify OnSaveChanges() to throw or cause
                {
                    if (!supported) //if we support the data type already then we don't care about exceptions as at worst case
                        throw;
                    else
                        temporaryException = ex;
                }

                if (supported)
                {
                    try
                    {
                        _wmiObject = (ManagementObject)_wmiObject.InvokeMethod("Modify", parameters, null);
                    }
                    catch (Exception ex)
                    {
                        if (temporaryException != null)
                        {
                            throw new ApplicationException("There were two exceptions, the primary failure"+
                                " was an exception that is encapsulated in this message however additionaly "+
                                "a virtual method that was optional to functionality also threw an exception "+
                                "but this was withheld till after the operation failed. Please examine the"+
                                " InnerException property for copy of the virtual methods exception.  The "+
                                "virtual methods exception message was: " + temporaryException.Message+".  "+
                                "The primary exceptions message (a "+ex.GetType().FullName.ToString()+") "+
                                "was: "+ex.Message, temporaryException);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    
                    if (temporaryException != null)
                    {
                        throw new ApplicationException("A virtual method that was optional to functionality "+
                            "threw an exception but this was withheld till after the operation completed "+
                            "successfully, please examine the InnerException property for a full copy of this "+
                            "exception.  The message was: " + temporaryException.Message, temporaryException);
                    }
                }
                else
                {
                    throw new NotSupportedException("The data type you attmpted to use ("+
                        RecordType.TextRepresentation+") was not supported, please implement support for"+
                    "it by overriding the method OnSaveChanges() and returning an array of filled WMI parameters "+
                    "or by updating this implementation.");
                }
            }

            /// <summary>
            /// Method to override to add additional methods to the DNS save changes support
            /// </summary>
            /// <param name="parametersIn">An array of parameters (not yet filled in if it's an 
            /// unknown type, potentially partially filled for known types)</param>
            /// <returns>An array of filled in parameters, or null if the parameters are unknown</returns>
            public virtual ManagementBaseObject OnSaveChanges(ManagementBaseObject parametersIn)
            {
                return null;
            }

            /// <summary>
            /// Delete a record from DNS
            /// </summary>
            public void Delete()
            {
                _wmiObject.Delete();
                //well that was easy...
            }

            private TimeSpan _ttl = new TimeSpan(0, 1, 0);

            /// <summary>
            /// The time that the resolvers should cache this record for
            /// </summary>
            public TimeSpan Ttl
            {
                get { return _ttl; }
                set { _ttl = value; }
            }

            private DNSRecordType _recordType = null;

            /// <summary>
            /// The record type
            /// </summary>
            public DNSRecordType RecordType
            {
                get { return _recordType; }
            }
            private string _host = "";

            /// <summary>
            /// The location in the DNS system for this record
            /// </summary>
            public string DomainHost
            {
                get { return _host; }
                set { _host = value; }
            }

            public override string ToString()
            {
                return _host + " " + _recordType.ToString() + " " + _target;
            }
        }

        /// <summary>
        /// The type of record in MS DNS Server
        /// </summary>
        public class DNSRecordType
        {
            /// <summary>
            /// Create a new DNS record type
            /// </summary>
            /// <param name="textRepresentation">The type to create</param>
            public DNSRecordType(string textRepresentation)
            {
                _textRepresentation = textRepresentation;
            }
            private string _textRepresentation = "";

            /// <summary>
            /// The text representation of the record type
            /// </summary>
            public string TextRepresentation
            {
                get
                {
                    return _textRepresentation.ToUpper();
                }
            }

            private string _recordMode = "IN";

            /// <summary>
            /// The mode of the record, usually IN but could oneday be something else like OUT
            /// </summary>
            public string RecordMode
            {
                get
                {
                    return _recordMode;
                }
                set
                {
                    _recordMode = value;
                }
            }

            public override string ToString()
            {
                return _recordMode+" "+_textRepresentation;
            }

            #region Some Defaults!
            /// <summary>
            /// An alias
            /// </summary>
            public static DNSRecordType CNAME 
            {
                get    { return new DNSRecordType("CNAME"); }
            }

            /// <summary>
            /// An IPv4 address
            /// </summary>
            public static DNSRecordType A
            {
                get { return new DNSRecordType("A"); }
            }
            
            /// <summary>
            /// A reverse host address inside yoursubnet.in-addr.arpa
            /// </summary>
            public static DNSRecordType PTR
            {
                get { return new DNSRecordType("PTR"); }
            }
    
            /// <summary>
            /// An MX record (mail exchange)
            /// </summary>
            public static DNSRecordType MX
            {
                get { return new DNSRecordType("MX"); }
            }

            /// <summary>
            /// An IPv6 host address
            /// </summary>
            public static DNSRecordType AAAA
            {
                get { return new DNSRecordType("AAAA"); }
            }

            /// <summary>
            /// A text record
            /// </summary>
            public static DNSRecordType TXT
            {
                get { return new DNSRecordType("TXT"); }
            }

            /// <summary>
            /// A nameserver record (domain delegation)
            /// </summary>
            public static DNSRecordType NS
            {
                get { return new DNSRecordType("NS"); }
            }

            /// <summary>
            /// An SOA record (start of authority)
            /// </summary>
            public static DNSRecordType SOA
            {
                get { return new DNSRecordType("SOA"); }
            }

            #endregion
        }
        
#endregion

        /// <summary>
        /// Create a new DNS Server connection
        /// </summary>
        /// <param name="server">The hostname, IP or FQDN of a DNS server you have access to with the current credentials</param>
        public DNSServer(string server)
        {
            ConnectionOptions co = new ConnectionOptions();
            _scope = new ManagementScope(String.Format(@"\\{0}\Root\MicrosoftDNS", server), co);
            _scope.Connect();  //no disconnect method appears to exist so we do not need to manage the 
                               //persistence of this connection and tidy up
        }

        /// <summary>
        /// Create a new DNS Server connection
        /// </summary>
        /// <param name="server">The hostname, IP or FQDN of a DNS server you have access to with the current credentials</param>
        /// <param name="username">The username to connect with</param>
        /// <param name="password">The users password</param>
        public DNSServer(string server, string username, string password)
        {
            ConnectionOptions co = new ConnectionOptions();
            co.Username = username;
            co.Password = password;
            co.Impersonation = ImpersonationLevel.Impersonate;
            _scope = new ManagementScope(String.Format(@"\\{0}\Root\MicrosoftDNS", server), co);
            _scope.Connect();
        }

        private string _server = "";

        /// <summary>
        /// The server this connection applies to
        /// </summary>
        public string Server
        {
            get { return _server; }
        }

        private ManagementScope _scope = null;

        /// <summary>
        /// Return a list of domains managed by this instance of MS DNS Server
        /// </summary>
        /// <returns></returns>
        public DNSDomain[] GetListOfDomains()
        {
            ManagementClass mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_Zone"), null);
            mc.Get();
            ManagementObjectCollection collection =  mc.GetInstances();

            List<DNSDomain> domains = new List<DNSDomain>();
            foreach (ManagementObject p in collection)
            {
                domains.Add(new DNSDomain(p["ContainerName"].ToString(), p, this));
            }

            return domains.ToArray();
        }

        /// <summary>
        /// Return a list of records for a domain, note that it may include records
        /// that are stubs to other domains inside the zone and does not automatically
        /// recurse.
        /// </summary>
        /// <param name="domain">The domain to connect to</param>
        /// <returns></returns>
        public DNSRecord[] GetRecordsForDomain(string domain)
        {
            string query = String.Format("SELECT * FROM MicrosoftDNS_ResourceRecord WHERE DomainName='{0}'", domain);
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery(query));
            
            ManagementObjectCollection collection = searcher.Get();

            List<DNSRecord> records = new List<DNSRecord>();
            foreach (ManagementObject p in collection)
            {
                records.Add(new DNSRecord(p));
            }

            return records.ToArray();
        }

        /// <summary>
        /// Create a new DNS host record
        /// </summary>
        /// <param name="zone"></param>
        /// <param name="bindStyleHostEntry"></param>
        /// <returns></returns>
        public void CreateDNSRecord(string zone, string bindStyleHostEntry)
        {
            try
            {
                ManagementObject mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_ResourceRecord"), null);
                mc.Get();
                ManagementBaseObject parameters = mc.GetMethodParameters("CreateInstanceFromTextRepresentation");
                parameters["ContainerName"] = zone;
                parameters["DnsServerName"] = _server;
                parameters["TextRepresentation"] = bindStyleHostEntry;
                ManagementBaseObject createdEntry = mc.InvokeMethod("CreateInstanceFromTextRepresentation", parameters, null);
                //return createdEntry; (no reason unless you changed your mind and wanted to delete it?!)
            }
            catch (ManagementException) //the details on this exception appear useless.
            {
                throw new ApplicationException("Unable to create the record " + bindStyleHostEntry + ", please check"+
                    " the format and that it does not already exist.");
            }
        }

        /// <summary>
        /// Create a new DNS host record
        /// </summary>
        /// <param name="zone"></param>
        /// <param name="record"></param>
        public void CreateDNSRecord(string zone, DNSRecord record)
        {
            CreateDNSRecord(zone, record.ToString());
        }

        /// <summary>
        /// Fetch DNS records for a particular name
        /// WARNING: This method has performance issues, iterate over the results of getting all the records for a domain instead.
        /// </summary>
        /// <remarks>Returns a collection as one hostname/entry can have multiple records but it can take longer
        /// than getting all the records and scanning them!</remarks>
        /// <param name="hostName">The name to look up</param>
        /// <returns></returns>
        public DNSRecord[] GetExistingDNSRecords(string hostName)
        {
            string query = String.Format("SELECT * FROM MicrosoftDNS_ResourceRecord WHERE OwnerName='{0}'", hostName);
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery(query));

            ManagementObjectCollection collection = searcher.Get();
            List<DNSRecord> records = new List<DNSRecord>();
            foreach (ManagementObject p in collection)
            {
                records.Add(new DNSRecord(p));
            }

            return records.ToArray();
        }

        /// <summary>
        /// Create a new zone in MS DNS Server
        /// </summary>
        /// <param name="zoneName">The zone to create</param>
        /// <param name="zoneType">The type of zone to create</param>
        /// <returns>The domain</returns>
        public DNSDomain CreateNewZone(string zoneName, NewZoneType zoneType)
        {
            try
            {
                ManagementObject mc = new ManagementClass(_scope, new ManagementPath("MicrosoftDNS_Zone"), null);
                mc.Get();
                ManagementBaseObject parameters = mc.GetMethodParameters("CreateZone");

                /*
                [in]            string ZoneName,
                [in]            uint32 ZoneType,
                [in]            boolean DsIntegrated,   (will always be false for us, if you need AD integration you will need to change this.
                [in, optional]  string DataFileName,
                [in, optional]  string IpAddr[],
                [in, optional]  string AdminEmailName,
                */

                parameters["ZoneName"] = zoneName;
                parameters["ZoneType"] = (UInt32)zoneType;
                parameters["DsIntegrated"] = 0; //false
                ManagementBaseObject createdEntry = mc.InvokeMethod("CreateZone", parameters, null);
                DNSDomain d = new DNSDomain(zoneName, createdEntry, this);
                return d;
            }
            catch (ManagementException) //returns generic error when it already exists, I'm guessing this is a generic answer!
            {
                throw new ApplicationException("Unable to create the zone "+zoneName+", please check "+
                    "the format of the name and that it does not already exist.");
            }
        }

        public override string ToString()
        {
            return _server;
        }
    }
    #endregion
}
Permalink  8 Comments 

Here I am installing SQL Server 2008 and I notice that it's installing .NET Framework 3.5 SP1.

There's a total lack of announcements so I'm not sure, but given that it's bonded at the hip with Visual Studio 2008 SP1 I am expecting it to install that next (and saw it when extracting the ISO).  Only thing is it seems (updated this post after hitting this) you can't use the SP1 that is shipped with SQL server to update your normal copy of VS2k8, it only does the limited shell.

I have found what seems to be the sole blog entry over at blogs.msdn.com that has any mention of it: http://blogs.msdn.com/euanga/archive/2008/08/07/sql-server-2008-installation-confusion-vs-2008-sp1-and-netfx-3-5-sp1.aspx which seems to confirm it's the RTM version at least but it opens a can of worms by saying that you need VS 2008 SP1 and that it isn't available to install against the non-sql server bits.

Regardless of if it has been officially released, .NET 3.5 SP1 (but apparently not yet Visual Studio SP1 and all its goodness) is certainly out now anyway...

Permalink 

Well the subject says it all!

MSDN subscribers can now download SQL Server 2008 - I'm sure an SQL Express 2008 edition will come out soon.  Interestingly there is an extra edition available on MSDN - "Web Edition", this is pure speculation but I'm guessing this is standard edition without CALs (or SQL Express without the memory, cpu and filesize restrictions if you want to look at it that way) so you can use it on webservers.

I downloaded a copy of it this morning and will install it in a bit to try it out.  Hopefully the software at work will be fine with it (we tested on an old release candidate but haven't recently) but it'll be good to have the new data types (finally we get a seperate date and time type, and the spatial data types are awesome) and an improved management studio either way.

Another feature that will be good is the ability to dictate the resources a particular database (or user) can take up, preventing a particular website or application consuming all the resources on a server.

Permalink 

http://fantasticcontraption.com/ is quite possibly the best free web game I've encountered.

In it you get to solve complex (or seemingly simple looking) physics puzzles to move one item to another with only bars that you join together and motors (wheels in the game) that turn uncontrollably.  It is then your job to make sense of these and make them do what you command!  A very simple premise that rapidly gets out of control.

(Chris gets the blame for suggesting it to me and making it consume a huge amount of my free time today...)

Permalink 

My site and a bunch of hosted sites were down over the weekend due to an explosion at the datacentre where the servers are located in Houston.  This is now fixed.

No e-mail was lost as the server in New York was still up, but it's still mighty annoying.

Permalink 

I just encountered a small tool Microsoft released in 2005 called Log Parser 2.2.  This is a tool that allows you to take literally any data format anywhere and turn it into anything, very handy - particularly when processing IIS logfiles.

The site that used to exist for it has vanished, but there's still a few good resources out there related to it:-

Alexis Eller's talk on using log parser (I couldn't find a blog for her unfortunately)

Professor Windows - May 2005 article which has a lot of good examples.

Update - I am trying to build a replacement for tools like Webalizer which aren't maintained anymore (or are insanely complex to install and configure and maintain strange files as 'summaries').  I am currently using the following command to create and maintain a multiple GB database containing an indexed set of logfiles and getting back graphs of the data instantly for display in Open Flash Chart.

The command I'm using is:

"C:\Program Files\Log Parser 2.2\logparser" "select 1, * into WebLog 
from <MyIISSiteNumber>" -i:IISW3C -o:SQL -server:(local) -database:NullifyDBLog 
-driver:"SQL Server" -createTable:ON 
-transactionRowCount 100 -iCheckpoint:CheckpointFile.lpc

Which is working really well, it took about 30 minutes to process 4 or 5 years of logs and now updates the database near instantly!

Watch out for the fact that LogParser isn't too bright when making tables, to do this with my log format you need to create extra indexes, I opted for indexing obvious things like date, time, the uri, the referrer, the bytes transferred and the user agent.

Permalink 

Just so I don't forget, Visio UML generation is no longer available in Visual Studio 2008 out of the box; to add it back in just run the following reg file (adapted for your file paths obviously):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Editors\{742C6336-9457-4885-8778-CBEC892F8EA2}]
"DisplayName"="#14"
"Package"="{FED78099-10D1-4A78-B037-ABCFA1A107B3}"
"ExcludeDefTextEditor"=dword:00000001
@="Visio Editor"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Editors\{742C6336-9457-4885-8778-CBEC892F8EA2}\Extensions]
"vsd"=dword:00000032

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\OutputWindow\{BC5C26E6-DF2B-46C9-B74E-0E057228055D}]
@="#2"
"Package"="{FED78099-10D1-4A78-B037-ABCFA1A107B3}"
"InitiallyInvisible"=dword:00000001
"Name"="Visio UML"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Packages\{FED78099-10D1-4A78-B037-ABCFA1A107B3}]
"InprocServer32"="C:\\PROGRA~1\\MICROS~1\\Office12\\UMLVS.DLL"
@="VisioUml Package"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Packages\{FED78099-10D1-4A78-B037-ABCFA1A107B3}\SatelliteDll]
"Path"="C:\\Program Files\\Microsoft Office\\Office12\\"
"DllName"="UMLVSUI.Dll"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Menus]
"{FED78099-10D1-4A78-B037-ABCFA1A107B3}"=", 1000, 1"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Projects\{D1DCDB85-C5E8-11D2-BFCA-00C04F990235}\AddItemTemplates\TemplateDirs\{FED78099-10D1-4A78-B037-ABCFA1A107B3}\/1031]
@="#213"
"TemplatesDir"="C:\\Program Files\\Microsoft Office\\Office12\\1031\\Vsdir\\"
"Package"="{FED78099-10D1-4A78-B037-ABCFA1A107B3}"
"SortPriority"=dword:00000032

(Taken from http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2488400&SiteID=1 )

Permalink 

I was trying to get speech recognition to work as easily as text-to-speech synthesis and noticed that there's a gotcha in that you can't use it from an MTA STA thread, so you need to invoke it on another thread.  Calling BeginInvoke on an anonymous delegate instance of voidDelegate sorts this pretty easily and is a nice and brief method of avoiding the problem.

Here's the necessary code to make a blank form with two text boxes (called TextBox1 and TextBox2) do speech recognition continually.  To use this code you need a reference to System.Speech.DLL and a using clause pointing to System.Speech.Recognition.

Note that hypothesized text is also displayed, so you can see the speech recognition engine 'thinking' which is handy as it lets you tell if you need to do more training with the engine.

private void Form1_Load(object sender, EventArgs e)

{

       InitSpeechRecognition();

}

 

SpeechRecognitionEngine _recognitionEngine;

 

public void InitSpeechRecognition()

{

       voidDelegate d = new voidDelegate(initSpeechRecognition);

       d.BeginInvoke(null, null);

}

 

private delegate void voidDelegate();

 

private void initSpeechRecognition()

{

       _recognitionEngine = new System.Speech.Recognition.SpeechRecognitionEngine();

       _recognitionEngine.SetInputToDefaultAudioDevice();

       DictationGrammar d = new DictationGrammar();

       d.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(d_SpeechRecognized);

       _recognitionEngine.UnloadAllGrammars();

       _recognitionEngine.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(r_SpeechHypothesized);

       _recognitionEngine.RecognizeCompleted += new EventHandler<RecognizeCompletedEventArgs>(r_RecognizeCompleted);

       _recognitionEngine.LoadGrammar(d);

       _recognitionEngine.RecognizeAsync();

}

 

void r_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)

{

       BeginRecognition();

}

 

delegate void SpeechHypothesizedPassThroughDelegate(object sender, SpeechHypothesizedEventArgs e);

 

void r_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)

{

       if (this.InvokeRequired)

       {

              this.Invoke(new SpeechHypothesizedPassThroughDelegate(r_SpeechHypothesized), sender, e);

       }

       else

       {

              textBox2.Text = e.Result.Text;

       }

}

 

delegate void SpeechPassThroughDelegate(object sender, SpeechRecognizedEventArgs e);

 

void d_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)

{

       if (this.InvokeRequired)

       {

              this.Invoke(new SpeechPassThroughDelegate(d_SpeechRecognized), sender, e);

       }

       else

       {

              //display the recognised text, or process it here

              textBox1.Text += e.Result.Text + " ";

       }

}

 

private void BeginRecognition()

{

       new voidDelegate(delegate()

       {

              _recognitionEngine.RecognizeAsync();

       }).BeginInvoke(null, null);

}

 

private void StopRecognition()

{

       new voidDelegate(delegate()

       {

              _recognitionEngine.RecognizeAsyncStop();

       }).BeginInvoke(null, null);

}

Permalink 

There's an example over on divelements blog about how to host the winforms designer, as there seems to be a lack of documentation anywhere... Permalink 

I wanted to run a bunch of methods simultaneously on as many threads as possible to get the job done, but still wait at the end.  I know MS have something coming to solve this, but wanted a lightweight solution, so here it is:

public class Parallel
{
       public static T[] ExecuteWaitAll<T>(Func<T>[] functions)
       {
              List<T> resultSet = new List<T>();
              int i = 0;
              object lockObject = new object();
              foreach (Func<T> function in functions)
              {
                     lock (lockObject)
                     {
                           i++;
                           function.BeginInvoke(delegate(IAsyncResult result)
                           {
                                  lock (lockObject)
                                  {
                                         resultSet.Add(function.EndInvoke(result));
                                         i--;
                                  }
                           }, null);
                     }
              }
 
              while (i > 0)
              {
                     Thread.Sleep(1);
              }
              return resultSet.ToArray();
       }
}

To use this, you simply call it with a list of delegates you want to execute, and define the return type:

public void ExecuteWait()
{
       List<Func<int>> list = new List<Func<int>>();
       for (int i=0; i<100; i++)
       {
              list.Add(new Func<int>(delegate()
                     {
                           Thread.Sleep(1000);
                           return 1;
                     }));
       }
      
       int[] quantity = Parallel.ExecuteWaitAll<int>(list.ToArray());
       int count = 0;
       foreach (int result in quantity)
       {
              count += result;
       }
}

The result is now valid for 100 executions but took a lot less time than 100 seconds to calculate.  You could of course just be running four things at the same time, all different using this.

Permalink 

This is really simple but annoyed me for a few minutes until I got .NET Reflector up, so hopefully this will help someone else.

The render method on ASP.NET WebControl based server controls seems to be adding a span tag around the HTML output for some reason, to fix this simply override the Render method:

protected override void Render(HtmlTextWriter writer)
{
    RenderContents(writer);
}

Permalink 

Well Microsoft have just released Windows Server 2008 and Visa service pack 1 to manufacturing.  Still waiting for SQL Server 2008, but the stack is almost complete :)

Excellent features I'm looking forward to are:

  • IIS 7 on a server OS!
  • IPv6 file and print sharing (and oh my goodness is file and print sharing faster)
  • IPv6 over VPN!!
  • RDP over IPv6! (Can you tell I'm an IPv6 fan)
  • Application level terminal services (like Citrix has)
  • Fail back router support!  If you bring one down and then back up, it doesn't keep using the failover!
  • IPv6 enabled by default and not uninstallable (disabling will have to do for people who don't want to use it which should result in a lot more IPv6 compatible servers).
  • Read only domain controllers - good for if the domain controller is sighted somewhere insecure.
  • ADAM is now a first class citizen if you need an LDAP server but not active directory.  Active directory is also no longer completely intrinsyc to the server once promoted too!

The only unfortunate thing I can see is the removal of the basic firewall and OSPF from routing and remote access, but the basic firewall has been replaced by windows firewall - I just hope you can still define rules for the other hosts in the network.

Permalink  3 Comments 

There's an article on MSDN about how to host the Windows Workflow Foundation design surface (which is a redistributable).

The example code is really complete and worth a look at, it's almost an out-of-the-box copy of just the workflow designer from Visual Studio.

If your end user requires more flexibility than most you can offer them drag-and-drop customisation of particular processes in your system (like creating a new customer could be made to go off and send details to a webservice without you, the developer; needing to get involved).

Permalink 

Just a single snippet of code to work from, I included explanations in the comments. This is really a very easy to use feature in C# 3. Unfortunately you can’t create extension properties yet, but that will likely be coming soon.

namespace ExtensionMethods
{
       //Note that this class is static, it must be to contain extension methods and
       //it also can't reside inside another class.
       public static class ExampleExtensionMethod
       { 
              //This is the extension method
              public static bool HasLetterAInIt(this string s)
              {
                     //Just return if it contains the capital letter A as this is just an
                     //example
                     return s.Contains("A");
              }
       }
      
       class Program
       {
              static void Main(string[] args)
              {
                     //This will print false
                     Console.WriteLine("TEST".HasLetterAInIt().ToString());
                     //whilst this will print true
                     Console.WriteLine("AAHH!!".HasLetterAInIt().ToString());
              }
       }
}

Permalink 

Create a .NET 3.5 Console, Windows Forms or WPF project, add an SQL Compact Edition local database, I left the default name of Database1.sdf for the example. Just right click and open it from solution explorer and add some tables. I added two tables:  Person and Pet, with a relationship between the two (right click the child table, choose properties to get the add relationship option).

Next you want to add a command to visual studios tools menu, using Tools, External Tools… This command will allow you to generate a Linq To SQL DBML file which describes the contents of the database. Once we have that Linq is perfectly compatible with SQL Compact Edition – but the Visual Studio design tools aren’t so we need to do this manually.
Title:  Make &Linq classes for Database
Command:  C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SqlMetal.exe
Arguments: $(ItemPath) /dbml:$(ItemFileName).dbml
Initial Directory: $(ItemDir)
Now this tool will let you generate your DBML, select your Database1.sdf file then choose Tools, “Make Linq classes for Database”. It should pop up and seemingly do something in a command prompt.
Now right click your project, choose Add existing item and then change the filetype to either Data Files or All files. You should see there’s a new file called Database1.dbml – select this file and add it to your solution.
Bingo! It is now available to be edited in Linq – you can double click this dbml file and you’ll get the designer up – it should show your classes (NOTE: At this point I should add that relationships weren’t automatically generated for compact edition).
Now it’s time to use Linq to actually connect and query/save some data. This is where Linq takes a lot of the hassle out of building software that talks to databases, it simply works.
static void Main(string[] args)
{
       //Connect to the database itself.
       using (Database1 db = new Database1("Database1.sdf"))
       {
              //This is easy because we used SqlMetal to generate
              //the dbml targetting an SQL Compact edition database.
              //Normally you'd have to specify a full connection string.
 
              //obviously remove these two lines if you don't want to start with an empty database each time.
              db.Person.DeleteAllOnSubmit(db.Person);
              db.SubmitChanges();
 
              //Create a couple of Person entities
              Person simon = new Person();
              simon.Name = "Simon";
              simon.EMail = "me@myhost";
 
              //and a cat for me
              Pet cat = new Pet();
              cat.Name = "Fluffy";
              simon.Pets.Add(cat);
 
              db.Person.InsertOnSubmit(simon);
 
              Person fred = new Person();
              fred.Name = "Fred";
              fred.EMail = "them@myhost";
              db.Person.InsertOnSubmit(fred);
 
              //now actually add them to the database
              db.SubmitChanges();
 
              //Select the names of some people                                   
              var names = from Person p in db.Person
                                  select p.Name;
 
              //Print those names
              foreach (string name in names)
              {
                     Console.WriteLine(name);
              }
 
              //but you can also get back the entities instead of just names
              //this is handy if you require more than just one item for a particular person
              var people = from Person p in db.Person
                                   select p;
 
              foreach (Person person in people)
              {
                     Console.WriteLine(String.Format("{0} has an e-mail adddress of {1}", person.Name, person.EMail));
              }
 
              //but what if you want multiple items not all in one person?
              //well you can use anonymous classes
              var peopleAndPets = from Person p in db.Person
                                                orderby p.Pets.Count descending
                                                select new { p.Name, p.Pets.Count };
 
              //this is where the var keyword becomes essential. peopleAndPets
              //is not of a type that can be described before compilation.
              foreach (var quantity in peopleAndPets)
              {
                     Console.WriteLine(String.Format("{0} has {1} pets", quantity.Name, quantity.Count.ToString()));
              }
       }
 
       Console.WriteLine("The example is over!");
       Console.ReadKey();
}
Permalink  1 Comments 

This is really easy and quick, but something that is handy. Getting speech synthesis to work under .NET 3.0 is really a breeze.

First, add a reference to System.Speech – this contains the managed speech API, it allows you to do recognition and synthesis extremely easily.
Next, add the following to an empty console application/button click event:
using (System.Speech.Synthesis.SpeechSynthesizer synth = new System.Speech.Synthesis.SpeechSynthesizer())
{
     synth.SelectVoiceByHints(System.Speech.Synthesis.VoiceGender.Female, System.Speech.Synthesis.VoiceAge.Teen, 0);
     synth.Speak("Hello world!");
}
And you’re done! That should have taken less than a minute if your references dialog didn’t take ages to load.
If you have Microsoft Anna (you have Windows Vista, Autoroute or Streets and Trips installed) then this will use that preferentially (that’s the SelectVoiceByHints line), otherwise it may use Microsoft Sam which sounds pretty bad but works well.
Permalink  3 Comments 

If you’re getting the Windows Forms designer instead of the Compact Framework 3.5 designer then you likely have a problem in your registry.  When you try to compile the build succeeds, but then you get the following warning:

C:\WINDOWS\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.Common.targets : warning : An internal error occurred in PlatformVerificationTask. System.Exception: Could not locate an IAsmmetaBindingService for platform PocketPC.  Check your registry settings for the platform.

C:\WINDOWS\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.Common.targets : warning :    at Microsoft.CompactFramework.Build.AsmmetaContext..ctor(String ndpversion, String platformFamily, String platformID, ICollection references)

C:\WINDOWS\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.Common.targets : warning :    at Microsoft.CompactFramework.Build.Tasks.PlatformVerificationTask.Execute()

This also manifests itself in the GetDeviceFrameworkPath entry not accepted by the schema in the “C:\WINDOWS\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.Common.targets” file.

Well, there’s an easy enough solution to these problems, the AsmmetaBinder entries are missing from the registry in each of the sub-types under:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0

So here’s the entries you need to copy into notepad, save as fix.reg or similar and then double click:-

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0\PocketPC\AsmmetaBinder]
"TypeName"="Microsoft.CompactFramework.Build.PocketPC.BindingService, Microsoft.CompactFramework.Build.Tasks, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0\PocketPC\AsmmetaBinder\4118C335-430C-497f-BE48-11C3316B135E]
"TypeName"="Microsoft.CompactFramework.Build.WM50PocketPC.BindingService, Microsoft.CompactFramework.Build.Tasks, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0\Smartphone\AsmmetaBinder]
"TypeName"="Microsoft.CompactFramework.Build.SmartPhone.BindingService, Microsoft.CompactFramework.Build.Tasks, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0\Smartphone\AsmmetaBinder\BD0CC567-F6FD-4ca3-99D2-063EFDFC0A39]
"TypeName"="Microsoft.CompactFramework.Build.WM50SmartPhone.BindingService, Microsoft.CompactFramework.Build.Tasks, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework\v3.5.0.0\WindowsCE\AsmmetaBinder]
"TypeName"="Microsoft.CompactFramework.Build.WindowsCE.BindingService, Microsoft.CompactFramework.Build.Tasks, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, Custom=null"

This one has been annoying me for a while now, as I do a bit of compact framework development.  I simply compared two exactly similar XP machines entire compact framework installs – files, registry settings, etc till I found the differences between a working and non-working system.

Permalink