NullifyNetwork

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

Archive - Historical Articles

You are viewing records from 10/11/2002 20:52:11 to 10/12/2011 12:40:30. I'll be adding support for selecting a date range in future.

Microsoft have just released version 5 of Silverlight, the tools for VS2010, the developer runtime and the full runtime!

Hopefully this isn't the end of Silverlight as the media are currently speculating, I'm hopeful that there'll be an announcement of a Silverlight 6 but either way Silverlight 5 has a ten year support cycle and is the underlying technology behind Windows Phone 7 and Windows 8 (albeit with different naming, etc.).

I've been very impressed with the capabilities of WPF/Silverlight and hope that it gets the resources it deserves.

Permalink  Digg This! 

This has been delaying my release of an upgraded version of this blog for months now, and has also meant that I have chosen to use Linq To SQL on several large projects when working (Linq To SQL fully supports this scenario despite being the 2nd class citizen in entity mapping at Microsoft), Paul Patterson explains it quite well here so I can be lazy and just link to his article:-

http://www.paulspatterson.com/technology/dot-net-development/entity-framework-4-and-sql-uniqueidentifier-bug/

I wasn't willing to employ his fix though, it was frustrating and would get overwritten whenever the schema was updated however I have just found that there is a hotfix for this now available from Microsoft so that you can correctly configure the EDMX in entity framework to support the rowguid correctly:-

http://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=37957

This isn't perfect as you still need to manually set the StoreGeneratedPattern option on the rowguid columns to Identity, but it is certainly better than the total failure that was the case before!  Hopefully they will consider better support for replication, guid's and server-generated values in future versions.

I guess this means I can finally switch to Entity Framework and get my new site version released... :)

Permalink  Digg This! 

Haven't had time to blog around work (figured I should at least post once a year though), but I'm pleased to see large companies suddenly enabling their IPv6 implementations today, it has been a rough ride.  I've seen a gradual climb in IPv6 end users (and web and mail servers) for some time now and it's very pleasing to see it gradually approaching critical mass.

With my network fully IPv6 enabled for some time now thanks to Hurricane Electric (and more recently Goscomb Technologies too) I am looking forward to the time when there's no more NAT being deployed, or at least not in the same fashion.

Edit: I'm glad to see Microsoft as one of the first companies to get their sites advertising their AAAA records today, curtesy of Akamai.  The rapid climb towards 100% amongst the participating companies is excellent to see, partcularly as the agreed start time is midnight UTC rather than british summer time when this post is being made (visible at http://www.worldipv6day.org/participants-dashboard/index.html and http://v6day.ripe.net/cgi-bin/index.cgi)

Permalink  Digg This! 

Just a quick note to say that I've been contracting for a while now and have avoided posting anything/not had much to post about but that I'm still around.

I did go to the Microsoft PDC's virtual event in Reading, UK so should really have been evangelizing about how great it is that there'll be private Azure clouds now but I've not really had a proper opportunity to investigate that yet, but perhaps the next post will be related to that.

Permalink  Digg This! 

So I have encountered a situation where I need to use two factor authentication and recently bought a couple of Yubico YubiKey's to investigate them as a potential solution where I can't use smart cards (mostly with other users who can't install the appropriate drivers or on locked down systems/internet cafe's).

They are fairly cheap (particularly in bulk) and provide similar functionality to an RSA Securid but without the need to type the password (they act as a keyboard and type onto the computer, but can't be modified by the computer in question).  They also have two 'slots' for different tokens, you can touch the button once for one and touch and hold for a second one time key.

Whilst there's plenty of code examples around for them (the company are extremely open), I haven't found a simple, logical C# library/class I could use to do authentication locally (they supply ones for remote auth on their servers) - there were always multiple files and loads of code in peoples projects.

My intention is to write a Windows SubAuthentication DLL (in C++ but I learn in C#) to use with these keys which also still checks your normal password - so when remote desktop'ing, using something that authenticates to Windows or using the Windows Radius server (NPS) to authenticate VPN's you can choose to either supply your normal password, or if you're on a system you don't trust you can use your username/low security pin code and Yubikey.

So here's my take on the decoding of a Yubikey's input - note that I'm not interested in making a software token though so the class doesn't generate values for the random value (if you wanted to do this you just need to ask RNGCryptoProvider for a few bytes of random though) and there's also no incrementing of the sessions, in-session use counters or timestamps included - however I did include the code to encrypt it appropriately if you do supply all the values.

To use this class you just need to call it like so - where privateKey is a byte[] array with the private key in and otpCode is a string like "fkfthfffktbnreffrghcldrffeclcgkt" output from a touch of the Yubikey:-

YubiKey otp = YubiKey.FromModHex(privateKey, otpCode);
if (otp.UidMatch(checkIdentity))
{
Console.WriteLine("Decoded the YubiKey's OTP code:-");
Console.WriteLine("\tSession:\t" + otp.SessionCounter.ToString());
Console.WriteLine("\tCount in session:\t" + otp.SessionUseCount.ToString());
Console.WriteLine("\tTime (high):\t" + otp.TimestampHighPart.ToString());
Console.WriteLine("\tTime (low):\t" + otp.TimestampLowPart.ToString());
Console.WriteLine("\tTime (low):\t" + otp.Uid.ToString());
}
else
{
Console.WriteLine("Failed to identify the keys correct OTP");
}

 

And you can decode the private key/identity as they're displayed in the YubiKey personalisation tool with the following handy (but not super efficient) hex to byte algorithms:-

 

public static byte[] StringHexDecode(string hexData)
{
List<byte> data = new List<byte>();
foreach (string b in hexData.Split(' '))
{
data.Add(Convert.ToByte(b, 16));
}
return data.ToArray();
}

public static string StringHexEncode(byte[] hexData)
{
return BitConverter.ToString(hexData).Replace('-'' ');
}

And finally here's the actual Yubikey class including the algorithm to decrypt the data that the USB dongles actually output in 'ModHex' which is an odd keyboard language independent format but seems to work quite well:-

 

/// <summary>
/// A YubiKey OTP is symmetric two-factor auth key, this class allows decoding and validating them
/// </summary> public class YubiKey {
public YubiKey()
{
}

private const int CRC_OK_RESIDUE = 0xf0b8; /// <summary>
/// Unique (secret) ID.
/// </summary> public byte[] Uid = new byte[6]; /// <summary>
/// Session counter (incremented by 1 at each startup).  High bit
/// indicates whether caps-lock triggered the token.
/// </summary>
public UInt16 SessionCounter; /// <summary>
/// Timestamp incremented by approx 8Hz (low part).
/// </summary>
public UInt16 TimestampLowPart; /// <summary>
/// Timestamp (high part).
/// </summary>
public byte TimestampHighPart;
/// <summary>
/// Number of times used within session + activation flags.
/// </summary> public byte SessionUseCount; /// <summary>
/// Pseudo-random value.
/// </summary>
public UInt16 RandomValue; /// <summary>
/// CRC16 value of all fields.
/// </summary>
public UInt16 CRC;

/// <summary>
/// Does the included UID match the one we expected?
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public bool UidMatch(byte[] uid)
{
for (int i = 0; i < uid.Length; i++)
{
if (Uid[i] != uid[i])
{
return false;
}
}
return true;
}

#region Post Decryption Conversion

private static int calculateCrc(byte[] b)
{
int crc = 0xffff;

for (int i = 0; i < b.Length; i += 1)
{
crc ^= b[i] & 0xFF;
for (int j = 0; j < 8; j++)
{
int n = crc & 1;
crc >>= 1;
if (n != 0)
{
crc ^= 0x8408;
}
}
}
return crc;
}

internal static YubiKey OtpFromRawByteArray(byte[] input)
{
if (input.Length < 16)
{
throw new YubiKeyException("Invalid OTP data - the amount supplied was
insufficient for a six byte identity."
);
}

if (calculateCrc(input) != CRC_OK_RESIDUE)
{
throw new YubiKeyException("CRC was invalid on that OTP");
}

YubiKey u = new YubiKey();
u.Uid = input.Take(6).ToArray();
u.SessionCounter = BitConverter.ToUInt16(input, 6);
u.TimestampLowPart = BitConverter.ToUInt16(input, 8);
u.TimestampHighPart = input[10];
u.SessionUseCount = input[11];
u.RandomValue = BitConverter.ToUInt16(input, 12);
u.CRC = BitConverter.ToUInt16(input, 14);
return u;
}

internal static byte[] RawByteArrayFromOtp(YubiKey input)
{
List<byte> data = new List<byte>();
data.AddRange(input.Uid);
data.AddRange(BitConverter.GetBytes(input.SessionCounter));
data.AddRange(BitConverter.GetBytes(input.TimestampLowPart));
data.Add(input.TimestampHighPart);
data.Add(input.SessionUseCount);
data.AddRange(BitConverter.GetBytes(input.RandomValue));
data.AddRange(BitConverter.GetBytes(input.CRC));
return data.ToArray();
}
#endregion

#region Cryptographic wrapper
internal byte[] AESEncrypt(byte[] data, byte[] key, byte[] iv)
{
Aes enc = Aes.Create();
enc.Key = key;
enc.IV = iv;
enc.Padding = PaddingMode.None;
using (ICryptoTransform transform = enc.CreateEncryptor())
{
byte[] output = transform.TransformFinalBlock(data, 0, data.Length);
return output;
}
}

internal static byte[] AESDecrypt(byte[] data, byte[] key, byte[] iv)
{
Aes aesImplementation = Aes.Create();
aesImplementation.Key = key;
aesImplementation.IV = iv;
aesImplementation.Padding = PaddingMode.None;

using (ICryptoTransform transform = aesImplementation.CreateDecryptor())
{
byte[] output = transform.TransformFinalBlock(data, 0, data.Length);

return output;
}
}
#endregion

#region ModHex Support

private const string alphabet = "cbdefghijklnrtuv";

internal static string ModHexEncode(byte[] data)
{
StringBuilder result = new StringBuilder();

for (int i = 0; i < data.Length; i++)
{
result.Append(alphabet[(data[i] >> 4) & 0xf]);
result.Append(alphabet[data[i] & 0xf]);
}

return result.ToString();
}

internal static byte[] ModHexDecode(String s)
{
List<byte> baos = new List<byte>();
int len = s.Length;

bool toggle = false;
int keep = 0;

for (int i = 0; i < len; i++)
{
char ch = s[i];
int n = alphabet.IndexOf(ch.ToString().ToLower());
if (n == -1)
{
throw new YubiKeyException(s + " is not properly encoded");
}

toggle = !toggle;

if (toggle)
{
keep = n;
}
else
{
baos.Add((byte)((keep << 4) | n));
}
}
return baos.ToArray();
}

#endregion

#region Factory helpers
/// <summary>
/// Create a Yubikey object from the mod hex and the private key
/// </summary>
/// <param name="privateKey">The private key to decrypt with</param>
/// <param name="modHex">The modhex content</param>
/// <returns>A yubikey object</returns>
public static YubiKey FromModHex(byte[] privateKey, string modHex)
{
//no IV in use
byte[] inputOTP = ModHexDecode(modHex);
byte[] decrypted = AESDecrypt(inputOTP, privateKey, new byte[16]);
return OtpFromRawByteArray(decrypted);
}

/// <summary>
/// Convert this yubikey instance into a mod-hex OTP string
/// </summary>
/// <param name="privateKey">The private key to encrypt with</param>
/// <returns>The modhext content</returns>
public string ToModHex(byte[] privateKey)
{
byte[] clearotp = RawByteArrayFromOtp(this);
byte[] encrypted = AESEncrypt(clearotp, privateKey, new byte[16]);
return ModHexEncode(encrypted);
}
#endregion

public class YubiKeyException : Exception
{
public YubiKeyException(string message) : base(message)
{

}
} }

Once decrypted it is a simple matter to verify it is a key with a particular code (the simple act of decryption should do that but there's a UID included inside too) and that the Session counter and SessionUseCount have been incremented appropriately to prevent re-use of existing keys.

Note that if you are trying to decode a key which has a prefix added you are interested in the last 32 characters of the output - the first few characters by default on a key are a static identifier so that it's possible to identify which encryption key to use to decrypt the token; I've turned this off and favour the user having to enter their own username/password prior to using the key.

Permalink  Digg This! 

Just a quick note that posting will resume at some point as I'm going to start some more hobby projects, due to some issues with Zorg Solutions though I took a contract and I try to avoid posting about anything work related on my blog.

I've not been doing much proper software development out of work, just helping the odd person with things and making sure I am up-to-speed with things like Windows Azure, Visual Studio 2010 and the new features of C++0x (I ended up doing quite a bit of C++ at my previous job at one point, and found I can do stuff in it with just a bit more time spent working out what library to use than C#, so it seems sensible to keep up to speed with it!).

Permalink  Digg This! 

Here's one from my distant past but which may be useful to me at some point today. I can actually see plenty of quicker ways to achieve this but it's also well tested for generating/checking credit card check digits:-

/// <summary>
    /// Creates/appends Luhn/Mod10 check digits to a specified numeric string
    /// Goes belly up with a text string.
    /// </summary>
    public class LuhnCheck
    {
        /// <summary>
        /// Initialise the LuhnCheck class with a number
        /// </summary>
        /// <param name="numberToAddDigitTo">The number to do work on</param>
        public LuhnCheck(string numberToAddDigitTo)
        {
            _numberToAddDigitTo = numberToAddDigitTo;
            setupok = true;
        }
        /// <summary>
        /// Initialise the LuhnCheck class without a number to start with
        /// </summary>
        public LuhnCheck()
        {
            _numberToAddDigitTo = "";
        }

        private string _numberToAddDigitTo;
        /// <summary>
        /// The number to work on
        /// </summary>
        public string NumberToAddDigitTo
        {
            get
            {
                return _numberToAddDigitTo;
            }
            set
            {
                _numberToAddDigitTo = value;
                setupok = true;
            }
        }
        private bool setupok = false;

        /// <summary>
        /// Add the check digit to the string and return the whole string
        /// </summary>
        /// <returns>The whole string with added check digit</returns>
        public string AddCheckDigit()
        {
            if (setupok)
            {
                NumberToAddDigitTo += Convert.ToString(CreateCheckDigit());
                return NumberToAddDigitTo;
            }
            else
            {
                throw new ArgumentException("Please specify the number to add the digit to.");
            }
        }
        /// <summary>
        /// Creates a check digit without using it
        /// </summary>
        /// <returns>The check digit</returns>
        public int CreateCheckDigit()
        {
            if (setupok)
            {
                return CreateCheckDigit(NumberToAddDigitTo);
            }
            else
            {
                throw new ArgumentException("Please specify the number to calculate the digit for.");
            }
        }
        /// <summary>
        /// Creates the check digit for a specified numeric string - it does not add it, only calculates it
        /// </summary>
        /// <param name="numberToAddDigitTo">The number to work the check digit out for</param>
        /// <returns>The check digit</returns>
        public int CreateCheckDigit(string numberToAddDigitTo)
        {
            string validChars = "0123456789";
            foreach (char c in numberToAddDigitTo)
            {
                if (!(validChars.IndexOf(c) >= 0))
                    throw new FormatException("The value passed in was not a valid number");
            }

            string tocheckdigit = numberToAddDigitTo + "0"; //We add zero to use it to calculate
            //the number from the correct point - since we'll be alternating between multiplying it by one or two
            int strlen = tocheckdigit.Length;
            int ncheck = 0;
            int nweight = 1; //this will alternate between one and two
            int intermediary = 0;
            string tosplit = "";

            for (int i = 1; i <= strlen; i++) //loop through the card number string
            {
                intermediary = nweight * Convert.ToInt32(Convert.ToString(tocheckdigit[strlen - i])); //make intermediary
//be the multiple of the variance and the card number
                if (intermediary > 9) //if it's bigger than ten...
                {
                    tosplit = Convert.ToString(intermediary);
                    //Split the two digits
                    int firstdigit = Convert.ToInt32(Convert.ToString(tosplit[0]));
                    int seconddigit = Convert.ToInt32(Convert.ToString(tosplit[1]));
                    //Then add them together
                    intermediary = firstdigit + seconddigit;
                }
                //Add the intermediary result to the rolling total
                ncheck += intermediary;
                //Change the variance as per the Luhn formula
                if (nweight == 2)
                {
                    nweight = 1;
                }
                else
                {
                    nweight = 2;
                }
            }
            //ncheck now has the total
            int remainder = 0;
            //Work out the remainder
            Math.DivRem(ncheck, 10, out remainder);
            //return ten minus the remainder (if it's zero then it divides perfectly by ten so is valid,
            //but since we're CREATING a check digit we want to know how far off the result is and how much we
	    //need to correct it using the check digit)
            if (10 - remainder == 10)
            {
                return 0;
            }
            else
            {
                return 10 - remainder;
            }

        }
        /// <summary>
        /// Whether the number stored in the object is a valid MOD 10/Luhn check digited number
        /// </summary>
        public bool IsValid
        {
            get
            {
                if (CreateCheckDigit() == 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }
Permalink  Digg This! 

Attempting to pass paths with a space or any other command line argument that requires being enclosed in quotes was causing me some mild frustration, it looks like the .NET implementation that fills args is incorrectly mangling the data that is passed into it.  A major bug that shouldn't have gotten through QA exists somewhere...

If you pass a path into a program that is foreach-ing over the args collection through the debug properties in visual studio (and presumably through anywhere else!) you go from:-

program.exe /path "c:\my documents\" /dostuff 1

Turn into two rather than four arguments:-

args[0] is /path
args[1] is c:\my documents" /dostuff 1

Which is downright strange, as you'd expect to get:-

args[0] is /path
args[1] is c:\my documents\
args[2] is /dostuff
args[3] is 1

What on earth are they doing to cause such a mess to command line arguments... Anyway, whilst I'm not sure when this started but thankfully the Environment.CommandLine is left intact and undamaged, so you can do something like this to fix the problem:-

private static string[] FixedExtractCmdLineElements()
        {
            List<string> elements = new List<string>();

            string[] firstParts = Environment.CommandLine.Split(' ');

            //reparse it
            StringBuilder temporaryPart = new StringBuilder("");
            bool inside = false;
            foreach (string part in firstParts)
            {
                //are we inside a quoted part, or did we just find a quote?
                if (!inside && !part.Contains("\""))
                {
                    elements.Add(part);
                }
                else
                {
                    if (inside)
                    {
                        //we are still inside a quote...
                        temporaryPart.Append(" " + part);

                        if (part.Contains("\""))
                        {
                            //then we are also at the end!
                            elements.Add(temporaryPart.Replace("\"", "").ToString()); //add the part minus its quotes to the array
                            //all done!
                            inside = false;
                        }
                    }
                    else
                    {
                        //else we just found a quote!
                        inside = true;
                        temporaryPart = new StringBuilder(part);
                    }
                }
            }

            return elements.ToArray();
        }
Permalink  2 Comments  Digg This! 

This is mostly so I don't have to ever type this in again, but it's most useful when you're also using an RSACryptoProvider to encrypt and send the key to the other end where you're decrypting whatever you are sending, but I thought these might be of use to someone...  There's really nothing complicated to these at all.

Note that the iv (initialisation vector) should be some random data that you pass with the encrypted data, but it does not need to be protected.  It's used to prevent two duplicate bits of input data outputting the exact same (and therefore being obvious), and to prevent someone making you encrypt something special in a way that you can have your private key exposed.

public static string AESEncrypt(string input, byte[] key, byte[] iv)
{
    Aes enc = Aes.Create();
    enc.Key = key;
    enc.IV = iv;
    using (ICryptoTransform transform = enc.CreateEncryptor())
    {
        byte[] data = Encoding.Unicode.GetBytes(input);
        byte[] output = transform.TransformFinalBlock(data, 0, data.Length);

        return Convert.ToBase64String(output);
    }
}

private static string AESDecrypt(string input, byte[] key, byte[] iv)
{
    Aes aesImplementation = Aes.Create();
    aesImplementation.Key = key;
    aesImplementation.IV = iv;

    using (ICryptoTransform transform = aesImplementation.CreateDecryptor())
    {
        byte[] data = Convert.FromBase64String(input);
        byte[] output = transform.TransformFinalBlock(data, 0, data.Length);

        return Encoding.Unicode.GetString(output);
    }
}
Permalink  Digg This! 

I needed to send some data over another medium to WCF and wanted to retain my current objects and classes, but also to retain the serialisation format for ease of direct input at the other end of this communications 'tube'.  I was actually sending these messages over XMPP after encrypting them, which is what the next post will be about...  It's worth noting that serialization using WCF is marginally faster than the more capable XmlSerializer but at the same time this has its own quirks and tends to produce more verbose XML.

So here's methods to serialise and deserialise DataContract marked up objects to XML strings:-

        public static string ContractObjectToXml<T>(T obj)
        {
            DataContractSerializer dataContractSerializer = new DataContractSerializer(obj.GetType());

            String text;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                dataContractSerializer.WriteObject(memoryStream, obj);
                text = Encoding.UTF8.GetString(memoryStream.ToArray());
            }
            return text;
        }

        public static T XmlToContractObject<T>(string data)
        {
            DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(T));

            byte[] binary = Encoding.UTF8.GetBytes(data);
            using (MemoryStream memoryStream = new MemoryStream(binary))
            {
                object o = dataContractSerializer.ReadObject(memoryStream);
                return (T)o;
            }
        }
 
(Google thinks I should spell Serialise incorrectly as Serialize so I'm very conflicted about which I should use in this article as anyone searching would probably look for the American spelling...!)
Permalink  Digg This! 

I was experiencing problems with SQL compact edition (complete .NET framework freezes) and found that not only is it not thread safe (fairly obvious) but it's doesn't like to even have multiple instances of itself on the same thread - it deadlocks a lot if it does.  I also found that if it runs out of instances then it unloads from memory and has to be reloaded again afterwards, so avoiding that can result in a many tens of thousands of percent performance improvement.

So here's a small class I wrote to track the connection per thread based on someone elses on the Internet (they were just creating one per thread, in this one it is creating one per thread and maintaining the lifespan of that connection to close it when the thread it was for has been closed).  I'll probably add per-connection-string pooling but figured that the important thing is the concept of the solution to the COM deadlocks and total freezing of the framework in this.

/// <summary>
/// Manages open connections on a per-thread basis
/// </summary>
public abstract class SqlCeConnectionPool
{
    private static Dictionary<int, IDbConnection> threadConnectionMap = new Dictionary<int, IDbConnection>();

    private static Dictionary<int, Thread> threadMap = new Dictionary<int, Thread>();

    /// <summary>
    /// The connection map
    /// </summary>
    public static Dictionary<int, IDbConnection> ThreadConnectionMap
    {
        get { return SqlCeConnectionPool.threadConnectionMap; }
    }

    /// <summary>
    /// Gets the connection string.
    /// </summary>
    /// <value>The connection string.</value>
    public static string ConnectionString
    {
        get;
        set;
    }

    /// <summary>
    /// Gets a connection for this thread, maintains one open one of each.
    /// </summary>
    /// <remarks>Don't do this with anything but SQL compact edition or you'll run out of connections - compact edition is not
    /// connection pooling friendly and unloads itself too often otherwise so that is why this class exists</remarks> 
        /// <returns>An open connection</returns>
    public static IDbConnection Connection
    {
        get
        {
            lock (threadConnectionMap)
            {
                //do some quick maintenance on existing connections (closing those that have no thread)
                List<int> removeItems = new List<int>();
                foreach (var kvp in threadConnectionMap)
                {
                    if (threadMap.ContainsKey(kvp.Key))
                    {
                        if (!threadMap[kvp.Key].IsAlive)
                        {
                            //close the connection
                            kvp.Value.Close();
                            removeItems.Add(kvp.Key);
                        }
                    }
                    else
                    {
                        kvp.Value.Close();
                        removeItems.Add(kvp.Key);
                    }
                }
                foreach (int i in removeItems)
                {
                    threadMap.Remove(i);
                    threadConnectionMap.Remove(i);
                }

                //now issue the appropriate connection for our current thread
                int threadId = Thread.CurrentThread.ManagedThreadId;

                IDbConnection connection = null;
                if (threadConnectionMap.ContainsKey(threadId))
                {
                    connection = threadConnectionMap[threadId];
                }
                else
                {
                    connection = new SqlCeConnection(ConnectionString);
                    connection.Open();
                    threadConnectionMap.Add(threadId, connection);
                    threadMap.Add(threadId, Thread.CurrentThread);
                }

                return connection;
            }
        }
    }
}
Permalink  Digg This! 

My integration/automation system 'Zorganiser' is almost ready for a closed beta test (albeit with a limited set of features but enough to be useful!).  It'll let you control automated tasks on any number of computers, so you can do things like tell 50 webservers to install a particular application, run a particular Windows Workflow or to just shift data around between your installed agents - for example to take data from one business application and deliver it to another in a slightly tweaked format regardless of where the two applications are.  You can also configure one event to trigger another - so the workflow could poll something and then trigger an event across all your workstations or servers.

I already have a few people who are interested in testing it, but if you're also interested please e-mail me at (redacted)!

Permalink  Digg This! 

Due to a variety of my circumstances changing and the way the exchange rate was fluctuating I changed from having all-US servers to using rented dedicated servers from 1and1 internet - billed in UK pounds. Right now it's particularly appropriate with both the Euro and the Pound hugely devalued against the US dollar.

I didn't expect much - the plan I picked was half literally half the price I was paying before but I got a higher specification server, unlimited bandwidth with no concern about overages anymore or anything of the sort and a significant improvement on the latency to my Texas based servers (which is understandable given the speed of light, but the network is also less congested and I can regularly get 85Mbps and less than 20ms to the UK when testing it).

So I've been with them almost a year now and the availability has far exceeded that of my previous datacenter - I've had about 2 minutes downtime the entire time, so I thought I would mention it as it's relevant to a project I'm working on at the moment...

Fortunately that does mean I've not managed to see if their support is any good, but the service certainly is.

Be warned that their network setup is not really conducive to VMWare or virtualisation in general - they use 1 ip sized subnets and gateways that you can always access even though they aren't in your subnet through some TCP/IP stack tweaks (I think it's "All subnets are local" combined with the switches having some custom configuration, but I've not yet figured it out and it's the first time I've seen something similar done).  Their network does have a nice Cisco switch based firewall for every custom because of the strange setup though, and it lets you have public IP's from different subnets so it's got its advantages (no renumbering!).

Permalink  Digg This! 

Just a very quick reminder to myself that the file dialogs filter string (OpenFileDialog.Filter and SaveFileDialog.Filter) format it is "Description|*.ext;AnotherDescription|*.txt" etc.

I always seem to get this around the wrong way for some reason.

Permalink  Digg This! 

I'm trying to use a Linq datacontext from a partial trust environment (well, after Assert'ing my way back up) and I'm getting the spectacularly verbose error:-

System.Runtime.CompilerServices.StrongBox`1..ctor(System.__Canon)

Which looks like it's lacking an actual human-readable error but at least it's distinct!

In my case rather than asserting the individually needed parts I had to assert full-trust from my intermediary class (the one that is signed by a strongname to allow it to always be full-trust in my custom AppDomain - see my article on creating a restricted AppDomain - see one of the comments in the code example) between the untrusted plugins (things inside the appdomain with the restrictions applied can call into this and anything the intermediary class does is unrestricted but even things it calls get the inherited permissions from the caller) and the Linq data context user:-

PermissionSet set = new PermissionSet(PermissionState.Unrestricted); //fulltrust especially for Linq :(
//removed my carefully crafted assert set for Linq/SQLCE in exchange for the above being unrestricted:-
//set.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
//set.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted));
set.Assert();

//Make the call to whatever uses the linq datacontext

Which was a little annoying, but worth knowing.  (The assertion will revert when the stack pops from the method this is in, however do be aware not to allow anything under this method to allow the plugin to execute a delegate or something).
 
I did find a possible workaround of moving a member variable I was accessing into a local variable in the Linq query, but this didn't work in my case where asserting this did.
 
Permalink  Digg This! 

I'm sure there's an easier way to do this, but I've been maintaining my own class to do the job for a while now and keep needing to find it in my code library every time I use it - I figure it might be useful to others:- 

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace CSVParser
{
    /// <summary>
    /// Simon's stock CSV parser class
    /// </summary>
    public class CSVParser
    {
        /// <summary>
        /// Takes the CSV files contents and creates a data table from it
        /// </summary>
        /// <param name="csvFileContents">The entire contents of a CSV file split by line (rather than the filename)</param>
        /// <param name="validateLayout">Validate the file to check conformance of the layout with expected standards</param>
        /// <param name="topRowIsHeader">The top row is the header row, take it as the column names rather than data</param>
        /// <returns>A datatable</returns>
        public static DataTable DataTableFromCSV(string[] csvFileContents, bool validateLayout, bool topRowIsHeader)
        {
            DataTable outputDataTable = new DataTable();
            List<string[]> csvFileRows = new List<string[]>();
            List<string> columns = new List<string>();

            #region Pre-parse the file
            int columnCount = 0;

            bool gotHeaders = false;
            int rowNumber = 0;

            foreach (string line in csvFileContents)
            {
                string[] parts = ExtractCSVElements(line);

                //initial set of header names but only if the top row is header option is set
                if (!gotHeaders && topRowIsHeader)
                {
                    columns.AddRange(parts);
                    columnCount = parts.Length;
                    gotHeaders = true;
                }
                else
                {
                    if (parts.Length > 0)
                    {
                        csvFileRows.Add(parts);
                    }
                }

                if (parts.Length > columnCount)
                {
                    //if set to validate the layout and that the first row contains the headers then we know any extra columns are wrong
                    if (validateLayout && gotHeaders)
                    {
                        throw new Exception("Row has extra data columns: " + rowNumber.ToString());
                    }

                    //new column detected mid-data-set!
                    for (int i = columnCount; i < parts.Length; i++)
                    {
                        columns.Add("Column " + i.ToString());
                    }

                    columnCount = parts.Length;
                }

                //we always ignore zero length rows as the last line can be empty
                if (parts.Length < columnCount && parts.Length != 0)
                {
                    if (validateLayout)
                    {
                        throw new Exception("Row has missing data columns: " + rowNumber.ToString());
                    }
                }


                rowNumber++;
            }

            #endregion

            #region Build the data tables layout and data

            //columns
            foreach (string column in columns)
            {
                outputDataTable.Columns.Add(column);
            }

            //rows
            foreach (string[] row in csvFileRows)
            {
                outputDataTable.Rows.Add(row);
            }
            #endregion

            return outputDataTable;
        }

        /// <summary>
        /// Extract the elements of a line from a CSV file with support for quotes
        /// </summary>
        /// <param name="line">The data to parse</param>
        private static string[] ExtractCSVElements(string line)
        {
            List<string> elements = new List<string>();

            //do the initial split, based on commas
            string[] firstParts = line.Split(',');

            //reparse it
            StringBuilder temporaryPart = new StringBuilder("");
            bool inside = false;
            foreach (string part in firstParts)
            {
                //are we inside a quoted part, or did we just find a quote?
                if (!inside //we're not inside
                && (!part.Contains("\"") //and we don't contain a quote
                 || ( //or we're handling a single quote enclosed element
                        part.StartsWith("\"") //we start with a quote
                        && part.EndsWith("\"") //and we end with a quote)
                    )
                )
                {
                    elements.Add(part);
                }
                else
                {
                    if (inside)
                    {
                        //we are still inside a quote...
                        temporaryPart.Append("," + part);

                        if (part.Contains("\""))
                        {
                            //then we are also at the end!
                            elements.Add(temporaryPart.Replace("\"", "").ToString()); //add the part minus its quotes to the array
                            //all done!
                            inside = false;
                        }
                    }
                    else
                    {
                        //else we just found a quote!
                        inside = true;
                        temporaryPart = new StringBuilder(part);
                    }
                }
            }

            return elements.ToArray();
        }
    }
}

 

Permalink  Digg This! 

Edit: Due to some investment problems I can no longer employ anyone, sorry :(

My new company - Zorg Solutions Ltd is now looking for at least one software developer to fill out a team, preferably you'll be an enthusiastic multi-skilled person but we're flexible and will distribute work based on what you can do.

Do you have a background as a C# software developer that has done either Winforms or ASP.NET in the past?  You will need to have at least a couple of years of experience developing with the .NET framework but I will be willing to take hobby or open source work as experience if it's provable that you did it.  This could be a great opportunity to jump into the software development world for someone - there's no requirement for a degree or other certification.  Any experience of Windows Azure, Silverlight or WPF would be an advantage.

Or do you have any experience (hobby or professionally) in electronics, firmware, C/C++?  We have contracts for some hardware development and the opportunity to work with and program the hardware we'll build - probably Atmel microprocessors or PIC microcontrollers, but even some Linux on ARM9 based chips.

If you answered yes to either of these questions and would be willing to commute to Basingstoke then I am interested in hearing from you!  Any experience of the following would make you stand out but is not essential:-

  • SQL experience (preferably SQL server's T-SQL dialect)
  • Windows Workflow Foundation
  • Biztalk
  • Silverlight
  • Mono

Salary will be negotiable (but as an idea we would be willing to pay either side of £35k) and based on experience.  Working hours and benefits will be fully negotiable - we can be very flexible and understand if you have personal commitments.  Training and an MSDN subscription will also be provided!

If you apply (just send me your CV) or contact me now to discuss in more detail then you will be ahead of the conventional advertisements for these roles - we'll be putting them out conditional on responses to this posting and a few other enquiries - so you have nothing to lose!  No recruiters yet please!

Edit: Due to some investment problems I can no longer employ anyone, sorry :(

Permalink  Digg This! 

I finally set up my new companies website (thanks to the snow here totally blocking the ability to get to the office!) - it's lacking content at the moment but the design is in place and I think it looks excellent and embodies the company quite well.

http://www.zorg.co.uk/ 

What do you think?  Anything I should change?

I went through the images on fotolia for quite a while before settling on that one for the front page.  We're using SIP phones for the telephony and I am hoping to have my automation system at the alpha stage within a few months so I'll be looking for people to test it before we go public with a beta (Google style!).  I already have a prototype Workflow designer but at the moment there's not enough to use in production yet.

I am hoping that this product will help in a lot of situations, everything from automated deployment of complex systems, testing your software without needing to sit there and do it manually to automating business processes (or virtual machine operations like creation, start-up and shut down!).

All as a simple and easy on-line service you can optionally subscribe to (there will be a free offering to get to try it!).

Permalink  Digg This! 

Today is my last day at my current employer, a company called EasyTrace which has gone from a few people in a cow-shed just outside Basingstoke selling a DOS product to a market leader selling modern software I can feel proud to have written the vast majority of.  Over a year ago now they were also bought by RM PLC and the RM people I have worked with have been enthusiastic and fun to interact with.

I have been there for over five and a half years and would like to wish all the present and ex-employees, suppliers and RM people who have helped out over the years the very best for the future.

Everyone has been amazing and put in a lot of long hours and effort to make a happy client base of schools, colleges, hospitals and big companies.  It has been a wild ride, and it has been a pleasure working with everyone.

I will be providing some help to the company after the relocation to the new offices in Abingdon this Christmas but I am mostly taking this opportunity to set up a new business and have some more relaxing fun for a while exploring an idea I have.  It also hopefully means I'll have the opportunity to get to some conferences and write more technical blog posts - I'm fully aware I haven't made any decent ones since May as I have been spending a lot of time on work projects even out of hours recently.

So, here's to the future!

Permalink  Digg This! 

This is an excellent usability study comparing the major webmail providers sign-up forms out there, it almost certainly has an applicability to all software development to a degree:-

http://www.cxpartners.co.uk/thoughts/web_forms_design_guidelines_an_eyetracking_study.htm 

So take a look.

It covers things like making name fields appear as one entry so they require less thought to enter, and making mobile phone fields not take an age to decide what bit goes in which box.  There were also some good basic layout hints and talk of when to use groupings and when not to!

Permalink  Digg This! 

With development getting more and more complex and thousands of lines of code being required to do things that used to take a couple of hundred, it's nice to see a simpler approach to things.  Sometimes the complexity has great benefit (like reuse and flexibility), but when learning that isn't necessarily first on your mind...

I just noticed a blog post on the Microsoft Small Basic blog and have since had a quick try... It looks like an awesome educational tool - easy to use and I certainly remember playing with logo and basic when I was younger - this provides both of those combined (along with decent graphics and no overhead).

So the blog is here:-

http://blogs.msdn.com/smallbasic/

And the program itself (which is basically a tiny IDE) is at http://smallbasic.com/ - hopefully it will let a new generation of children find programming as fun as it was for me when I was young.

Edit: Yes this is no match for C# which is a wonderful language and has a vast framework beside it, but this IS able to make a logo turtle move around in three lines on screen...

Permalink  3 Comments  Digg This! 

For those that don't know me, I work in an education sector linked business - which means that when the schools/colleges and universities go back we always get a vast increase in the amount of calls and number of issues that people raise.

So I've been trying to make up for the development time that was lost doing support by working later here and there.  Thankfully September is now over so I can return to having a personal life and possibly doing more research/blogging!

I have several projects I am investigating right now and hopefully one of them is getting closer to being something I can talk about and that will be able to have a bit of an ecosystem sit on top of it.  For all those people that were disappointed that Oslo wasn't a platform, this might make up for it, depending on what you were expecting :)

Permalink  Digg This! 

I am looking to merge both a P2P WCF service (for LAN support and accessible hops) and a publish-subscribe server to coordinate everything, this is just a post to keep track of the URL's related to doing this as I find them :)

WCF can't use its PeerChannel to connect to both hosts on the internet and local network at the same time, so there'll need to be two PeerChannel's and a Publish-Subscriber service bus connection to work out where everything else is.

Publish-Subscriber: http://msdn.microsoft.com/en-us/library/ms752254.aspx
PeerResolvers: http://msdn.microsoft.com/en-us/library/aa702771.aspx
Writing a custom peer resolver (new one needs to handle non-local hosts by requesting from the publish subscribe service): http://msdn.microsoft.com/en-us/library/ms751466.aspx
Sample WCF P2P chat app: http://msdn.microsoft.com/en-us/library/ms751502.aspx

(This post will be updated with more links as I find them)

Update: What I didn't realise when I started looking into this is that the PeerResolver service is not a magic 'find everything on your network' concept, but rather just a WCF service you register and unregister at, so it's basically already the public-subscriber list I was looking at.  That leaves the question of if the PeerResolver service does that, how on earth is it supposed to find things on the local network?  Well that would be the PNRP service, which happens to not be compatible with loads of OS' and isn't installed by default.  Great one there guys!

So instead I'll just make my own UDP transport for the peer resolver, and if anything I'll get to learn how to make a new transport :).

Permalink  Digg This! 

So this is a little different to my normal posts that are all 100% technical, but via the Omnima site (company who sell some embedded components) there's a link to a review of one of the products they sell which is used for monitoring stuff, which links to a specialist site on aquariums.

Well, it has some amazing photos of reefs on it which I thought were worth linking to.

Permalink  Digg This! 

Downloading Windows 7 slowly from MSDN since around 6.30PM here and it has been getting gradually slower.

So please wait before downloading it ;)

Edit: And Server 2008 R2 is out too as of the 14th August 2009!

Permalink  Digg This! 

This is a moan mostly, but has anyone else noticed that the developer story for silverlight development is totally utterly rubbish?

I had hoped that Microsoft would fix it for Silverlight 3 so that developers had a first class UI editor rather than having to go use a trial copy of Expression Blend with the worst user interface I have ever come across (I have MSDN but there's only ever an old version of it available and the current version appears to always be the CTP/Beta), or some third party app just to put down some buttons (with the 'run an app locally' addition and loads of other great features why has no time been spent on a decent design-time experience?).

I don't want to write XAML, I wouldn't mind adding controls using some C# code but I really hate XML, it's neither intuitive nor functional (can't easily call/make functions to draw X number of controls but must declare them individually).  I might be able to do Silverlight using 100% C# but it still comes down to the point of why is there no decent designer!???

I know this is supposed to be fixed in Visual Studio 2010 but it's really annoying.

Update: Now it's released I've had a chance to play with Expression Blend 3 and it's quite a bit better than the previous incarnations, and controls are now visible in a seperate section, so I think they are let off there as they've fixed the UI problems quite a bit; but the developer story for Silverlight is still terrible.

Permalink  Digg This! 

My ASP.NET server/composite controls were not getting events from child controls, but only on the real pages, they worked fine on test ones.  It turned out that I had a master page enabled but didn't have an ID set for it, so it was auto generating it.

Just add this to your master pages code file:-

protected override void OnInit(EventArgs e) 
{ 
	base.OnInit(e); 
	this.ID = "SomeName"; 
}

And child controls will start working as expected.  I don't often do web development so this stumped me for quite a while...

Permalink  Digg This! 

This is a story of how to handle a customer who could have ended up sending something back and getting expensive.

So: I've been interested in getting an Aeron chair or Freedom Task Chair or similar for a while now and only been put off by the price.  Eventually Amazon being the amazing pool of data it is made a suggestion of a similar one called a Cobham mesh back chair, which looked like it would fit the bill but only cost £270.

I obviously googled for more information, and found that whilst there weren't any reviews there was a place called chairoffice.co.uk selling the exact same Cobham mesh back chair for almost a hundred pounds less than the other suppliers out there (around £150 with VAT and delivery, or £119 ex VAT).  A quick background check to see if they were legitimate showed they were based really close to the actual manufacturer - Teknikoffice and happened to have their site hosted on the same server which explains the price difference a bit!!  I ordered after a particularly bad day at work in a sort of shopping to make myself feel good mood.

I was really impressed when it arrived the next day after ordering (very speedy dispatch), and I got right down to assembly after work - which was remarkably simple compared to a few other chairs.  Right after assembling it though I sat down in it and tried to adjust the headrest - and noticed that the back part had a hairline fracture, which when the headrest was extended was completely sheered through in shipping!!!!  I was gutted as the chair was really comfortable and I had that sick feeling of looking forward to a painful argument with a customer service rep.

I immediately sent a photo to the manufacturer and chairoffice customer service asking if they could supply me with the part that had broken and Mark in teknikoffice's customer service department replied almost immediately (appreciate this is around 10PM at night) and shipped me the entire back of one of the chairs as a replacement without any questions (which I got and fitted yesterday evening).

This is such good customer service it has made me want to talk about them.  Sometimes a faulty product can actually make you happier with the company if they deal with it correctly - and this is one of those cases; I don't think I have EVER encountered that good a customer service from any other company, so I would fully recommend if you're interested in a mesh back chair like an Aeron but a lot cheaper you get one from chairoffice.co.uk.

Permalink  Digg This! 

I even put OpenID on my own site in the latest iteration of its software as it is slowly growing in momentum, Scott Hanselman has details of how to implement OpenID support here.

This works particularly well with sites like Yahoo, where you can just put in the URL and be logged in near-instantly.

Permalink  Digg This! 

Very quick snippet that I just made use of to allow users to remotely change passwords over the web.  Just leave the domainname and username black to change the current users password.  This works great from ASP.NET and requires no special permissions, unlike some solutions that use LDAP or impersonation.

[DllImport("netapi32.dll", CharSet = CharSet.Unicode, 
   CallingConvention = CallingConvention.StdCall, SetLastError = true)]

static extern uint NetUserChangePassword(string domainname, string username, string oldpassword, string newpassword);
Permalink  Digg This! 

Just a quick example:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Policy;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Reflection;
using System.IO;

namespace RestrictedAppDomainTest
{
    class Program
    {
        /// <summary>
        /// Example program demonstrating AppDomains with restrictive security
        /// Note that it requires the existance of C:\windows\win.ini to actually demonstrate it, but you 
        /// can modify this to any other file.
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            //we want to define a level of access for this appdomain to have
            PolicyLevel levelOfAccess = PolicyLevel.CreateAppDomainLevel();

            //now we grant the permissions to do something specific:-
            PermissionSet executeOnly = new PermissionSet(PermissionState.None);

            //bare minimum to run at all
            executeOnly.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

            //if you wanted to grant the rights to read files then you need to grant FileIO:-
            //uncommenting this means that the appdomain created can complete its task rather than erroring 
            //as intended:-
            //  executeOnly.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, "C:\\Windows\\"));

            PolicyStatement executeOnlyStatement = new PolicyStatement(executeOnly);
            UnionCodeGroup applicablePolicy = new UnionCodeGroup(new AllMembershipCondition(), 
		executeOnlyStatement);
            //and tie the code group we defined to the level of access
            levelOfAccess.RootCodeGroup = applicablePolicy;
            
            //now we could add rights for things we trust, so they can assert that they
	     //want to perform operations that the appdomain normally can't:-
            /*
             * This is commented out because it grants the running assembly those rights, but this is an 
             * example of how to do it:-
             * 
             * After using this, you can use a MessageBox or form from inside the plugin.
             * 
             * (Change to a using multiple assemblies to restrict each one seperately)
             *
            
            //this example results in the UI being available
            PermissionSet displayUI = new PermissionSet(PermissionState.None);
            displayUI.AddPermission(new UIPermission(PermissionState.Unrestricted));
            //wrap the permission set in a statement
            PolicyStatement displayUIPolicyStatement = new PolicyStatement(displayUI);
            //And only grant this permission when the assembly has been strong named
            AssemblyName exampleAssembly = Assembly.GetExecutingAssembly().GetName();
            UnionCodeGroup selectPolicy = new UnionCodeGroup(new StrongNameMembershipCondition(
                        new StrongNamePublicKeyBlob(exampleAssembly.GetPublicKey()
                    ), exampleAssembly.Name, exampleAssembly.Version
                ), displayUIPolicyStatement);
            
            //and finally we add this new policy to the previous one so there's a chain
            applicablePolicy.AddChild(selectPolicy);
            
             */

            //create the appdomain with our basic permission set!
            AppDomain domain = AppDomain.CreateDomain("RestrictedDomain", null, 
		  AppDomain.CurrentDomain.SetupInformation); //do not supply the level permission using this
// constructor, you can't change it later using//SetAppDomainPolicy
            
            //define the total security policy here
            domain.SetAppDomainPolicy(levelOfAccess);

            //now we load our example class from below, for this example we are using the currently executing 
            //assemblies name this can be replaced with another filename, or full assembly name to load a 
            //particular one from the GAC for example
            string thisAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
            IPlugin examplePlugin = (IPlugin)domain.CreateInstanceAndUnwrap(thisAssemblyName, 
		"RestrictedAppDomainTest.ExampleClass");

            //this starts running a thread and doing other things that are potentially dodgy.
            examplePlugin.DoSomething();
            //(note that if this didn't choose to start a thread it would still run in a seperate thread anyway)

            //no (security) exception here, just to show we are unaffected by the appdomain's restrictions!
            string dummyData = File.ReadAllText("C:\\windows\\win.ini");

            Console.WriteLine("An app domain is now started and running in the background.");
            Console.WriteLine();
            Console.WriteLine("Press any key to terminate and unload the restricted appdomain!");                    
            Console.ReadKey();

            //This results in the termination of the thread the AppDomain//started, and unloading of
            //any resources (including locked files or assemblies).
            AppDomain.Unload(domain);
            
            Console.WriteLine("Domain has been unloaded.  Press any key to end.");
            Console.ReadKey();
        }
    }

    /// <summary>
    /// Defines an example plugin
    /// </summary>
    public interface IPlugin
    {
        void DoSomething();
    }

    /// <summary>
    /// An example (malicious) plugin
    /// </summary>
    public class ExampleClass : MarshalByRefObject, IPlugin
    {
        /// <summary>
        /// This is called from the unrestricted AppDomain
        /// </summary>
        public void DoSomething()
        {
            voidDelegate startDoingThings = new voidDelegate(longNaughtyOperation);
            //run the method on a different thread to demonstrate we can  	     //even start threads here
            startDoingThings.BeginInvoke(null, null);
        }

        private delegate void voidDelegate();

        private void longNaughtyOperation()
        {
            while (true)
            {
                try
                {
                    //this is denied so will fail
                    string dummyData = File.ReadAllText("C:\\windows\\win.ini");
                    //this won't ever happen hopefully :)
                    Console.WriteLine("Restricted AppDomain completed its work!!!");
                    //stop looping if the above works!
                    break;
                }
                catch (SecurityException)
                {
                    Console.WriteLine("Security exception as expected, trying  again so you can terminate this thread... :)");
                }
                Thread.Sleep(1000);
            }
        }
    }
}
Permalink  Digg This! 

I am not much of a Linux fan and mostly run Windows even for servers, but while that is true I appreciate both a wide ecosystem of operating systems incase something bad happens to Windows or a particular component, love the vastly lower cost of purchase and like both the freedom and opportunity to change things if you need to. I have one Linux server right now for example, and with Mono it can even host the same sites as the Windows servers.

My choice for Linux has been Debian since about 2001, when I settled on it after moving away from Mandrake and Redhat and their terrible RPM dependency chains. (I did try SUSE in the middle and found that to be extremely polished and usable but the reality is that I have Windows for polished.)

Anyway, I am glad to see that Debian finally has a graphical installer and the ability to run live from the CD/DVD!  You can get it from here for 32bit x86 CPU's:-

http://cdimage.debian.org/debian-cd/5.0.0/i386/

And from here for 64bit x86 CPU's:-

http://cdimage.debian.org/debian-cd/5.0.0/amd64/

The folders with BT in the name contain the bittorrent distribution files. 

Permalink  Digg This! 

TribesNext have released a patch (and a full free download!) to work around the shutting down of the login servers for Tribes 2 in late 2008, so it's now finally possible to play again.

The patch also fixes the sierra update errors that were intentionally created and means that new accounts no longer require a CD key (Vivendi gave the game away for free to promote a sequel but stopped giving out keys and shut down the servers even for those that originally bought the game back in 2001 when it was released).

Now I'm just hoping that someone can update the engine to the latest version of the Torque engine from Garage Games (this is the game that gave me the incentive to buy a license for it originally).

Permalink  Digg This! 

I needed to get the unmanaged container name of an X509Certificate2 in order to be able to get access to the equivalent private key for it on Windows Mobile (I also wanted to be able to use the same routine on Windows CE and Win32 so this works as-is on all platforms).

No decent commentary but hopefully it will help someone else who is stuck without the container name or something.


#region Structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPT_KEY_PROV_INFO
{
      [MarshalAs(UnmanagedType.LPWStr)]
      public string pwszContainerName;
      [MarshalAs(UnmanagedType.LPWStr)]
      public string pwszProvName;
      public uint dwProvType;
      public uint dwFlags;
      public uint cProvParam;
      public IntPtr rgProvParam;
      public uint dwKeySpec;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPTOAPI_BLOB
{
    public uint cbData;
    public IntPtr pbData;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CERT_DSS_PARAMETERS
{
    public CRYPTOAPI_BLOB p;
    public CRYPTOAPI_BLOB q;
    public CRYPTOAPI_BLOB g;
}
#endregion

[DllImport("crypt32.dll")]
public static extern bool CertGetCertificateContextProperty(IntPtr pCertContext, 
	uint dwPropId, IntPtr pvData, ref uint pcbData);

public CRYPT_KEY_PROV_INFO GetCertificateContextProperty(X509Certificate2 certificate)
{
     //call once to get the size, call again to get the data
     try
     {
         IntPtr certhandle = certificate.Handle;
         uint pcbData = 0;
         if (CertGetCertificateContextProperty(certhandle, 2, IntPtr.Zero, ref pcbData))
         {
             IntPtr memoryChunk = Marshal.AllocHGlobal((int)pcbData);
             try
             {
       		    if (CertGetCertificateContextProperty(certhandle, 2, memoryChunk, 
			ref pcbData))
                  {
                    CRYPT_KEY_PROV_INFO context = 
				(CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(memoryChunk, 
				typeof(CRYPT_KEY_PROV_INFO));
                     return context;
                  }
                  else
                  {
                      throw new Exception("Failed to fetch the Certificate Context 
Property, possibly due to the certificate being modified during the call.  Please try again!");
                  }
              }
              finally
              {
                  Marshal.FreeHGlobal(memoryChunk);
              }
          }
      }
      finally
      {
          //dispose of certhandle or not??
      }
      throw new Exception("Failed to fetch the Certificate Context Property");
}

(Incidentally the extra structures are handy for doing other operations with the certificate so I left them there - the blob particularly is used in a lot of places in the CryptoAPI)

Permalink  Digg This! 

(This post is getting updated as I use things and need to be able to find things easier)

Generate a new private key (but no x509 public cert yet):-

openssl genrsa -aes128 -out selfsignedprivkey.key 2048

And then generate the public certificate to go with it:-

openssl req -new -x509 -days 7300 -key selfsignedprivkey.key -out selfsignedpubkey.cer

To convert from a pkcs12 key (sometimes called a .pfx file) to an OpenSSL key without a password to protect it:-

openssl pkcs12 -in file.p12 -out file.pem -nodes

(Handy for various open source servers that don't take pcks12 keys or use CryptoAPI on Windows, including stunnel, openvpn, hmailserver 5, webmin/apache, etc - omit -nodes if the system you are exporting for supports passwords on the private key.)

The converse when using OpenSSL to make a pkcs12 bundle is:-

openssl pkcs12 -export -inkey privkey.pem -in pubcert.cer -out combined.p12

And make a new certificate request:-

openssl req -new -out request.txt

And then there's sign certificate requests with the above key (assuming you edited your openssl.cfg file to point to certificates and keys that could have been generated above):-

openssl ca -in request.txt -out response.cer

The response is effectively the public certificate for the key file saved during the request generation, just use the two as-is.  To use the cert and key you just generated in windows, use the pkcs12 -export line above.

Note that if you want to sign request that include details of the subjectAltName (for multiple hostnames or e-mail addresses for example) then you need to make sure that "copy_extensions = copy" is not commented out in the openssl.cfg file.  If you do this though watch out, someone can make themselves a sub-CA with their certificate then if you don't check every request carefully.

And finally make a CRL for it (you should edit the openssl.cfg file to set the location of this on certificates prior to signing any requests as it's included in the certificate - use crlDistributionPoints = URI:http://www.yourdomain.net/ca-crl.crl in addition to nsCaRevocationUrl  = http://www.yourdomain.net/ca-crl.crl as otherwise windows won't think there is one there):-

openssl ca -gencrl -out mycrl.crl

Other things to note when using OpenSSL CA mode: index.txt needs to be completely empty, the serial needs to have 01 and a carriage return line feed ( echo 01>serial ) and you need to create crlnumber as being 01.

Permalink  Digg This! 

The multiple minute delay where Visual Studio freezes doing this is easy to fix and related to checking for necessary components on the target; it's actually down to the Platform Verification Task defined in the MS Build targets for the compact framework, but in .NETCF 3.5 you can just skip it by adding the following build rule (or you can set this up to apply to your entire system using the environment variables screen):-

set SkipPlatformVerification=true

Pre .NETCF 3.5 I think you need to manually add this as a constraint on the target, just google for SkipPlatformVerification for an example of how to do that.

Permalink  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

(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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

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  Digg This! 

There's a limit on the data sent and recieved in WCF, resulting in errors like this when the webservice sends back larger messages:

"The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element."

The fix for this is to create a new binding with much larger limits:

System.ServiceModel.BasicHttpBinding binding = new System.ServiceModel.BasicHttpBinding();
binding.MaxBufferSize = 1024 * 1024 * 2; //bit bigger than default
binding.MaxReceivedMessageSize = 1024 * 1024 * 2;
binding.ReaderQuotas.MaxStringContentLength = 1024 * 1024 * 2;
ServiceReference1.MyServicesSoapClient client = new ServiceReference1.MyServicesSoapClient(binding, new System.ServiceModel.EndpointAddress("http://myservice/service.asmx"));

Permalink  Digg This! 

Just a reminder to myself, to let a user run an application pool without granting silly rights, you just need to run:

aspnet_regiis -ga <machine>\<user>

Which grants rights to the metabase and the temporary files folder.

Permalink  Digg This! 

I just noticed that Visual Studio 2008 is now out to MSDN subscribers!

Unsurprisingly MSDN is struggling to work :)

Permalink  Digg This! 

Well the subject says it all - I am indeed still around.

Trying to develop Windows Presentation Foundation applications with VS2K5 is more an exercise in suffering than getting any work done, even with Expression Blend to help.

Since it is coming out this month I should have plenty more to post at some point soon :)

 

Permalink  Digg This! 

Just a quick note so I can look it up easier later :)  - Simply call EnableGlass(); in the Form load event and it'll change the form to support glass.

You also need to implement support for changing the colour scheme of Vista and it needs to invalidate the form after running EnableGlass - a call to this.Invalidate();

private void EnableGlass()

{

       if (Environment.OSVersion.Version.Major >= 6) //check for vista

       {

              DwmEnableComposition(true);

              DWM_BLURBEHIND d = new DWM_BLURBEHIND();

              d.dwFlags = DWM_BLURBEHIND.DWM_BB_BLURREGION | DWM_BLURBEHIND.DWM_BB_ENABLE;

              d.fEnable = true;

              d.hRegionBlur = IntPtr.Zero;

              DwmEnableBlurBehindWindow(this.Handle, d);

              Paint += new PaintEventHandler(Glass_Paint);

       }

}

 

[StructLayout(LayoutKind.Sequential)]

public class DWM_BLURBEHIND

{

       public uint dwFlags;

       [MarshalAs(UnmanagedType.Bool)]

       public bool fEnable;

       public IntPtr hRegionBlur;

       [MarshalAs(UnmanagedType.Bool)]

       public bool fTransitionOnMaximized;

 

       public const uint DWM_BB_ENABLE = 0x00000001;

       public const uint DWM_BB_BLURREGION = 0x00000002;

       public const uint DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004;

}

 

[DllImport("dwmapi.dll", PreserveSig = false)]

public static extern void DwmEnableComposition(bool bEnable);

 

[DllImport("dwmapi.dll", PreserveSig = false)]

public static extern bool DwmIsCompositionEnabled();

 

[DllImport("dwmapi.dll", PreserveSig = false)]

public static extern void DwmEnableBlurBehindWindow(IntPtr hWnd, DWM_BLURBEHIND pBlurBehind);

 

private void Glass_Paint(object sender, PaintEventArgs e)

{

       if (Environment.OSVersion.Version.Major >= 6)

       {

              if (DwmIsCompositionEnabled())  //check Aero is enabled

              {

                     e.Graphics.FillRectangle(Brushes.Black, this.ClientRectangle);

              }

       }

}

Permalink  Digg This! 

MSDN subscriber downloads now works in Opera, I just happened to use the wrong browser to go there and it worked great.  They even suggested manually installing the file transfer manager if not running IE.

I'm impressed, congratulations to whoever at Microsoft instigated making it cross browser (or at least, not moan and stop you outright).

Permalink  Digg This! 

Microsoft has released Visual Studio 2008 Beta 2 for your enjoyment and testing, which is excellent news.

Hopefully there has been some significant improvement in the tools for WPF built into it!

Permalink  Digg This! 

Microsoft UK have a blog about MS tech in UK schools (which is indirectly related to my current job as we do a lot of work for the education sector).

Some of their examples are actually heavily related to what I actually implement (although the products I've built for my employer are here right now rather than theorhetical!!) - particularly the cashless and registration systems one presentation shows off.

Permalink  Digg This! 


(Warning: Boring legal bit of news!)

A limited precedent has been set by the European court of human rights which just found in favour of the complainant in a case against the UK government regarding the unlawful monitoring of private communications at work.  (Covered by numerous news outlets)

Effectively this overrides UK law and any contracts implicitly in respect to human rights cases - statute law automatically trumps contract law; and means that employers and the government will have to respect article 8 of the human rights act.

What does that mean?  The specifics are available on the this UK government site http://www.opsi.gov.uk/acts/acts1998/80042--d.htm - just scroll down to article 8.

It will be interesting to see if this precendent is able to be called upon in UK courts themselves, where there is a law that permits employers to monitor employees communications for the purposes of detecting things like viruses and seeing how employees interact with customers however that law itself could breach the data protection act in several situations.

With such a contradictory situation a precedent is actually needed to decide how to interpret the 'grey area' - and this could well be the one used in that situation.

Permalink  Digg This! 

using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System.ServiceModel.Channels;

 
namespace WCFTest
{
       ///<summary>
       /// A data contract stores some data and allows it to be serialised for passing to OperationContract's
       ///</summary>
       [DataContract]
       class Thing
       {
              ///<summary>
              /// Some data that will be filled.
              /// This can be a property and the getter and setter will be called on it when it is filled with data.
              ///</summary>
              [DataMember]
              public string SomeData = "";
       }
 
       [ServiceContract]
       interface IDoStuff
       {
              ///<summary>
              /// Jump method
              ///</summary>
              [OperationContract]
              void Jump();
              ///<summary>
              /// Duck method
              ///</summary>
              [OperationContract]
              void Duck();
              ///<summary>
              /// Hide method
              ///</summary>
              ///<param name="thingy">A parameter DataContract to be passed</param>
              [OperationContract]
              void Hide(Thing thingy);
       }
 
       ///<summary>
       /// This is the class that provides the actual code to do the work, it conforms to the contract defined earlier.
       ///</summary>
       class StuffWorker : IDoStuff
       {
              #region IDoStuff Members
              ///<summary>
              /// The jump method, with code
              ///</summary>
              public void Jump()
              {
                     Console.WriteLine("Jump: Remote method executing here.");
              }
              ///<summary>
              /// The duck method with code
              ///</summary>
              public void Duck()
              {
                     Console.WriteLine("Duck: Another remote method executing here.");
              }
              ///<summary>
              /// Hide something method
              ///</summary>
              ///<param name="thingy">The item to hide</param>
              public void Hide(Thing thingy)
              {
                     Console.WriteLine("Hiding: Remote method is hiding " + thingy.SomeData);
              }
              #endregion
       }
       ///<summary>
       /// A self hosting WCF service that does not use the configuration file
       ///</summary>
       class Program
       {
              ///<summary>
              /// Program start
              ///</summary>
              ///<param name="args"></param>
              static void Main(string[] args)
              {
                     Console.Write("Starting the service...");
                     ServiceHost host = start();
                     Console.WriteLine("Ok");
                     Console.ReadKey();
              }
 
              static ServiceHost start()
              {
                     //this does the hosting
                     ServiceHost host = new ServiceHost(typeof(StuffWorker), new Uri("http://localhost:7000/WCF/"));
                     //this lets you browse to the WSDL:
                     ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                     smb.HttpGetEnabled = true;
                     smb.HttpGetUrl = new Uri("http://localhost:7000/WCF/Meta");
                     host.Description.Behaviors.Add(smb);
                     //This is the bit that handles HTTP
                     WSHttpBinding ws = new WSHttpBinding(SecurityMode.None);
                     Binding mex = MetadataExchangeBindings.CreateMexHttpBinding();
                     //These are the endpoints on that HTTP server
                     host.AddServiceEndpoint("WCFTest.IDoStuff", ws, "http://localhost:7000/WCF/DoStuff"); //one to actually do things
                     host.AddServiceEndpoint(typeof(IMetadataExchange), mex, "http://localhost:7000/WCF/Meta"); //and one to provide metadata
 
                     // start listening
                     host.Open();
                     return host;
              }
       }
}

A self hosting service example mostly so I don't forget how.  Remember to add references to System.ServiceModel and System.Runtime.Serialisation!

Permalink  3 Comments  Digg This! 

If you are getting the following error on its own in a message box:

Exception from HRESULT: 0x80041001

Whilst trying to update your WCF service reference then it can't access either the .map file or the C# source file.  I happened to have them open in visual studio whilst trying to update the reference!!

Oddly if you open them after running the program then it doesn't moan...  For some reason I think it may be to do with clickonce locking the files.

Permalink  Digg This! 

I very rarely write about anything to do with work, usually I intentionally avoid topics that would relate to it but I feel that it is my moral duty to provide information should people look for it.  I’d also like to defend the system I wrote which doesn’t seem to be happening in the media.

 

Firstly I feel people should know a little about the background before they read the actual article:

 

1) I disagree with ID cards and biometrics as a requirement for things, there should be no reason you need to constantly prove who you are and you should always have a choice to avoid needing to prove your identity.  Maybe you will loose some benefits that you would have gained, but there should never be an infringement of your rights or make something more difficult than it is now.

 

2) I’m the programmer of a cashless catering system that allows fingerprinting students and using their finger instead of carrying the equivalent of a debit card.  I’m responsible from a technical standpoint for how it works technically underneath (although we do buy in the algorithm to analyse the image of a fingerprint that comes from the reader and work out what bits we’re interested in).  Yes, I write one of those systems that is being both criticised and is massively in demand right now for various reasons.

 

Okay now that’s out of the way I see a lot of publicity about the fingerprint systems in use today and I will leave the other companies and people involved to defend their own systems, but more than likely they are using a similar technique to what I have used simply because it’s easier.

 

I’ll start by explaining why I believe the system I wrote isn’t a threat to privacy, then I’ll add what we’re going to provide shortly to ENSURE it isn’t a threat to privacy where the customer (the student usually) wants and we’ll cover why I think our system benefits from having support for fingerprints.

 

How the System Works, and How it Protects the Fingerprint

 

The system I developed does not store a fingerprint.  We really don’t want to store one - we don’t want to have to comply with police requests for a forensically valid database of fingerprints.  It shouldn’t be our responsibility and the student didn’t give us their fingerprint for that reason.  What is needed for criminal forensics is a picture of the fingerprint otherwise there’s doubt, and our system deliberately introduces doubt in favour of:

-         Speed

-         Lower storage requirements

-         Lower memory requirements

-         Accuracy of detecting the closest fingerprint (note: not the exact person, but just the most similar)

-         A degree of privacy.

 

To achieve this we store relative points on the fingerprint and discard data that applies specifically to the persons fingerprint.  Techie bit: We also cryptographically hash the details where possible so there’s no way to get back to the original ones to allow generating a fingerprint that would pass as the original from the data, but you can read up on irreversible encryption at wikipedia or somewhere else.  Basically if you don’t have the data the hash is made from it makes the data useless (that being the person’s real fingerprint).

 

So what we have is a map that says something like this: “left one unit and up one unit there’s an interesting point that goes up, right two of those distances there’s an interesting point that goes down” – that’s two points but you get the picture.  It’s like street directions only we add in that the directions are actually stored in a one way method.  The unit of distance is actually not specified in any way other than the relative distance either, so what you end up with is a set of street directions that is like “Turn left, then turn right then right again.  Turn right, turn left, turn right.”  Could you work out what city those directions are for?  I hope not.  But if you were given a bunch of maps and tried them all, it would only be possible on a few.

 

So it is my belief that our system stores sufficiently little of the fingerprints they can’t be used anywhere else.  In reality it causes us business problems as schools would like to have a single fingerprinting session for all their students but that’s a trade-off we willingly make.

 

I won’t go into the protection we apply to the database, if someone wants that just e-mail me and I’ll cover it.  Needless to say if you turned up on site you shouldn’t be able to just stick a USB key in and copy it without physically getting to the server and entering valid usernames and passwords.  Which in most circumstances we as a company don’t have and only the school have.

 

And finally, the last step is that (as far as I’m aware) we require both the students and parental permission prior to fingerprinting.  In many cases our projects department has helped draft the consent letters the schools send out, and we will provide assistance should anyone not wish to be fingerprinted – or indeed change their mind and want their fingerprints removed from the system!!

 

Measures Being Taken and Who Benefits

 

To buy a meal you would either pay cash (we provide full support for cash by an account, or cash from anyone without an account in our system) or swipe a card/place your finger on the reader and the till operator sees your picture on screen and selects the food you have on your tray.   They then press confirm sale and you walk off having paid for the meal.  If the person is on free school meals (low income family) then there’s nothing said, the money is just there as if it were loaded on by the student earlier in the day.

 

Kids love it!  Bullying gets reduced a little!  Till staff love it as it’s quick and easy!  Catering companies love seeing how much of something they are selling easily, how much it changed during the year (who would guess that sandwiches get more popular in the summer?).  The schools love the fact they don’t handle as much cash – it all goes through an ATM like machine that eats the money and counts it up for them, or an online service that the parent uses.  Parents love it as they can ask for what their son/daughter has been eating and see their balance online!

 

Privacy advocates hate it!  Kids that steal dinner money really do hate it (they pass a card over to the till operator and a different picture appears and it’s known they stole the card), and anyone that was stealing from the tills must hate it.

 

Occasionally the two ends up as the same thing, so in this case we can offer a few options:

-         The tills accept cards (proximity and magnetic stripe) as well as biometrics, so we can give the students cards if that satisfies their discomfort with using their fingerprint.

-         The person can always opt out of the entire system and pay cash, as long as they aren’t free school meal.  If they are free school meal they need to pay cash and use a voucher (we support the idea of a voucher sale in our software).

But we just had a request from a parent to not store data about a student.  And the school don’t want them to just use cash; they want the system to still behave as it does now.  But the government need us to be able to back up our financial transactions!!

 

Talk about contradictory requirements.  How do we do this?  I’ve not finished the work to do it yet, but I fully intend to find a solution that makes the transaction of the individual totally anonymous:

-         We will not store who the sale was made to.

-         Or by, because we don’t want the human operator being asked.

-         Or on what till.

-         We still need to store their name and balance, and financial transactions.  So we’ll flatten the financial transactions and have just one transaction for how much they loaded and one for how much they bought.

-         If the person is free school meals they will need to use a voucher and say they are free school meals at the till.  We can give them some paper vouchers they can give to the till operator then.  If the person that is marked uses a voucher, we’ll make two separate entries, one for the voucher purchase and one for the items they were sold.  We won’t store the time on the voucher purchase.

 

If anyone can think of anything else I need to do, please e-mail me.  Also if you have any questions about biometrics or indeed anything to do with the system I write (or want contact details of our sales people to buy it!!) please feel free to e-mail me.  My contact details are on the right.

 

I would rather not have my name posted on sites like http://www.leavethemkidsalone.com/ and my employer already is, so please respect my privacy as much as I do others.

 

This article is being posted publicly though so anyone can feel free to link to this.  If you have any criticism, please feel free to comment (sign up with fake details if you wish) or e-mail me!

Permalink  Digg This! 

Jen Frickell needs to restore her site, it was about a year ago I last visited and it was down then too.

Why I keep remembering to check her site though for no apparent reason is extremely odd...  Maybe it was that her site used to brighten my day when I did network admin?

Permalink  Digg This! 

It's all in here:

http://www.microsoft.com/technet/community/columns/cableguy/cg0902.mspx

Though there seems to be no way to make it auto-generate routes and issue them to interfaces, so if you're on DHCP you are relatively stuffed :(

Edit: Nothing a simple app to generate the appropriate netsh commands can't solve.

netsh interface ipv6 set interface "Internet" forwarding=enabled advertise=disabled
netsh interface ipv6 set interface "LAN" forwarding=enabled advertise=enabled
netsh interface ipv6 6to4 set state enabled
netsh interface ipv6 set interface "6to4 Tunneling Pseudo-Interface" forwarding=enabled
REM WWXX:YYZZ = decimal to hex conversion of
WWW.XXX.YYY.ZZZ with two hex digits per byte
netsh interface ipv6 add route 2002:WWXX:YYZZ:1::/64 "LAN" publish=yes

Permalink  Digg This! 

Getting a long PageRequestManagerParserErrorException error with some HTML in it when using ASP.NET Ajax with something like a Gridview?  Does the error only happen once in a while, usually the first time you do something specific after a long delay?

Know you're not actually using Response.Write and only using MS controls?

Well, maybe asp.net is trying to do a postback for you to create a cookie when you are using the session variable.

Solution: A dirty solution was calling Session["IWantASessionCookie"] = true; on a page load, after this the problem went away as ASP.NET didn't need to create the local cookie when doing things in the UpdatePanel as it had already done it.

Anybody have a neater solution to extend ASP.NET Ajax to create the cookie automatically using client side Javascript instead of ASP.NET trying to do it normally?

Permalink  Digg This! 

Just a handy little method I chucked together that might be useful when prototyping stuff in command line applications:

/// <summary>
/// Draw a progress bar at the current cursor position.
/// Be careful not to Console.WriteLine or anything whilst using this to show progress!
/// </summary>
/// <param name="progress">The position of the bar</param>
/// <param name="total">The amount it counts</param>

private static void drawTextProgressBar(int progress, int total)
{
 //draw empty progress bar
 Console.CursorLeft = 0;
 Console.Write("["); //start
 Console.CursorLeft = 32;
 Console.Write("]"); //end
 Console.CursorLeft = 1;
 float onechunk = 30.0f / total;
 
 //draw filled part
 int position = 1;
 for (int i = 0; i < onechunk * progress; i++)
 {
  Console.BackgroundColor = ConsoleColor.Gray;
  Console.CursorLeft = position++;
  Console.Write(" ");
 }

 //draw unfilled part
 for (int i = position; i <= 31; i++)
 {
  Console.BackgroundColor = ConsoleColor.Black;
  Console.CursorLeft = position++;
  Console.Write(" ");
 }

 //draw totals
 Console.CursorLeft = 35;
 Console.BackgroundColor = ConsoleColor.Black;
 Console.Write(progress.ToString() + " of " + total.ToString()+"    "); //blanks at the end remove any excess
}

Permalink  2 Comments  Digg This! 

This is just a quick example so I don't forget how to search generic lists easily and have to hunt for it again, but I've fleshed it out so it will hopefully be of help to someone else!

//Set up our example generic List
List<string> myItems = new List<string>();
myItems.Add("This isn't going to be found");
myItems.Add("Nor this");
myItems.Add("But it will find this example for nullify!");
myItems.Add("And it will find this nullify example too!");
//This is our search term, it is a local variable
string localVariable = "nullify";

//Do the search using a Predicate<> delegate here.
//
//This can point to a method that takes a parameter of the type, but
//doing that will result in not being able to pass parameters to the
//method (its parameters are predefined as the type, with a boolean
//return as far as I can see). We get around this by using an
//anonymous delegate, so it is inline and can access local variables

string[] matches = myItems.FindAll(delegate(string searchItem) {
    //this is an anonymous delegate, that
    return searchItem.IndexOf(localVariable)>-1;
}).ToArray();

 

Updated 7/2/2010: You could also use a Lambda expression to do this with a few less characters:-

string[] matches = myItems.FindAll(searchItem => {
    //this is an anonymous delegate, that 
    return searchItem.IndexOf(localVariable)>-1; 
}).ToArray();

Permalink  Digg This! 

I have played around with plasma pong a few times in the past, and am always impressed by how spectacular the fluid dynamics are... It's a great game! Check it out: www.plasmapong.com.

Permalink  Digg This! 

I have had a typematrix 2030 for some time, and found that it reduces the pain in my hands when typing - but that it is missing the key that is a backslash/pipe when used with a UK layout... And if you don't use a UK layout you loose several other keys.

So I eventually put the effort in and made a new keyboard layout for it!  This is mostly just so I don't lose it :)

TypeMatrix 2030 UK keyboard layout with the back-slash, pipe and tilde keys reinstated to their US keys.

Permalink  Digg This! 

Well, in addition to the high profile slew of products Microsoft also released Windows Powershell for XP and Windows 2003!

I've been looking forward to an improved shell for Windows for some time as I like using Bash on Linux (and refuse to run it on Windows using Cygwin for anything more than just testing).

Permalink  Digg This! 

Well, it is out, although at 50MB clients will be even less inclined to install it. I'll hold judgement on how awesome it is till I've played with it....

http://www.netfx3.com/blogs/news_and_announcements/archive/2006/11/06/.NET-Framework-3.0-has-been-released_2100_.aspx

Once installed you can now happily* watch a true 3d accelerated cube spin in your browser...!

I am actually really looking forward to the Windows Communication Framework - ever since I first saw C# and .NET (well they were showing Longhorn really) demonstrated by Chris Anderson and Don Box.

Permalink  Digg This! 

To fix the error:

Unable to obtain a server-assigned IP address. Try again later or enter an IP address in Network settings.

Fire up your registry editor then head to:

[HKEY_LOCAL_MACHINE\Comm\RNDISFN1\Parms\TcpIp]

Set AutoCfg to 1

Set EnableDHCP to 0

Soft reset the device and there should be no further errors!

Permalink  Digg This! 

If you're getting an annoying error about an IP address not being able to be assigned then you are not alone.

Well, seeing the dialog titled "Pocket PC Networking" with the text:

Unable to obtain a server-assigned IP address. Try again later or enter an IP address in Network settings.

Annoyed me one too many times and google hasn't quite got it yet - if you too have this problem and have any idea then please add a comment or possible solutions. My trial and error attempt at fixing it (so far) ended with using your favourite pocket pc registry editor (like PHM Regedit) and adding the following three DWORD registry keys in [HKEY_LOCAL_MACHINE\Comm\VMINI1\Parms\TcpIp] with nice and large numbers (FFFFFFFF in hex!):

  • DhcpMaxRetry
  • DhcpInitDelayInterval
  • DhcpRetryDialogue

This comes from MSDN's Windows CE 4 documentation but still applies.

The lack of details in the error is EXTREMELY frustrating.

Permalink  Digg This! 

Better late than never, I noticed a couple of days ago there's a GoLive license now for Indigo and a release on my MSDN that works under the RTM of .NET 2.0!  It's actually a public release too, so go download it.

Congratulations to Don Box's team for getting it out the door.

Permalink  Digg This! 

My webserver in the states has been hammering the processor for the last 405 hours - all SQL Express hitting 100%.

The other instances of SQL Server were idling, so I don't know what it was getting up to, as it certainly wasn't dealing with any serious load!

Permalink  Digg This! 

The missing ones for Visual Studio 2005 (including some quite nice ones) are available from MSDN here.

Permalink  Digg This! 

As everyone here knows I'm a bit of a C# fanatic... And I just found Python which is a perfect complement to C#! It lacks the heavy duty features of C# but does have simplicity and speed of programming for 'getting an answer' making it an ideal scripting language.

Which leaves only one thing to do, mix them together! IronPython does just this - it is a version of Python written in C# and designed to run inside .NET programs. This video on MSDN TV is a good introduction to it!

I've even found a couple of pocket pc python interpreters out there which is nice.

Permalink  Digg This! 

I am SO happy I've found the solution to this annoyance - from http://msdn.microsoft.com/vstudio/support/knownIssues/default.aspx:

1.45 Using Windows Roaming Profiles may cause first time launch message to show on each startup.

When any one of the Visual Studio family of products is used with Windows Roaming Profiles, the first time launch message that says "Visual Studio 2005 is configuring the environment for first time use. This might take a few minutes." might appear on every session startup. This might cause unnecessary slowdowns in startup performance.

To resolve this issue

Click on the Tools > Options... Select "Import and Export Settings", and change the path under "Automatically save my settings to this file:" to a path that is NOT under the "My Documents" directory.

Permalink  Digg This! 

So I noticed an article mentioning you couldn't get clickonce working in Opera or Firefox. Well, I don't know about Firefox but I've had it working in Opera 8 for some time now.

The first problem you hit is that the framework isn't returned in the user agent. To solve this create a folder in your my documents called OperaJS and a new text document adddotnet2.js. Include the following:

// Adds the user .NET CLR user agent
navigator.userAgent = navigator.userAgent+'; .NET CLR 2.0.50727';

Now when looking at a clickonce install site you get the option to just install (make sure you actually have 2.0.50727).

The next step is to make Opera pass the url to the .application file format to EXPLORER. I would pass this to the appropriate file to run it, but then it won't be in the right context so we'll settle for a little dodgyness.

Click Tools, Preferences, Advanced, Downloads then add a new mime type of application/x-ms-application with an extension of application, then check "Open with other application", enter "explorer.exe" and check "Pass web address directly to application".

Now you should be able to navigate to clickonce applications and use them as if you were in IE!

Update 5th July 2008:

There is a firefox add-in here.

Permalink  Digg This! 

Well, I'm off to someplace with work, if anyone needs to contact me be prepared I'll be on roaming and not available like normal. Permalink  Digg This! 

Well, so far I'm unhappy with SQL 2005 and I've given it a full month so I'm going to rant a bit.

Microsoft did a good job of making visual studio 2005 pretty solid (although the visual designer is so very slow), but SQL 2005 is lacking in features that used to be there, and seems to consume far more resources than it ever used to.  Why can't I import/export objects (to move a database around) for one thing?!?

It seems enterprise manager and query analyser are much better than SQL management studio in almost every way (even usability, there's definitely more clicking needed to get things done in the new management studio and others have agreed with this).

I have a variety of hardware (from low spec with just 512MB ram and a 3Ghz cpu to a dual 64bit beast with gigs of RAM) running the new tools, and there is no solution to the unbelievable slowdown experienced when managing the system (I haven't been able to find any actual decrease in query performance against the server process although it now regularly consumes 15% on  machines whilst idle).

Taking copying a database between servers as an example of my frustration, on two machines I have:

TITLE: Copy Database Wizard
------------------------------

No such interface supported

Errors when trying to copy a database using SQLDMO.

On a working machine I found that it can't copy to or from SQL 2000.  Then I found it can't copy to SQL 2005 express.  What's going on up there, did nobody test these features and if so what cloud were they on when they shipped something incapable of moving databases around?

At present I have to generate scripts for everything, then run them against the other server, then run a set of hand built scripts to enter the data...

Resolution (29 Jan 2006): Run the following to register MS XML again:

regsvr32 msxml3.dll
regsvr32 msxml6.dll
I have no idea how this got broken and why nothing else was working, but I did have the old version on the system. Still, scripting and copying doesn't work properly but apparently its being investigated.

Permalink  1 Comments  Digg This! 

I've been playing with Microsoft Sharepoint and have to say I'm very impressed...  I might replace this site with a sharepoint site if I can make it do exactly what I want (which I should be able to do! I can program webparts for it...) Permalink  Digg This! 

Hot on the heels of its big brother SQL 2005, SQL Express now has a management studio that provides similar features to the tool I was originally writing for MSDE!

Available for download from Microsoft now, it offers a nice selection of features that the free version of SQL was always missing (it's annoying when you have an engineer on site and they are struggling to find a problem that could be located with a single query!). Permalink  Digg This! 

My MSDN subscription is a bit broken at the moment unfortunately but it appears that Visual Studio 2005, .NET 2.0 and SQL 2005 are now all out and available to download!

Permalink  Digg This! 

There is currently a threat of another large hurricane, this one is likely to make landfall and directly affect the area that my datacentre is located in (Houstan, Texas).

Although over 10,000 gallons of diesel are already on hand please ensure that queeg.nullify.net (located at Fortress ITX in New Jersey) is correctly set up to provide backup DNS and/or mail as needed.

Good luck to everyone staying behind in the datacentre in the face of the evacuation and the storm.

Permalink  Digg This! 

As many of you know, I am a great fan of Opera. It has proved to be a highly secure, fast and powerful browser... Basically sites work and work well, and I can limit what they can and cannot do.

Now thanks to google changing their contract with opera when it comes to ad revenue from searches they can afford to make it 100% free.

Totally written from scratch and not based on Mosaic, Mozilla, IE or any other existing browser, it supports the following operating systems:

  • Windows!
  • Linux (a large number of distros AND a tar.gz)
  • FreeBSD
  • Solaris
  • OS/2
  • QNX
  • And a large number of mobiles
  • Mac

I still urge you to pay for premium support or whatever they call their subscription service in support for this great browser; but hey, if you can only pay by using it that's still good.

Permalink  6 Comments  Digg This! 

Here's a static method that sets a printer as the default on a system, using the windows scripting host network stuff from C#.  It's here so I can look it up if anyone else asks in future since I couldn't easily find how to do this in C#...  (I know the exception handling is poor)

/// <summary>
/// Sets a default printer
/// </summary>
/// <param name="name"></param>
public static void SetDefaultPrinter(string name)
{
   try
   {
      //late bind to the wsh network com object
      //create an instance of a reflection type
      Type t = Type.GetTypeFromProgID("WScript.Network");
      //create an instance using the system activator, consuming the type
      Object o = Activator.CreateInstance(t);
      //invoke the method using the object we created
      t.InvokeMember("SetDefaultPrinter", System.Reflection.BindingFlags.InvokeMethod, null, o, new object[] {name});
   } 
   catch (Exception)
   {
      throw new Exception("Unable to set a default printer. The printer may not exist or the WScript.Network com object may have a problem.");
   }
}

Permalink  Digg This! 

Hosted sites may start to experience routing issues, I've had several messages regarding them already. I suspect this is as the diesel runs out at telco sites in or around the Gulf Coast. Ev1 are doing a great job in Houston and there are no visible problems with connectivity over here in the UK/Europe. Permalink  Digg This! 

You can get a free license key for Opera (similar to firefox but commercial, faster, more secure, smaller and with a lot more features - like torrent support) at their 10th birthday party!. Permalink  Digg This! 

I've worked on this for a total of eight hours now inbetween everything else, but this is a sneak peak of a new product/freebie I am working on.  Take special note of the syntax editor...  I couldn't make richedit do what I wanted, so have been writing a syntax highlighting, intellisense supporting editor from scratch.

Eventually it will colour keywords and provide region support, but just showing text, the line numbers, a movable cursor and scroll bars was a milestone for me given it is done totally from scratch (even if the scrollbars are yet to be mouse enabled).

Zorg Sql Manager

Permalink  2 Comments  Digg This! 

I am still alive, and I must apologise for not posting anything to my blog but work has been eating most spare time whilst I've been getting some software out the door.

Hopefully work will no longer need so much of my time though, so I've started a few projects, one of which is a management studio for MSDE/SQL Express that allows profiling and various other features that I can't find anywhere else (some are in the toolset that Microsoft ship which isn't licensed for use with MSDE and isn't freely downloadable, but some aren't to be found anywhere afaik).  Just trying to add intellisense to my query execution part of the application now.  One bug to fix with stopping traces and I'll put a version up for download.

In unrelated news I've also enabled SPF on all the domains on xerxes using Mail Essentials from GFi, so those of you with e-mail on queeg are the last not to get it - hopefully debian has something to implement SPF with exim otherwise I'll need to write something to wrap it...

Permalink  Digg This! 

Apologies for the lack of posting, but crunch mode at work whilst approaching beta has stopped me having time.  I'll probably get some more posted when it's over. Permalink  Digg This! 

This is good fun :) - a DHTML lemmings remake!

http://193.151.73.87/games/lemmings/index.html

It works on IE and Opera, so I have no doubt it'll work on Firefox/Mozilla.

Permalink  Digg This! 

[ Taken from my post at the ms forums here: http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=1014 ]

This is a bit bodged but I just hashed it together to see how easy it would be.  I thought back and could have just iterated over chararray, but it works which is what counts :D

/// <summary>
///
Represents a paragraph in English
///
</summary>
public
abstract class Paragraph
{
///
<summary>
///
Convert a string in arbitrary case to English sentence capitalisation.
///
</summary>
/// <param name="text">The text to convert
</param>
/// <returns>The paragraph of text
</returns>
public
static string ToSentenceCase(string text)
{
string
temporary = text.ToLower();
string
result = "";
while
(temporary.Length>0)
{
string
[] splitTemporary = splitAtFirstSentence(temporary);
temporary = splitTemporary[1];
if
(splitTemporary[0].Length>0)
{
result += capitaliseSentence(splitTemporary[0]);
}
else
{
result += capitaliseSentence(splitTemporary[1]);
temporary =
""
;
}
}
return
result;
}

private static string capitaliseSentence(string sentence)
{
string
result = "";
while
(sentence[0]==' ')
{
sentence = sentence.Remove(0,1);
result+=
" "
;
}
if
(sentence.Length>0)
{
result += sentence.TrimStart().Substring(0, 1).ToUpper();
result += sentence.TrimStart().Substring(1, sentence.TrimStart().Length-1);
}
return
result;
}

private static string[] splitAtFirstSentence(string text)
{
//these are the characters to start a new sentence after
int
lastChar = text.IndexOfAny(new
char[] {'.', ':', '\n', '\r', '!', '?'})+1;
if
(lastChar==1)
{
lastChar = 0;
}
return
new string[] { text.Substring(0, lastChar), text.Substring(lastChar, text.Length-lastChar) };
}
}

Permalink  Digg This! 

[posted by me on the new Microsoft Forums at http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=1266 ]

ProcessStartInfo pi = new ProcessStartInfo("cmd.exe", "/c dir");
pi.WindowStyle = ProcessWindowStyle.Hidden;
pi.RedirectStandardOutput =
true
;
pi.UseShellExecute =
false
;
Process p = Process.Start(pi);
p.WaitForExit();
p.Start();
TextReader t = p.StandardOutput;
MessageBox.Show(t.ReadToEnd());

Permalink  Digg This! 

A colleague of mine at work was having some trouble with multithreading this morning and I suggested using asynchronous delegates (a delegate - something you can make do something else, in .NET this can be used as a form of trigger for starting any number of other methods that take and return the same parameters as it uses - that runs its methods in another thread).  This got me thinking so I decided to do a quick tutorial for anyone else who hasn't yet encountered this way of doing multithreading!

This is a short sharp example, and can be created in a simple form.  Remember not to try to update the form from the method being run by the delegate - if you want to do this you need to run a method that checks if InvokeRequired is true, then runs Invoke(myMethodsOwnName, new object {myFirstParameter, mySecondParameter, andSoOn}) if it is true, or does whatever UI editing is needed if false - so it is calling itself in the context of the user interface.

Anyway, on with the example!

/// <summary>
///
A button on the form
///
</summary>
private
void button1_Click(object sender, System.EventArgs e)
{
   delegateInstance = new
ExampleDelegate(beginThink);
   d.BeginInvoke(5, new
AsyncCallback(finished), null);
}

This is where the program runs, a simple button that can be pressed.  We want to do something CPU intensive and blocking in here but instead we create an instance of a delegate and use the BeginInvoke method, the methods parameters come first (in this case 5) and then the callback method to run, and null for the object state.

/// <summary>
///
An instance of the delegate used to call a method asynchronously
///
</summary>
private
ExampleDelegate delegateInstance;

/// <summary>
///
The example delegate
///
</summary>
private
delegate string ExampleDelegate(int param);

/// <summary>
///
The example method that would block the user interface
///
</summary>
/// <param name="workOn">Number of seconds to block for
</param>
/// <returns>Some string
</returns>
private
string beginThink(int workOn)
{
   Thread.Sleep(workOn*1000);
   return
"Done";
}

This is our ficticious CPU intensive action, in this case we'll just block the thread by seeping for workOn seconds.

/// <summary>
///
The method that is called when work is done
///
</summary>
/// <param name="results">The results of the work
</param>
private
void finished(IAsyncResult results)
{
   MessageBox.Show(delegateInstance.EndInvoke(results));
}

And this is run when the asynchronous delegate is completed.  EndInvoke automatically return the same type as the delegate, (a string) which makes this nice and easy.

Permalink  Digg This! 

Two days ago I got my new P7010 laptop, although it's hardly large enough to be called a laptop with a tiny 10.6" screen and being light enough to use with one hand whilst holding it with the other.

This is a review, or maybe a quick pass over it.  I'm typing it on it, but I won't be doing any serious coding on it due to some oddities with the keyboard layout (cursor keys, home and end which I regularly use are all one thing and need to use the fn key, which is in the wrong place being between ctrl and windows rather than on the far left.  And the delete key is in the top right.  I'll upload some pitctures to my public photo album shortly.)

There are several things nobody says when they talk about this laptop, and I wondered whether I should buy it because of this.  I'm very glad I did.

Firstly, it runs 3D games very well - if you use lower end settings.  This is to be expected with an Intel extreme graphics chip in it, and it's a tradeoff you need to make for the small size and long battery life.  I've yet to try Half-Life 2, but will update this post if I get a chance - probably on the weekend.

The 1.1Ghz processor easily beats a P4 3Ghz processor in appearing to be fast, it may not when encoding an MP3, or some other maths intensive task but it seems highly responsive - infact it appears as responsive as my Opteron workstation when using applications (although the drive is slower, however this is to be expected 10,000 rpm is physically a lot faster than 4200 rpm!).

Next, the screen everyone says is too high res for the size IS too high res for the size - but I've noticed that after using it for a short while I have easily gotten used to it.  Maybe support for 1024x640 would have been better than 1280x768, but this is at least future proofed for when Windows is fully vector based and will scale.  The screen is extremely crisp, clear and bright, though reflective and needing cleaning to prevent dust accumulating.  The screen is perfect for DVD's and videos - anything that scales..

It also has a slew of standard and not so standard features: Wireless, cd/dvd drive (in such a small laptop!) sd/mmc/memory stick reader, compact flash and seperate PCMCIA slot, two USB ports, video out, LAN, international modem, svideo and firewire.  An impressive battery life (so far tested to 6 hours 50 minutes real world use, with wifi on, without the second battery and with the screen not on the lowest brightness) rounds it off.  An impressive selection of modular options from Fujitsu Siements also makes this a very versatile device.

Missing features: GPRS/GSM, Camera, Bluetooth, serial and parallel ports, rear connectors (everything is conveniently on the sides), a top catch (although it seems to be sprung with some kind of mechanism that holds it shut and closes it quickly, but allows the screen to move freely to any angle), and dedicated graphics memory and card.

Overall I'm very impressed.  Apart from bluetooth most of the features are either not neded or an acceptable tradeoff for a device this small, the absence of bluetooth however should have meant the necessity of GPRS/GSM in a portable device - yet I am fine with wifi for the places I go.  As a large (book sized) PDA it serves well, as something to take notes on it beats a pda hands down - as you can see from this having been typed on it (I have no trouble touch typing on it).  As something to code on, we shall see.  As an entertainment device - well, you can watch dvd's, listen to music and play games - what more do you want?

I don't recommend anyone try to make such a small device their primary laptop or even worse primary computer without an external screen and keyboard/mouse however if you want small and portable because you travel or just don't want to carry a full laptop then this is the machine for you.

Permalink  Digg This! 

I've been looking for a good .NET profiler and always been hit by large price tags, bloated software and general failure.

So, I was happy to discover the person that made NAnt also makes NProf!  http://nprof.sourceforge.net/Site/SiteHomeNews.html

I had to delete regasm.exe from his archive as it throws an exception for some reason, but when using the one on my system it registered fine and is now working - happily integrated into Visual Studio .NET :)

Permalink  Digg This! 

Mere minutes after posting about VSA I notice a post on developer fusion by Mark Belles that is MUCH better at describing Microsoft.VSA - I suggest if your interest is peaked by my sample code you go have a look at his article!

Permalink  Digg This! 

Have you ever wanted to be able to add scripting to your application?  So your users can write a script and you can have it run inside it, adding menus, etc. - like Microsoft Visual Basic for Applications allows in Office.

This is a preliminary article, I'll try to rewrite this but I just took a 20 minute break from work (need to have something done for tomorrow - so yes, still working at 10PM!  GRR) and finally managed to get scripting using Microsoft.VSA working from a C# application.  Apologies for the lack of tabs in the formatting and lack of explanation.

This is missing the ability to access anything in the running application - you need to register a global item (in the same way as code items and references are added) to do that - then it is exposed as an object inside the environment running the script.

Add references to Microsoft.VisualBasic.Vsa and Microsoft.Vsa (when I repost I'll be using JScript.NET - never fear!) and add the following using clauses:

using System.Reflection;

using Microsoft.Vsa;

using Microsoft.Win32;

And then add the following code to a button or similar (with a textbox to enter your script in) - this will create a scripting engine object, then run your script inside it.  The best test is to show a message using MessageBox.Show(""); (remember though, it's VB!)

ScriptEngine eng = new ScriptEngine();
eng.Start();
IVsaCodeItem c = eng.CreateCodeItem(
"ScriptItem");
c.SourceText = textBox1.Text; //Where this is a simple vb.net script with a class called Script and a Sub called Main
eng.Run();

And now, the main code chunk snippet that is the scripting engine:

public class ScriptEngine : IVsaSite
{
private IVsaEngine Engine;
public ScriptEngine()
{
Start();
}

public void Start()
{
try
{
Engine =
new Microsoft.VisualBasic.Vsa.VsaEngine();
Engine.RootMoniker =
"myapp://net.nullify.www/ScriptEngine";
Engine.Name =
"vsaEngine";
Engine.RootNamespace =
"VsaEngine";
Engine.Site =
this;
Engine.InitNew();
CreateReference(
"Mscorlib.dll");
CreateReference(
"System.dll");
CreateReference(
"System.Windows.Forms.dll");
CreateReference(
"System.Data.dll");
CreateReference(
"System.Drawing.dll");
CreateReference(
"System.XML.dll");
}
catch (VsaException ex)
{
MessageBox.Show(
"Scripting engine error: "+ex.Message);
}
}

public IVsaReferenceItem CreateReference(string assemblyName)
{
IVsaReferenceItem item = (IVsaReferenceItem)Engine.Items.CreateItem(assemblyName, VsaItemType.Reference, VsaItemFlag.None);
item.AssemblyName = assemblyName;
return item;
}

public IVsaCodeItem CreateCodeItem(string itemName)
{
try
{
IVsaCodeItem item = (IVsaCodeItem)Engine.Items.CreateItem(itemName, VsaItemType.Code, VsaItemFlag.Class);
return item;
}
catch (VsaException ex)
{
MessageBox.Show(
"Problem creating the code item: "+ex.Message);
}
return null;
}

public void Run()
{
if (Engine.Compile())
{
Engine.Run();
Type type = Engine.Assembly.GetType(
"VsaEngine.Script");
MethodInfo method = type.GetMethod(
"Main");
method.Invoke(
null, null);
}
}

#region IVsaSite Members

public object GetEventSourceInstance(string itemName, string eventSourceName)
{
// TODO: Add ScriptEngine.GetEventSourceInstance implementation
return this;
}

public object GetGlobalInstance(string name)
{
// TODO: Add ScriptEngine.GetGlobalInstance implementation
return null;
}

public void Notify(string notify, object info)
{
// TODO: Add ScriptEngine.Notify implementation
}

public bool OnCompilerError(IVsaError error)
{
MessageBox.Show(
"["+error.Line+"] Compiler error: "+error.Description);
return true;
}

public void GetCompiledState(out byte[] pe, out byte[] debugInfo)
{
// TODO: Add ScriptEngine.GetCompiledState implementation
pe = null;
debugInfo =
null;
}

#endregion

}

And finally to test this you can use the following code:

imports System
imports System.Diagnostics

module Script
sub Main()
MessageBox.Show("Boo")
end sub
end module

Permalink  2 Comments  Digg This! 

Robert Scoble is once again on his high horse about how RSS is amazing, comparing it to GUI versus CLI without looking at how primitive RSS itself is.  I propose it is around the other way, and that there is a successor for RSS out there.

RSS and ATOM as they are today WILL become extinct - or at least as limited as CLI's are today, with a small following; syndication and a standard way of reading content from a site without visiting the URI in a browser are going to be around for ages - I have no doubt about this.

The key is that someone needs to define a standard (e.g. - based on SOAP since we already happen to have it!) to query a 'feed' (now, we can term it a web service) for:

  • New entries without content since a previous point in time
  • New entries with content since a previous point in time
  • All entries ever
  • A specific web log entry
  • Comments for an entry
  • All comments since a point in time
  • All comments ever
  • An entry with comments at the same time
  • Indicating you linked to a post or comment
  • Posting a comment
  • And to 'subscribe' where you are e-mailed or IM'd (or using msn alerts, or Jabber alerts or whatever) when there is an update.

It is inevitable, RSS uses too much bandwidth and gives duplicated content only when you request it which means you need to keep checking back (even if you get a HTTP message to say it hasn't changed).

A web service could make available only the necessary information in a smart way, it could track and push information in the background to notify when your aggregator needs to look at your RSS/ATOM data for new entries, and it could allow a full integration of the site with the aggregator for comments.

Robert needs to look forward, not back...

Permalink  Digg This! 

http://me.sphere.pl/indexen.htm

MoonEdit is a multi user text editor, it lets two (or more) people simultaneously edit a single file, with multiple cursors, entry points and near real time collaboration.

I've been waiting for something like this for a while, after posting about it first, then a year or two later seeing SubEthaEdit for the Mac.  Hopefully someone (http://docsynch.sourceforge.net/) will come up with a plugin for Visual Studio.

(Links curtesy of ntk.net)

Permalink  Digg This! 

I've noticed some google oddness, a lot of sites have suddenly dropped quite low in rankings. My DeveloperFusion profile page is now higher than my CV and this site!

Robert Scoble is now the SECOND Robert. No other search engine places THAT much weight on the Scobleizer.

And when searching for my current employer, Capita, a partner of theirs comes up first!

I have been trusting gigablast and even the new MSN search more than google recently, and I can't quite be sure what google have done to skew their current index.

(At least, on the search engine front. I am very impressed with some of google's current projects.)

Permalink  1 Comments  Digg This! 

This is some code I wrote a little while back as an example OR mapper.  When inheriting from BaseDataDynamicEntity you can use the attribute [DataDynamic(“Name”)] to indicate the field name in the database and then use the class below to fetch data from the database or update it with any changes.

 

This is just an example though and doesn’t do the updating – but instead returns an array of string that can be used to look at what’s in the object now.  Return a set of SqlParameter’s and plug it into a stored procedure for a working example.

 

      public abstract class BaseDataDynamicEntity

      {

            /// <summary>

            /// An attribute to use to work out which properties have names that are in the database

            /// </summary>

            public class DataDynamic : Attribute

            {

                  public DataDynamic(string fieldName)

                  {

                        _fieldName = fieldName;

                  }

                  /// <summary>

                  /// The name of the field in the database

                  /// </summary>

                  public string FieldName

                  {

                        get

                        {

                              return _fieldName;

                        }

                  }

                  private string _fieldName = "";

            }

 

            /// <summary>

            /// Return a set of properties on this class as a demonstration of what is possible

            /// </summary>

            /// <returns></returns>

            public string[] ListDataProperties()

            {

                  Type t = this.GetType();

                  PropertyInfo[] p = t.GetProperties();

                  ArrayList properties = new ArrayList();

                  foreach (PropertyInfo pi in p)

                  {

                        if (pi.IsDefined(typeof(DataDynamic), true))

                        {

                              properties.Add(pi.Name+": "+pi.GetValue(this, null).ToString()); //could instead write these out to some parameters.

                        }

                  }

                  string[] values = new string[properties.Count];

                  properties.CopyTo(values);

                  return values;

            }

 

            /// <summary>

            /// Given an SqlDataReader from ExecuteReader, fetch a set of data and use it to fill the child objects properties

            /// </summary>

            /// <param name="dr"></param>

            public void SetDataProperties(SqlDataReader dr)

            {

                  while (dr.Read())

                  {

                        for (int i = 0; i<dr.FieldCount; i++)

                        {

                              setProperty(dr.GetName(i), dr.GetValue(i));

                        }

                  }

                  dr.Close();

            }

 

            private void setProperty(string name, object data)

            {

                  Type t = this.GetType();

                  PropertyInfo[] p = t.GetProperties();

                  foreach (PropertyInfo pi in p)

                  {

                        if (pi.IsDefined(typeof(DataDynamic), true)&&pi.CanWrite)

                        {

                              object[] fields = pi.GetCustomAttributes(typeof(DataDynamic), true);

                              foreach (DataDynamic d in fields)

                              {

                                    if (d.FieldName == name)

                                    {

                                          pi.SetValue(this, data, null);

                                    }

                              }

                             

                        }

                  }

            }

      }

 

And to use this, you do something like:

 

      public class NewsArticle: BaseDataDynamicEntity

      {

            private int _id = 5;

            /// <summary>

            /// The Id in the database

            /// </summary>

            [DataDynamic("id")]

            public int Id

            {

                  get

                  {

                        return _id;

                  }

                  set

                  {

                        _id = value;

                  }

            }

 

            private string _name = "Demo object";

            /// <summary>

            /// The name in the database

            /// </summary>

            [DataDynamic("subject")]

            public string Name

            {

                  get

                  {

                        return _name;

                  }

                  set

                  {

                        _name = value;

                  }

            }

      }

 

Which makes populating it as easy as:

 

SqlConnection sq = new SqlConnection("Data Source=(local);InitialCatalog=nullifydb;Integrated Security=SSPI;");

      sq.Open();

      SqlCommand sc = sq.CreateCommand();

      sc.CommandText = "SELECT TOP 1 * FROM newsarticle";

NewsArticle n = new NewsArticle();

      n.SetDataProperties(sc.ExecuteReader());

      sq.Close();

 

Obviously this is just an example, and you would want to use a DAL of some sort to do the data extraction, but integrating the DAL with the above technique should be fairly easy.

Permalink  Digg This! 

This is something I posted to a mailing list in response to a question, but since posting it I've also been asked some questions which it answers - so, here is a VERY quick rundown of DNS:

> I basically need:

>

> (a) An overview of all the bits in the process – so I know I haven’t

> missed something obvious!

DNS is a tiered, cached method of querying some form of data, not necessarily a name, not necessarily an IP, not necessarily a text record, location, what the router for a network is, what the Kerberos/LDAP servers for a domain are, etc.

It uses the format:

Name dot

To distinguish a part, therefore (taking my own domain as an example) it is not really:

Nullify.net

But instead:

Nullify.net.

However the last dot is commonly left off for convenience incorrectly.  One or more domains controlled as a single entity on a server is called a 'zone'.

The bits that make up DNS from a practical standpoint consist of:

- A resolver

- A cache

- An authoritative server

The resolver is what exists on every persons computer, and on the ISP's nameservers along with a cache.  The resolver requests from the default DNS servers for the current dns 'zone' any desired records - in the case of a site that is in the root zone, or wants to query the root set of DNS servers there are a set of root servers, located at root-servers.net.  These can be extracted using dig, and have always stayed at the same IP - although one of these servers got a new multicast IP a short while ago.  Once a result (or NXDOMAIN, indicating there isn't a result) is retrieved - it is stored in the cache along with it's time to live (ttl).

The cache is the reason a small number of servers can support the whole Internet (obviously there's a lot behind the scenes, but the point still holds).  The vast majority of traffic is handled by the caches at every stage.  There will commonly be as many as four caches between an authoritative nameserver and a client.  The TTL maintains how long these hold a record of those results, and this is why DNS changes take so long.  The exact same cache is also used on a secondary nameserver, but rather than the TTL for records, the TTL for the zone itself is used.

An authoritative nameserver is a server that knows something itself.  It responds with a particular record for a particular domain - assuming anyone asks it.  There are both master and slave servers - a master holds the file that actually stores the data, the slave stores it in memory and relies on the TTL for the zone itself to decide how long it is authoritative.  The slave uses the zones serial number to decide whether a change has been made, and refreshes every time the refresh property of the SOA record for a zone has been expired.

>

> (b) An idea of how to set up the Windows Server as the primary nameserver

> (not sure if this is a good idea)

For windows you just create a zone and create the desired records.  From then on it will answer as a nameserver and can be queried by a machine looking at it as the default nameserver, or using nslookup/dig with it set as the default.

You then need to point a server on the internet for the domain you are adding a record to (say, net. Or com.!) to think it is the nameserver for a domain, and you require either an out of zone A record for it, or a glue record for it so that the IP of your own server can be resolved in order to go query it for the details of your domain!

To simplify this, for my domain I have a primary nameserver xerxes.nullify.net. at 2002:4262:b24a::4262:b24a and 66.98.178.74

This means that in order for xerxes to host nullify.net, the nameservers for net. Need to also contain both AAAA and A records for it.  A records resolve to a host.  (AAAA is ipv6, which you don't need to worry about yet).  You should request these records are created in advance for ALL nameservers that do not already have glue records.  The contact for the netblock the servers are hosted in may be contacted by some TLD providers, but net. And com. Don't do this.

Once you have glue records that have propagated to all the servers for the zone they are in, you need to ask them to delegate a hostname for you - this is your domain and results in your domain being set to use your nameservers - this is an NS record, and there will be one for each nameserver that knows that domain.

>

> (c) An idea of how to configure secondary nameservers

Create a secondary zone, pointed to yourdomain.tld. - note the last dot.  Then you need to make sure you secondary server has a glue record in your TLD and an A record in your domain, add an NS record to YOUR copy of the domain pointing to the nameserver, and finally have it added as an additional NS record to the TLD's servers.

>

> (d) An overview of how MX records work

An MX record is a mail exchanger record, when you send mail to a host the following happens:

- An attempt to resolve it as an A record occurs.

- An attempt to resolve it as an MX record occurs.

   - Each returned MX record points to an A record that is resolved.

      - Mail delivery is attempted at the A records in order of weighting.

The MX records take priority, to allow an A record to exist for a host, but that host not to receive mail.

MX records have a weighting, the lowest takes mail first, then if that is down the next highest, and so on.  MX records with the same weighting are attempted randomly.

If all MX records bounce, the message is queued but eventually bounces with a failure.  If there are no MX records, and no A record, the mail bounces instantly.  If there is an A record, delivery is attempted and on failure, it bounces instantly.

>

> (d) An overview of Reverse DNS records (and whether I need these)

>

You do need these to deliver mail, you also need these if you wish to run a netblock, since it can be revoked by the issuer if you fail to do so.

As the owner for a netblock, you also have rights to a second domain - in in-addr.arpa.

This domain follows the reverse of an IP, so it can be followed down through the ownership chain, for example if RIPE issued you the address space 192.168.0.0/24 you would have the zone:

0.168.192.in-addr.arpa.

You could set up www here, but it wouldn't be much use.  More use would be to set up a PTR record for your SMTP server, which is at 192.168.0.8:

8.0.168.192 PTR mymail.mydomain.tld.

Note that when working with DNS you really do need the final dot, otherwise it'll exist in your current domain.  This is the common cause of lots of problems like cname's not resolving correctly and instead trying to go to mymail.mydomain.tld.mydomain.tld.

A cname is an alias.

------

Records you need to know about are:

SOA - the actual domain as it is stored on your system.

A - a hostname to IP

PTR - an ip to hostname

SVR - a common service (e.g. - ldap and Kerberos to log in to active directory in a domain)

MX - mail exchanger

CNAME - an alias, or common name

TEXT - information or other details

You can explore from the client side and test everything is working by using nslookup.

To use nslookup, open a command prompt, define what record type you want, and type the name of the record.

For example:

C:\>nslookup

Default Server:  vex.nullify.net

Address:  192.168.0.5

> set type=a

> xerxes

Server:  vex.nullify.net

Address:  192.168.0.5

Name:    xerxes.nullify.net

Address:  66.98.178.74

> set type=ns

> nullify.net.

Server:  vex.nullify.net

Address:  192.168.0.5

nullify.net     nameserver = ns1.twisted4life.com

nullify.net     nameserver = queeg.nullify.net

nullify.net     nameserver = xerxes.nullify.net

queeg.nullify.net       internet address = 66.45.233.165

xerxes.nullify.net      internet address = 66.98.178.74

xerxes.nullify.net      AAAA IPv6 address = 2002:4262:b24a::4262:b24a

> set type=mx

> nullify.net

Server:  vex.nullify.net

Address:  192.168.0.5

nullify.net     MX preference = 20, mail exchanger = xerxes.nullify.net

nullify.net     MX preference = 30, mail exchanger = queeg.nullify.net

nullify.net     MX preference = 10, mail exchanger = firewall.nullify.net

nullify.net     nameserver = ns1.twisted4life.com

nullify.net     nameserver = queeg.nullify.net

nullify.net     nameserver = xerxes.nullify.net

firewall.nullify.net    internet address = 81.109.199.173

xerxes.nullify.net      internet address = 66.98.178.74

xerxes.nullify.net      AAAA IPv6 address = 2002:4262:b24a::4262:b24a

queeg.nullify.net       internet address = 66.45.233.165

Feel free to use my domain to explore it if you want, it (should be) set up correctly, and everything is public info anyway.  http://www.demon.net/toolkit/internettools/ contains some useful tools to test your domain from outside your own network if you don't have access to an external machine.

Exploring from the client side gives a good idea what the server side setup will need to be.

Permalink  2 Comments  Digg This! 

I don't normally talk about things like the Tsunami, or September 11. I let others who are much better qualified or more descriptive cover them and try not to clutter the net with more useless content, however with the deathtoll officially passing 125,000 confirmed dead - I have just one thing to say:

125,000 people are dead. Please donate something to help.

Permalink  Digg This! 

I've had IPv6 in testing on xerxes.nullify.net (in Texas, US) for some time as xerxes.ipv6.nullify.net but have now finally enabled it on the main hostname since it has been stable and worked reliably. I've also got it working from home (Surrey, UK) and am working on queeg.nullify.net (now New Jersey, US).

If anyone has any problems viewing anything please let me know.

Permalink  Digg This! 

Merry christmas and a happy new year everyone! Permalink  Digg This! 

This is possibly the best post I've seen on debugging .NET memory leaks (when the garbage collector isn't quite working as intended, or your program is keeping something alive longer than it should). Permalink  Digg This! 

You can do this using the OPENROWSET function in MS SQL Server, so:

SELECT * FROM OPENROWSET('MSDASQL', 'ConnectionString', 'SELECT * FROM mytable')

Which is great in a view for data consolidation - but even better is to remove the need for a connection string by connecting just once!

You can do this using sp_addlinkedserver and sp_addlinkedsrvlogin.  Requirements will change depending on what you want to connect to - if you need help with a particular one feel free to e-mail me.

You can then simply use the new 'server' you just set up as so:

SELECT * FROM OPENQUERY(servername, 'SELECT * FROM mytable')

Permalink  Digg This! 

Have you ever wondered how to check if a number is an integer in C#?  Wondered why Convert.ToInt32() surrounded by a try catch is so slow?  Well, this article is for you then.

As everyone should know, catching an exception is an extremely time consuming task, but there's no obvious way to check if a string is actually an integer.  And before the visual basic people all say about Microsoft.VisualBasic.Information.IsNumeric this is actually simply a try...catch around a Convert.ToInt32 call.

Okay, so what is the performance drop incurred by using a try...catch and why should you worry?

Well, it's slow - 1000 iterations of try...catch around a Convert.ToInt32() comes to 2515.625 ms.  Programmatically that's a HORRID use of CPU time.

Some people (particularly on blogs.msdn.com's comments) have suggested that this is simply something you must incur, however due to NumberStyles (in System.Globalization) containing an entry for Integer it needn't be. We can use Double.TryParse without the risk that the double we'll be getting back will contain anything but a valid integer.

This is amazingly faster - 100,000 (100 times more than with a try...catch!) worst case scenarios come to a grand total of 46.875 ms of cpu time used up.  A much better overhead.

It returns True if the string was an integer and false if not - altering the value of the double passed into it as an out parameter to be the correct amount if it succeeds.  Note that the if statements are just there to confuse the compiler so it doesn't optimise out the whole contents of each for loop.

Here's an example application (sorry, no walkthrough as I have too little time, but this should be simple enough!):

using System;

using System.Globalization;

namespace BlogExamples.IntegerValidation

{

   class IntegerValidator

   {

      static void Main(string[] args)

      {

      Console.WriteLine("Comparison of speed of try..catch checking of integers");

      Console.WriteLine("1000 iterations of try...catch Convert.ToInt32()");

      DateTime before = DateTime.Now;

      for (int i=0; i<1000; i++)

      {

         try

         {

            int t = Convert.ToInt32("testing");

            if (t>1000)

            {

               Console.WriteLine("Optimisation cheating");

            }

         }

         catch

         {

         }

      }

      TimeSpan duration = DateTime.Now - before;

      Console.WriteLine(duration.TotalMilliseconds+" ms total");

      CultureInfo MyCultureInfo = new CultureInfo("en-GB");

      Console.WriteLine("100000 iterations of Double.TryParse() (to be fair!)");

      DateTime before2 = DateTime.Now;

      for (int i=0; i<100000; i++)

      {

            double d = 0;

            Double.TryParse("testing", System.Globalization.NumberStyles.Integer, MyCultureInfo, out d);

            if (d>1000)

            {

            Console.WriteLine("Optimisation cheating");

            }

         }

         TimeSpan duration2 = DateTime.Now - before2;

         Console.WriteLine(duration2.TotalMilliseconds+" ms total");

         Console.ReadLine();

      }

   }

}

 

Which when run on my machine reports the following:

Comparison of speed of try..catch checking of integers
1000 iterations of try...catch Convert.ToInt32()
2515.625 ms total
100000 iterations of Double.TryParse() (to be fair!)
46.875 ms total

Permalink  1 Comments  Digg This! 

Yes, I know I haven't posted anything in over a month - I don't really have anything useful or relevant to post now either.

This is the unfortunate result of actually doing work rather than blogging, which has come about due to full time employment once again!

I'll possibly post something later on to do with C# and finding out if a string is an integer without using try...catch (it's not as simple as it sounds, no, that VB isnumeric thingy uses a try...catch!).

Permalink  Digg This! 

Then maybe you have something in your most recently used list that's needing a network lookup.  UNC pathnames were causing the problem for me.

You can clear your MRU lists by exiting visual studio, then opening registry editor and deleting the contents of the keys FileMRUList and ProjectMRUList in HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\7.1\

Permalink  Digg This! 

If your web service (or one you're consuming) is playing up (405 Gateway timeouts, other odd behaviour) when being debugged, maybe it's the VsDebuggerCausalityData header - it's a huge string seemingly mostly filled with AAAA!

You can disable this from being sent by adding the following in your App.Config file:

<configuration>
  <system.diagnostics>
    <switches>
      <add name="Remote.Disable" value="1">
    </switches>
  </system.diagnostics>
</configuration>

This will disable the debugger from getting involved with the badly configured server (the VS header is within the HTTP specs upper limit on size).

This has been annoying me for at least the past day trying to talk to a partner of works web service!

Permalink  Digg This! 

So for my 21st birthday I figured I'd treat myself to a new computer (which is missing a graphics card for the moment, so I have to use it over terminal services :-/) - it's spectacular...

Whilst most machines get ~1.7GB/s bandwidth to the system memory, and the latest Intel Xeon's get 2.6GB/s - with a NUMA enabled copy of Windows XP (ie - it has service pack 2) I get a nice 10GB/s.  And no, I'm not mixing up Gb and GB!  I think this is the fastest result for the SiSoft Sandra benchmark I've seen out there without overclocking...  Actually, even with overclocking I think it's the fastest!

I think that's the fastest ever memory benchmark result in sisoft sandra!

If you ever want performance, dual Opteron (AMD64 server CPU's) are definetely it when mixed with a good motherboard like the DK8N from Iwill!  I only had some minor problems with the Western Digital hard disks and trying to use both raid controllers at once.

(Thanks has to go out to http://www.rainbow-it.co.uk/ who managed to source all the components and have provided simply the BEST customer service - even when I wanted particularly special components - they beat Dabs in shipping time by months...)

Permalink  Digg This! 

Whilst I wait for someone to get back to me about something, I figured I'd quickly post about something that initially confused me about server controls on ASP.NET so that there's at least SOME new content!

ASP.NET server controls are simple components that run on the server and allow you to refactor a quantity of your sites code (such as a menubar) into a seperate module that is both reusable like a user control, and dynamic.  A server control can run code and also allows you to drag and drop it onto your pages.  A server control also has the ability to render itself at design time, so no more silly grey boxes - but rather a WYSIWYG situation (once it's built)!

So, once you've created a server control using VS.NET 2003 (or your choice of IDE!) you can easily add to the render override with something simple:

output.Write("<div class=\"mycssclass\">Hi there!</div>");

Any HTML will work, and all is fine if you just want to insert static HTML.

But what if you want to use the server control as if it were an ASP.NET application - or winforms control?

You know, so you can click a link or a button and it'll do 'something' and remember that state.

To do this you will need an event handler attached to a control.  So you create an instance of a control and delegate its event to the handler!  In winforms this is extremely easy, but in ASP.NET making it work right requires you set it all up early enough in your code!  This is the gotcha that had me stuck for a couple of hours when I first learnt it.  I did it in the render override.

Instead, change your render override to read as follows:

protected override void Render(HtmlTextWriter output)
{
   output.Write("<div class=\"myCSSClass\">");
   this
.RenderContents(output);
   output.Write("</div>");
}

This will allow you to render the controls at the right time and yet create them early enough - which will let ASP.NET create the eventhandler and wire it up for you.

Now, to create the controls that will be rendered you need to create a new override:

public LinkButton l;

protected override void OnInit(EventArgs e)
{
   base
.OnInit (e);
   l = new
LinkButton();
   l.Text = "Click me!  I'm a link that triggers an event on the server!";
   l.Enabled = true
;
   EventArgs args = new
EventArgs();
   l.Click += new
EventHandler(this.l_Click);
   this
.Controls.Add(l);
   Literal br = new
Literal();
   br.Text = "<br />";
   this
.Controls.Add(br);
}

This will create two controls - which will be rendered in the render method.  You can now create the event handler and any code you place in it will work!  If you had created the controls in the render method itself you would have found that although they posted back to the server, they didn't call the event handler.

(Note: += means "add one of" and "base" is the WebControl you are inheriting from when creating a server control.)

A quick example event handler for those who've never done one before:

void l_Click(object sender, EventArgs e)
{
   l.Text = "Thanks!";
}

Permalink  Digg This! 

If wbmsn's security is in doubt, how about using a web based msn from the horses mouth?

http://webmessenger.msn.com/

Microsoft's own web based msn messenger should be more reliable.

(Excuse for lack of posting: Tackling an interop issue)

Permalink  Digg This! 

I've been asked by some friends to upload some photos of Ballard - a building at Collingwood College where I used to work (yes, the evil workplace I have thankfully left was a College!).

They are really poor quality as they are just what I managed to take using my camera phone, and were (all but one) already photos of the building from the past.

If anyone reading this wants to contribute some other photos feel free to e-mail me with them and I'll upload them.  I also have a small forum for discussion by Collingwood College alumni - incedentally they have pictures of the new building that will be replacing it at the official site.

Permalink  Digg This! 

You can now finally order a copy of Beta 1 of Visual Studio 2005 in Europe - it's only £3 and is available from here. Permalink  Digg This! 

Sorry for the lack of entries, I've been pre-occupied with non-programming work things.  I will be building an intranet shortly though (whether the person that mentioned it actually wants it or not since it'll keep my technical skills active - will probably actually build 'bits' similar to sharepoint if I don't choose to go that route), so should have something interesting to post in the next few weeks.

The big question is, sharepoint or code?

Permalink  Digg This! 

This is just google juice as an experiment - hopefully after google gets this searching for a cryptic number will lead here. Permalink  Digg This!