NullifyNetwork

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

Archive - Historical Articles

You are viewing records from 08/10/2002 09:51:02 to 11/19/2022 13:24:57. I'll be adding support for selecting a date range in future.

I'll preface this by saying this post is just for me, it's for Alpine Linux and specific to one use case where I need a static file webserver but want to reconfigure the domains occasionally.

You can set up self-reloading config if you have a script that watches for file changes to the conf files. Just apk add inotify-tools and then create a script like this in /etc/inotify/nginx-reload.sh and make it chmod u+x nginx-reload.sh:-

#!/bin/bash

set -e

while true; do
    while inotifywait /var/www -e modify --include '.*\.conf'; do
        nginx -s reload
    done
done

You then need to set this up to run on startup using openrc. Put the following in /etc/init.d/nginxreload and again, chmod it u+x:-

#!/sbin/openrc-run

name="Nginx Reloader"
command="/etc/inotify/nginx-reload.sh"
command_args=""
command_user="root"
command_background=true
pidfile="/etc/inotify/nginx-reload.pid"

depend() {
        need net localmount
}

Now run:-

rc-update add nginxreload default
service nginxreload start

And any edits to the conf files specified result in nginx reloading its configuration.

Permalink 

Are you having issues with loading the designer and getting InvalidOperationException and a NamedPipeTimeout trying to connect?

Open a PowerShell in Admin mode and run this to set an exclusion in Defender for the design server:-

Add-MpPreference -ExclusionProcess 'DesignToolsServer.exe'
Permalink 

I just upgraded this site to .NET 6, I thought it was over due with the release of Visual Studio 2022 having happened a while ago now.  Great to see it was a quick and easy upgrade, the performance has improved even more on the new version too.  I just updated the build version, compiled, then fixed some warnings caused by RenderPartial, I'm pleased to see they sorted that deadlock!

I also took the time to switch to Alpine Linux as the docker container hosting it as it's my preferred Linux distribution, however found I needed to solve the ICU issue (the MS SQL Server client expecting to have localisation libraries) by getting the packages during the container build:-

FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
RUN apk add --no-cache icu-libs
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

Oddly when building the ARM64 containers for the dev version of the site that runs on a pair of Raspberry PI's I also encountered a problem with the cross-compilation of docker containers, but that was easily fixed by running the below on the host building the containers as part of the pipeline:-

docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
This allowed the ARM components inside the 6.0-alpine-arm64v8 container to work so the apk command didn't error.
 
Other than the above it was seamless, and it's nice to switch to an even smaller container size.
 
Permalink 

Happy new year to anyone still reading this, it has been more than 21 years I've written on this blog, varying from extensive articles to years where I've not felt the need to say anything at all (including last year!).  I just this week completely rebuilt this blog to run on .NET 5 and be hosted under Docker, no changes to paths, content, etc. but it's good to keep things current and modern and each rewrite I distill it further and make it simpler.

With the move to a more digital way of running businesses and all the working from home it's an interesting time for a technologist.  A decade ago this would have been possible but unlikely to be something we'd end up doing for real or such a prolonged period, now it suddenly normal.  It seemed appropriate to confirm all is well for anyone that still does look.

Life/work update: I've been managing a team of developers for the last few years now, I am still staying as technical as I can and run regular training sessions for my team.  I'm doing fairly well.

Permalink 

For the last year I went permanent and I've been a Delivery Lead managing about 30 people and a swathe of business applications.  It's good fun, and I've still got the ability to be really deeply technical or help my team members with actual programming at times - I'm glad to have a great team who can do most things on their own.

I've just updated this site with the latest version of the code that runs it (I did a complete re-write, though the layout is exactly the same for the moment).  If anyone spots anything broken please e-mail me.

I'm still around and doing fine.  My company (Fizl Ltd) is still in existance and sitting there ready for me to use for things.

Permalink 

I can't believe it's still a thing; I've encountered this error repeatedly since the release of Windows Identity Foundation and have just encountered it again with Azure AD and MVC.  You just have to add this somewhere before using an AntiForgeryToken in MVC:-

AntiForgeryConfig.UniqueClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";

To get past the error:-

A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity.

Permalink 

Well I had a very bad year with my father passing away and various business related things like the umbrella company I was using going insolvent and taking an amount of my money with it (and I expect a few more road bumps over the next few months as things settle down) I'm hoping for a better year going forward.  Many of the things that happened have just pushed me to adapt.

I know I haven't posted blog articles properly in years as I've been constantly trying to avoid posting work related items (as I've been contracting for others), I've been doing some interesting research myself recently and hope to talk more about that instead soon though there are a few hurdles to overcome before there's anything useful produced from it.  I do need to get back in the habbit of doing my own things though, I have buried my head into my customers work like it was sand without thinking of my own reality and that needs to change.

I have lots of ideas for Fizl Ltd to do in the future too so I wish any readers that remain well and hope you will remain patient - pretty easy with an RSS reader.

First things first though, this site needs an upgrade and some new content...

Permalink 

I'm still alive and contracting, I've been radio silent for some time because I don't really want to talk about anything related to my employer on here and hadn't done much personal software development for a while.  About six months ago I started to do some again so hopefully will resume posting.

I've just finished a full set of server upgrades for my off-site physical servers (nice new hardware, and to Windows Server 2012r2), if you were hosted on them and haven't contributed for more than ten years or so your accounts may not work when you try to get in as I didn't migrate those people.  I may still have copies of your content if you ask before the old servers are turned off.

I still have spare capacity for friends to use on the servers (much more than previously actually).  For those of you using the teamspeak server, it's still on on the same address beta.nullify.net but the IP will have changed.  Let me know if there are any issues.

Permalink 

Well, I managed to go almost a year without posting which is pretty bad.  I'm still contracting and haven't had anything interesting to post technically but the new items at the Build conference looked good.

I've been pre-occupied for ages now with getting my own house sorted but it's finally looking like it's almost there.

I hope to get a chance to post more useful content in the future but I really need to do something about this website being a bit out of date!

Permalink 

Not sure anyone is still reading this, but I've not really been blogging while contracting for semi-obvious reasons; I'll be leaving this post here at the top just to confirm I am still alive and will post some interesting stuff about both Workflow Foundation and 3D engine development in the future.

All but a few of my servers are now upgraded to Server 2012 or FreeBSD and I'm ditching the remaining Linux ones, overall I've been finding Server 2012 a solid platform and while the initial version had power management bugs and various other quirks updates have really improved it.  It has made things like Active Directory extremely lightweight too.

I've also been making a 3d engine in my spare time (though only putting in a concerted effort over holidays!) so should have plenty to say about geometry/meshes, collision detection, mouse movement, shaders and physics.  I also toyed with flow-field pathfinding and am amazed how effective it can be with so little code so hopefully that'll be the subject of a few blog posts on its own.

TFS 2012 has been an awesome platform for doing builds and keeping track of what I was working on given quite lengthy gaps too.

Permalink 

You can download the theme editor from here.  I have started to get used to the colours to be honest, though they are still substantially worse than the VS2010 scheme.  Commence the addition of colour!

Permalink 

I'm still contracting so haven't posted much at all but I am still alive!  This is just a quick note because it's nice to see people like Microsoft have taken world IPv6 launch day seriously and enabled their main websites permanently with AAAA records.

Unfortunatey major UK ISP's haven't yet enabled it for their customers (JANET excluded), but the smaller ones (and international carriers) are shining through and able to provide service.

I took the IPv6 Enabled logo off my site; however I'm proud to say it has been enabled here for the last eight years or so.

Permalink 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

(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 

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 

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

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

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

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

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

Permalink 

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

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

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

Permalink 

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

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

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

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

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

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

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

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

default local
prompt 1

label local
 MENU label ^Local Boot
 LOCALBOOT 0
 timeout 10

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

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

Permalink 

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

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

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

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

Permalink  2 Comments 

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

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

Permalink 

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

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

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

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

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

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

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

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

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

    #region Real code

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

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

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

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

            private DNSServer _server = null;

            private string _name = "";

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

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

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

            private ManagementBaseObject _wmiObject = null;

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

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

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

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

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

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

            private ManagementObject _wmiObject = null;

            private string _target = "";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            private DNSRecordType _recordType = null;

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

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

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

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

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

            private string _recordMode = "IN";

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

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

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

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

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

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

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

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

            #endregion
        }
        
#endregion

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

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

        private string _server = "";

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

        private ManagementScope _scope = null;

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

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

            return domains.ToArray();
        }

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

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

            return records.ToArray();
        }

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

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

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

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

            return records.ToArray();
        }

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

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

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

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

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

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

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

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

Permalink 

Well the subject says it all!

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

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

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

Permalink 

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

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

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

Permalink 

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

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

Permalink 

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

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

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

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

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

The command I'm using is:

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

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

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

Permalink 

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

Windows Registry Editor Version 5.00

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

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

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

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

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

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

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

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

Permalink 

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

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

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

private void Form1_Load(object sender, EventArgs e)

{

       InitSpeechRecognition();

}

 

SpeechRecognitionEngine _recognitionEngine;

 

public void InitSpeechRecognition()

{

       voidDelegate d = new voidDelegate(initSpeechRecognition);

       d.BeginInvoke(null, null);

}

 

private delegate void voidDelegate();

 

private void initSpeechRecognition()

{

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

       _recognitionEngine.SetInputToDefaultAudioDevice();

       DictationGrammar d = new DictationGrammar();

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

       _recognitionEngine.UnloadAllGrammars();

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

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

       _recognitionEngine.LoadGrammar(d);

       _recognitionEngine.RecognizeAsync();

}

 

void r_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)

{

       BeginRecognition();

}

 

delegate void SpeechHypothesizedPassThroughDelegate(object sender, SpeechHypothesizedEventArgs e);

 

void r_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)

{

       if (this.InvokeRequired)

       {

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

       }

       else

       {

              textBox2.Text = e.Result.Text;

       }

}

 

delegate void SpeechPassThroughDelegate(object sender, SpeechRecognizedEventArgs e);

 

void d_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)

{

       if (this.InvokeRequired)

       {

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

       }

       else

       {

              //display the recognised text, or process it here

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

       }

}

 

private void BeginRecognition()

{

       new voidDelegate(delegate()

       {

              _recognitionEngine.RecognizeAsync();

       }).BeginInvoke(null, null);

}

 

private void StopRecognition()

{

       new voidDelegate(delegate()

       {

              _recognitionEngine.RecognizeAsyncStop();

       }).BeginInvoke(null, null);

}

Permalink 

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

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

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

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

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

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

Permalink 

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

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

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

Permalink 

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

Excellent features I'm looking forward to are:

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

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

Permalink  3 Comments 

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

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

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

Permalink 

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

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

Permalink 

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

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

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

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

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

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

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

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

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

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

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

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

Windows Registry Editor Version 5.00

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

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

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

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

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

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

Permalink 

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 

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 

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

Unsurprisingly MSDN is struggling to work :)

Permalink 

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 

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 

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 

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 

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 


(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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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

Permalink 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

[ 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 

[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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

Merry christmas and a happy new year everyone! Permalink 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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 

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

Dina (no url/blog yet!) mentioned wbMSN to me.  I'm not sure of the security of your password if you use it, however the concept is great - it allows you to use MSN without actually being logged into the smart client so you can chat if MSN Messenger itself is blocked on your corporate network (although then it's still inadvisable ;)).

Permalink 

So, I manage to bump into an article by Sam Gentile who is complaining about SQL Express.

He complains there's no manual, docs, sample database or user interface.

I do believe there's a lack of documentation - it could do with coverage of the new features, however MSDE had no user interface, sample database or documentation.  It's a royalty free, application deployable database - not SQL Server 2005 Enterprise Edition.

The product is a good year away at least!  And given what has already been written in various weblog entries and comments you can get the key parts to work fine.  Creating/importing a sample database is also simple enough as to be a good entry level test of the user to see if they actually should be playing with the product.

Beta means Beta, not "finished product to try out" - if will always take some effort to get the most out of a beta.

Permalink 

As of 30 June 2004 Office 2000 is no longer supported - well, there's always Microsoft extended support, but that's it - no more bugfixes or patches to be released - and even extended support only lasts till June 30 2009, when even the download site will vanish.

I'm using Office 2003 here, and very happy - particularly with Outlook and OneNote, however almost every tool has improved considerably since the 2000 version.  Hopefully the next version of Office will include support for languages other than VBA so people who program in languages like C# or C++ won't have to suffer VB!

But the 2000 version was the first version that was actually reliable and didn't crash all the time on people.  And for that there should be a nod of the head.  My memories of 97 are that of a downright bad product (although it wasn't lacking features).

Permalink 

The new version of MS SQL Server has a cut down version, like SQL Server 2000 had (MSDE) called SQL Server Express.

The restrictions on the new version are much more sensible for deployments, as rather than being limited to a predefined amount of work they're limited to storage space and hopefully clients are not likely to set up >4GB datasets of work data without being able to afford to buy a full copy of SQL Server.

The big plus though, is that you can have a .NET assembly work INSIDE your database as if it were a stored procedure, this offers major advantages - ie - a trigger can now send an MSN message or post to a web service.  And that's not touching the performance differences of code in .NET.

This is further differentiating MySQL and MS SQL, which is how I like things to be since both are excellent databases.

MySQL is the definitive victor in the price/ease of use arena for data warehousing and small applications whilst MS SQL server will be the victor in the performance/features arena for smart applications or where the data will be accessed from different systems (where stored procedures/.NET assemblies enable code reuse and advanced server side logic).

Permalink 

This one had me and google stumped for a few minutes yesterday till I remembered that dBASE isn't actually a relational database but is a flat file one.

Each file is one table, so use the file management classes in .NET to list the tables your connection string has opened!

Unfortunately there's also no way to list tables in ODBC that I can see in .NET for the moment, so if you want to use a different database format you would be better off using OLE or ADO to connect if you can.

Update: Whoops, here's the code I forgot to put in:

private OdbcConnection connection;

private string dbpath;

public dBaseEasyDBConn(string path)

{

dbpath = path;

Console.Write("Opening dBASE Files...");

connection = new OdbcConnection("Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq="+path);

connection.Open();

Console.WriteLine("Done");

}

public string[] GetTables()

{

int position = 0;

string[] templist = Directory.GetFiles(dbpath, "*.dbf");

string[] filelist = new string[templist.GetLength(0)];

foreach (string s in templist)

{

filelist[position] = s.Replace(dbpath, "").Replace(".DBF", "").Replace("\\", "");

position++;

}

return filelist;

}

Permalink  1 Comments 

I'm currently sitting here considering what 'acceptable performance' is in a variety of situations, ranging from databases to user interfaces, or back end control systems.

The answer in most cases is:

It changes depending on your situation.

Even what is acceptable in a real-time environment varies dramatically, for example software designed to change the course of a cruise missile would need to respond quick enough to keep up with the rate of change of terrain, whereas something monitoring the change of temperature in a green house would likely not need to process the data faster than once every ten seconds.

A database must be atomic, as soon as you commit a transaction the data MUST exist in the database, but with concurrent connections and multiple physical CPU's this is clearly impossible without the database working sequentially - so acceptable performance in this case is "fast enough that nobody notices, but not fast enough to slow down total performance".

However, there is one exception to it being variable depending on the task:  When the user is involved.

What is acceptable performance for a user interface?

Back in the days pre-DOS and PC era people likely waited the same amount of time for applications as they do today only with fewer features.  (Obviously there were some exceptions, I remember waiting 5 minutes for a word processor to load, but things evened out pretty quickly since nobody WANTED to wait that long).

Firstly, there are two things to monitor to decide on performance.  How fast the application is available to be told what to do and how much you can tell it to do without actually needing to wait.  There are simple answers to both.

The second one is commonly ignored by a lot of bespoke software developers, and really big players who shall remain nameless - in the education sector the key piece of software in UK schools had serious problems with concurrency, and even after a full rewrite still has problems with concurrency of use of a single application - you tell it what to do and go have a cup of tea/coffee - it might be done when you get back.

This is obviously unacceptable, and companies like Microsoft who can afford to do user studies have clearly looked at the problem - for example, how long do you wait whilst you're saving in Word?  How about printing?

Correct - you don't, even if it takes a while a seperate thread is doing the actual work so there is zero delay in the user interface - you can even be typing away as it saves or prints.  They don't do it everywhere, but they do do it whereever the user would have to wait a varying amount depending on the data being altered.

This ensures that Word acts speedy even with a hundred megabyte document.

The question is, would anyone be willing to wait at that point to save the development work?

Whilst the answer is 'probably not' there would be a point where people wouldn't mind word freezing - would you mind a 1/2 a second on a save?  How about 2 seconds?

This brings me onto the issue of actual delay before the application is usable again.  What is acceptable here?

The first delay is loading the application.  This is dependant on what the user wants to do, since we don't know in advance what they want to do our user interface must APPEAR to load almost instantly so that we can shave an additional second or so of time off our REAL loading time whilst the user decides what to click or press.

The second delay is the activity of doing something with the application, and can consist of one of two things from a user perspective:

  • Critical continuity activities
  • Non-critical activities (note - non-critical to UI flow, not anything else)

In the case of critical activies that the UI is dependant on, obviously the user must wait - unfortunate as it is, they will appreciate that it must happen.  They might not be happy, but it IS going to take time to load the document they want to look at (even if just the first page is loaded for display purposes before the rest of the data is loaded).  In these cases you can try as hard as possible, but you will always cause the user to wait - sometimes a nasty amount of time.

The non-critical activies are ones that do not have to occur to leave the user interface operable.  Saving is a good example, so is printing and seach and replace - although this one is commonly not implemented properly.  These activities should be implemented as seperate threads, with the user able to continue with their task, or do an additional slow task at the same time to avoid any actual period where the user is twiddling their thumbs.

So, in essence the only real answer to both key areas of UI performance is THE USER WANTS IT NOW unless it HAS to take longer, and then they should be allowed to do something else at the same time!

I wish developers would take the perspective of the actual end user when writing the application.  We would see a lot more multi-threaded, multiple document interface applications I'm sure.

Congratulations to Microsoft for getting it right.

Permalink 

A good friend of mine and his father are doing the London Bikeathon - a 26 mile ride for Leukaemia Research.

If anyone would like to sponsor them, even a tiny token amount please donate/pledge something.

Permalink 

It seems as though Microsoft have a little competition going - and it revolves around the Visual Studio 2005 Express line of products.  Note that this includes the new version of SQL Server!!

If you're like me and have been eager to get your hands on a copy of Visual Studio 2005 - this is a good way to check out some of the new features - even if most are missing from these small applications.

This is exactly the right idea and will hopefully encourage more people to use .NET!

Update: Who cares about the little bits (although they're available and you can actually download them, unlike whidbey for the moment).  The almighty whidbey is ALSO in beta!

Permalink 

Matt Warren over at The Wayward WebLog is seemingly in a war of... posting with those who are only posting work related stuff.

As a sign of support I'm posting a link to his blog, not that my meagre quantity of visitors will make much of a difference but you can always hope ;)

That and I'm not going to spam for his blog, it's not THAT good.  Well, not yet.

So please, visit his totally irrelevant blog and read some of the good stories, gibberish, filler he's written.

Permalink 

This is a nice neat way of resizing an image, I've simplified it and de-refactored (?) it for simplicity.

Firstly, you ask for your image from the database:

SqlCommand cmd = new SqlCommand("SELECT image FROM images WHERE id=@id", connection);
cmd.Parameters.Add("@id", Request.QueryString["id"]);
SqlDataReader dr = cmd.ExecuteReader();

Allocate an array of bytes to store it in temporarily:

byte[] image = null;
while
(dr.Read())
{
   image = (byte
[])dr.GetValue(0);
}
dr.Close();

Now you have an array of bytes that contains your image, you can freely load it into a bitmap from the array:

Bitmap b = (Bitmap)Bitmap.FromStream(new MemoryStream(image));

And you can resize that bitmap easily using the overloaded bitmap constructor:

Bitmap output = new Bitmap(b, new Size(320, 240);

One resized bitmap that you can now save or send anywhere - including Response.OutputStream!

Permalink 

I've added CDATA's to my rss feed to prevent the html from FreeTextBox 2.0 escaping and making my feed invalid.

I've also added photo album support to SiteBuilder, although I'm not sure what I'm going to do with it until I add security rights.

Update: I put a few of my better photos in it as a test.

Permalink 

What has happened to me??  This used to be a staunchly anti Microsoft blog.  Well, when it was tech oriented.

I think the stuff over at http://blogs.msdn.com is infectious, I've posted more programming and techie articles since reading that site.  And it's all been favouring Microsoft...

Hum...

Uh oh, I might've been evangelised without realising it by Robert Scoble!!

Permalink 

I just found an article at http://blogs.msdn.com/mszCool/archive/2004/06/14/155420.aspx that goes really well with my old article about using reflection to build plugins.

Basically the article covers how to change the permissions of the appdomain to allow only permitted actions - a great thing to do if you want your users to be able to build the plugins!

(Update five years later in 2009: Also I forgot all about this article and did my own example here http://www.nullify.net/ViewArticle.aspx?article=315 )

Permalink 

All my code related articles, answers to questions, random bits and pieces that are of use are all listed here if you wish to ever revue them.  I'll add an entry to the menu as soon as I get a chance. Permalink 

Kudos to the guys (and gals) over at MS for making a serious contender in the server OS market - my Windows 2003 Server has been up for four months now after its teething troubles where it crashed once.

The question is, will it beat the 207 days uptime my Linux servers were on before they got replaced by newer hardware?

I now have Linux and Windows running side by side in two datacentres - both have only been down (the machines rather than than the connections) for security updates* in those four months - which I don't count as downtime since I choose when to reboot.

* - I do keep track of these:  The Debian based Linux server has been down three times for kernel updates and the Windows 2003 server has been down once for an update but I didn't note down what it was.

Permalink 

Another easy question!

Firstly, what type of screen size are you looking for?

There are two types you can look at - the current working area, basically the area a maximised application will fill if it is polite, and the physical screen which expands from the top row of pixels to the bottom of the start bar on a standard setup.

Thankfully the Screen class offers up both of these as properties, so you can easily:

int height = Screen.PrimaryScreen.Bounds.Height;

int width = Screen.PrimaryScreen.Bounds.Width;

To get the exact, complete screen size (it may be better to handle this as a Drawing.Rectactle as that is how Bounds is typed, however for ease of explanation this is how I will do it here).

And, just as easily you can do:

int height = Screen.PrimaryScreen.WorkingArea.Height;

int width = Screen.PrimaryScreen.WorkingArea.Width;

To get the total usable space for a friendly application.

Please don't use this to work out how big you need to make your always on top application so you can obscure the start menu - that is extremely annoying ;)

Permalink 

This is a quick and easy one for anyone else trying to send mail using ASP.NET.

First, add the using statement for the appropriate namespace:

using System.Web.Mail;

Then, create a MailMessage object:

MailMessage m = new MailMessage();
m.From = "
simon@nullifynetwork.com";
m.Subject = "A demo message";
m.Body = "This is the body\n\n-Simon";
m.To = "
you@yourplace.com";

And then, finally - send it using the static method off SmtpMail:

SmtpMail.Send(m);

Another example of why .NET rocks, what used to be an annoyingly complex task is now suddenly relegated to being easy as anything...

(This article is to make up for this monster article which discusses opening file handles on physical devices...)

Permalink 

Yes, the wisptis.exe file is legitimate - it is part of the Windows Journal Viewer and you probably installed it through Windows Update.

It's related to the tablet PC architecture.

Permalink 

So Robert made another "RSS is the ultimate solution" post and almost everyone jumped on it like rabid dogs.  Me included...

I have taken particular offense (not really the right word, as I greatly respect much of what Robert brings to the surface, and his opinions are usually fairly good - a healthy discussion is also nothing to get angry about!) at a reply to my comment there:

"It's also a way of getting the content itself delivered and stored on my computer."

This is a fairly annoying and completely false assumption regarding RSS.

The concept of the content being delivered to you is an excellent one, however this is NOT what RSS does.  RSS is a simple XML file (or webservice) sitting on the web waiting to be collected by your computer.

It does not tell you when it is updated.  It doesn't send itself down the line to you.  It isn't even distributed as far as your ISP's servers like E-Mail/Usenet is!

RSS is being used to solve a problem because it wasn't designed from the ground up as a syndication format - it was designed as a machine readable version of a site, hosted in the same way as the site itself.

A true solution to the problem would be to send the appropriate part of the RSS file down the line via some active means - even if it's just as far as the ISP's servers, this is a much more logical solution.

It has a plethora of benefits just a couple of which are instant or near instant updating of your readers with what they're interested in and far less bandwidth usage in all scenarios at all ends.

The technology already exists with MSN Alerts, MSN/AIM/Yahoo/ICQ/Jabber and dare I say it, E-Mail (shame this has been spoilt by the spammers and is useless for its purpose).  Why can't we use one of these instead of making a square solution fit a round problem?

RSS frustrates me for what it's being turned into, and for how that is being done.

Permalink  2 Comments 

Want to access a physical device (COM port, tape drive, LPT port, anything...) using C# .net?

You can't.  Not natively anyway:

FileStream fs = File.Open("\\\\.\\COM1", FileMode.Open, FileAccess.ReadWrite, FileShare.None);

Happily errors out saying you can't use the \\.\ format with a FileStream (\\.\ becomes \\\\.\\ when you escape the backspaces with more backspaces).

FileStream does however allow opening a handle, so all is not lost, you can simply use the format:

FileStream fs = new FileStream(handle, FileAccess.ReadWrite);

After opening the handle using a CreateFile API call.  To use this API call you need to platform invoke it, which is easily done once you look at the docs for it:

[DllImport("Kernel32.dll")]
static
extern IntPtr CreateFile(string filename, [MarshalAs(UnmanagedType.U4)]FileAccess fileaccess, [MarshalAs(UnmanagedType.U4)]FileShare fileshare, int securityattributes, [MarshalAs(UnmanagedType.U4)]FileMode creationdisposition, int flags, IntPtr template);

(http://www.pinvoke.net/ is a great place to cheat)

You can then get handle (note that rather than defining the actual handle variable I'm going to put the CreateFile call inside the FileStream constructor - you could instead do InPtr handle = CreateFile() etc) by using CreateFile:

FileStream fs = new FileStream(CreateFile("\\\\.\\COM1", FileAccess.ReadWrite, FileShare.ReadWrite, 0, FileMode.Create, 0, IntPtr.Zero), FileAccess.ReadWrite);

First timers should remember to update their using statements, one for the FileStream, and one for the [DllImport]:

using System.IO;
using
System.Runtime.InteropServices;

Permalink  1 Comments 

This one's an old one.  It has been floating around in the blogging community for ages, and I figured I would just plain ignore it because the arguments both ways are fairly petty from my perspective.  I don't like that method of syndication at all - and would much prefer a push medium for it, like msn alerts or icq/jabber can be made to do.

But someone has finally asked me if I can add an Atom feed.

This is a little annoying, since I already have an RSS feed, but I like to satisfy people so I busily go to look at the atom specification.  It was far better though out than RSS for the job it's doing and it supports many more features.  If I was implementing the syndication features of my site again I'd choose atom.

But I have no intention of doing it for the moment, there's simply no practical benefit to it over rss - they are simple xml versions of content that is available through other means - I'm really tempted to make my own spec for a syndication format and see if a similar contest occurs...

Lets call it Stupidly Simple Syndication

<Feed>
   <Name>Feed name</Name>
   <Home>URL</Home>
   <Pinger>Address of XML webservice that allows you to define a url, ip and port, icq number, jabber address, passport or email address to call back when this feed is updated</Pinger>
   <Article>
      <Date>UTC date and time</Date>
      <Link>Link back to the article</Link>
      <Author>Author contact details or name</Author>
      <Subject>Subject</Subject>
      <Category>Optional category of article</Category>
      <Extensions>Somewhere people can put anything extra about articles</Extensions>
      <Content>The article</Content>
         <Comments>
            <Comment>
            <Date>UTC date and time</Date>
            <Author>Author contact details or name</Author>
            <Subject>Subject</Subject>
            <Extensions>Again, optional stuff</Extensions>
            <Content>The comment</Content>
            </Comment>
         </Comments>
   </Article>
</Feed>

We will rely on the existing HTTP headers to define when it was updated, when to fetch another one, etc. since we don't want to waste bandwidth.

We will also not worry about implementing shedloads of features most people won't care about in the base implementation, instead we will allow the use of extensions.  Setting up a webservice at a reliable url on the net allowing people/aggregators to pull out a description of the extensions used in a feed (ie - what format data is in there, why, what's a human name for it, is it for a machine or a reader, etc.) and anyone to register one.

This will permit it to have more features than either RSS or ATOM, whilst also being small, perfectly logical, and stupidly simple to begin with.

Permalink 

So, I had come up with a way to do double buffering before using a backbuffer and flipping it to the front for a simple game I wrote as a test.

I have just noticed there's a Control.SetStyle method:

this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();

Executing this in the forms constructor will turn on double buffering the correct way.

Permalink 

Three years to the day yesterday, at 12pm I started full time work at unnamedWorkplace when I should have gone on study leave for my exams.  12:00pm today I am 24 hours free with a stack of work, list of projects, bunch of supportive friends and no worries for the moment.

My departure from unnamedWorkplace was swifter than I expected after giving in my notice, but overall this is probably for the best...  I have lots of ideas for the short term, but in the long term I'm open to suggestions, or whatever turns my way.

Now to build my own future...

Permalink  1 Comments 

Well, I finally resigned. Permalink 

This is a rant, one of those almighty work related rants that result in Bad Things usually.  But it needs to be said.

So I go to unnamedWorkplace every day, in and out and support the 2500 ish users there.  I do more than my hours and make sure of it after I was complained about leaving early one day (after coming in early) even though I take none of the legally required breaks and just take the time at lunchbreak I need to eat.

I put in as much effort as I can.  I am overworked, yet I feel like I haven't exerted myself mentally.  The problems are always small, simple issues; relating to configuration, data extraction, hardware problems, accidents or downright stupidity on the part of the users.

The points on my appraisel completed, are ones I had to complete - all training was paid for by me, all self improvement driven by me.  The unnamedWorkplace has failed to complete all of its points, including taking another appraisel, I believe it was due December 2003.

On the whole, the only reasons I have not resigned are the sallary and lack of a replacement for the moment; and my other colleagues across all departments.  My friends are steadily leaving too - another four people I work with and have known well for many years have announced they are leaving, and the beaurocracy and inefficiency is steadily increasing.

So go on, make my day.  Eliminate one of the reasons for me to stay:  If you have an interesting programming or complicated support job let me know, if I can't do it now I will adapt.  Fast.  I'd even work for free for a bit as a trial (subject to contract of guaranteed employment or payment for time spent!).

Permalink  7 Comments 

A friend of mine has made a post to his blog (http://www.illegalexception.com/content.php?id=102) about web services been bad for security.  I don't see it that way - I see them as exactly the same as any other web based application or script that's exposed to the Internet and no more insecure.

That's not to say they aren't all insecure in some way, but he's concerned about the security of the client when a web service runs on the server and provides only an XML interface for data exchange.

I can't understand why a virus, trojan, or idiot user would install a webserver, configure a scripting language (Java servlets, PHP, ASP.NET, what have you...) then advertise a web service that will run with fairly limited rights (ie - the rights of the scripting language, which can't be root on *nix and is commonly INET_yourcomputer on Windows).

It would be far easier for a virus to simply down the local firewall then open a port - and it would have more power once it was done.

Web services simply take the human interface out of web applications, allowing an application to directly access one as if it were a local module of code - IE - the amazon web services allow you to search for products on the amazon site, and get back a list of objects in your application.

Permalink 

So someone e-mailed to ask me why I used as to cast my object:

IHiThere asm = o as IHiThere;
if (asm != null)
{
   MessageBox.Show(asm.Hi());
}
else
{
   MessageBox.Show("Failed to make asm = o");
}

Rather than:

IHiThere asm = (IHiThere)o;

When it is neater.

Well, there is a simple reason: This is because the as operator doesn't throw an exception when it fails but instead fills the variable with null. The check if the value was null checked if the code worked or not.

Note that in the real world you will want exception handling around the whole block, incase the user selects the wrong type of dll, or access to it is denied, or... well, you get the picture.  This means you can freely use brackets to cast it - but this is in my opinion a more elegant method of doing something where you want to know if it succeeded or failed.

Update in 2009:

I would just like to clarify that I was trying to make the point that you should use the as keyword when it's something you expect not to work but don't want to use the is keyword!

Permalink 

So, I'm playing with reflection with the intention of making a plugin system for future use and looking at the obscenely complex articles on the net that don't cover anything even remotely like what I want to do.  Which is have an object I didn't write, and be able to .DoSomething with it rather than have four lines of garbage to just run a static method.  I wanted to be able to use classes I hadn't referenced at design time, just as if I had.  Obviously they need to follow my interface definition, but that's a small price to pay.

So I eventually give up and go back to the docs and play around for a bit, eventually getting this ludicrously simple method working:

OpenFileDialog openasm = new OpenFileDialog();
if (openasm.ShowDialog() == DialogResult.OK)
{
   Assembly testasm = Assembly.LoadFrom(openasm.FileName);
   Type[] asmtypes = testasm.GetTypes();
   foreach (Type t in asmtypes)
   {
      if (t.IsClass & t.GetInterface("IHiThere")!=null)
      {
         object o = testasm.CreateInstance(t.FullName);
         IHiThere asm = o as IHiThere;
         if (asm != null)
         {
            MessageBox.Show(asm.Hi());
         }
         else
         {
            MessageBox.Show("Failed to make asm = o");
         }
      }
   }
}

A quick rundown, that's:

Load the assembly you want to look at:
Assembly testasm = Assembly.LoadFrom("filename.dll");

Get a list of the types of objects in it:
Type[] asmtypes = testasm.GetTypes();

Loop through it, and for each class that implements your chosen shared interface* create it as an object:
object o = testasm.CreateInstance(t.FullName);

Then cast it to your interface:
IHiThere asm
= o as IHiThere;

Creating it as a local object you can use just like normal:
MessageBox.Show(asm.Hi());

* - Note that the interface in question here looked like:

public interface IHiThere
{
   string
Hi();
}

And was referenced by both the 'plugin' and the 'host' application.

The plugin side was a simple class that implemented the IHiThere interface, and returned "Hellow world from the plugin" as a string in the Hi method:

public class Class1 : IHiThere
{
   public
Class1()
   {
   }
   public
string Hi()
   {
      return
"Hello World - from the plugin";
   }
}

Permalink  1 Comments 

This is a bit of a rant, but still - might help someone also plagued with the same problem.

I'm doing several projects at the moment, and finding that templating in ASP.NET is non-existant.  I know it's going to be fixed with the new version in 2005 - but still, I don't want to wait.

So when making a page with a consistent layout you have three choices:

  • Generate all content for the page and feed it to the client, ignore all the object based capabilities of asp.net and use it like PHP or Java server pages.
  • Use copious literals to import your header, footer and menu from someplace or auto-generate them at run time.  Again loose any nifty advantages asp.net had over the others in the process - but keep them for the main content
  • Output the page as XML then format it using an XSLT.

At the moment I've gone with the literals - it's easiest whilst still being asp.net rather than c# generating html and sending it to a client.

But what did Microsoft expect us to do?  This is just downright silly...

Permalink 

Update: If you work for Maindec, go away and research this yourself.  My previous manager was certain that you already knew how and didn't want my help before I resigned yet I'm getting repeated hits from your domain, so obviously what was meant was 'you know how to use a search engine'.

Someone hit this site with a referrer searching for how to convert from MDaemon to Exchange - something I'm casually thinking about every time I have to change something user related on our MDaemon server at work.  MDaemon is technically fine at handling lots of users (although a bit slow without referential storage).  But the user interface isn't suited to more than 500 users.

It's either Exchange or Exim and Courier-IMAP - which is a nice quick migration if you use Maildir's.  (HINT: To convert from MDaemon's mail format to Maildir you simply need to copy the messages into the new folder of each maildir, then convert them to have unix carriage returns - you can do this easily with a three line shell script in bash on Linux)

With converting to Exchange, I think you would have to use the exchange migration tool that comes with Exchange 2003.  This will allow you to connect over IMAP and fetch the messages.  This will take a CONSIDERABLE time, but I haven't looked into exchanges API - it is likely fairly easy to bulk import the mail much quicker than via IMAP.  I'll investigate this when I get a chance!

The easiest path to take is this:

  • Set up the Exchange server, with all users, etc.
  • Configure the Exchange server to recieve all mail
  • Rename the MDaemon servers domain to be something else
  • Notify all users to use the Exchange server and configure the machines/accounts/profiles of all users to use the new exchange server
  • Tell the users they can access their old mail and send fine from the MDaemon system - but will only recieve mail on exchange.
  • Migrate anyone that complains, and if you have lots of time free (cpu wise) try to migrate everything.

This will get your Exchange system up and running as quickly as possible - and fielding the queries from people will take less time than migrating the data in cases where there's considerable data in place.  You'll be surprised that only a few people will want all their old mail in the same place as their new mail - mail is time dependant and eventually it will cycle over.

This does require that you leave the MDaemon system running for a year or so - but use of the system will decrease as time passes by, and when it reaches zero in a month you can back up the data and reuse the physical hardware.

Permalink  1 Comments 

This makes me very happy - it loads quicker than visual studio and has a few of the features I like from visual studio in it.  It also allows you to highlight the current line - very handy in a wordwrapped document!

Omar Shahine at Microsoft mentioned it, and also covers how to replace notepad the correct way.

Permalink 

Has anyone seen an online real-time code collaboration application out there yet?

I'm looking for something suited to pair programming but online and with simultaneous data entry capabilities.  So both people have Visual Studio, both see the same code.  One types in one place another types in another and they can each see (assuming they're working on the same file, and the same place in that file) each others typing in real time.  This would allow for you to help another programmer.  They say two heads are better than one...

That or if anyone has any idea how you can easily do the user interface to such a thing so I can build one myself.  I was tempted to use accessibility to read the contents of a window then remoting to send it but this fails on the actual display of the entered data aspect.  It adds a major disconnect to have it in a seperate window.

And I don't want to reinvent the wheel and completely write a multiline, multiuser edit box by hand.  Not yet anyway... Permalink  6 Comments 

I've been thinking, how do you get around telco lockin?

You can't - well, not easily.  At least, not till there's a wireless mesh covering the whole world.

This is what the people at consume are aiming to get.  I think the ham radio operators got it right when they tackled the problem, which puts consume on the right track - but we need a longer range wireless medium to solve the problem.

Now, 3g, gsm, and others can solve the problem of range - but they're also all relatively slow in performance.  Ideal for keeping a continuous link, for chatting, for mail if you don't mind waiting - for voice services even.

So, what service covers the middle ground?  I don't particularly want a multi-billion dollar license just to provide wireless service to my peers.  I don't need a range in the kilometres (although that'd be handy!).

Permalink 

Okay so I need to know a bit of Java and be able to build something if needed to help a friend so figured I should install the stuff to get it going.

Now, here's the first of not many plusses over .NET.  I installed Eclipse by unzipping the archive and running it with an appropriate SDK installed.

It ran.  I was impressed.  Mostly by:

  1. The fact it wasn't as slow as treacle like all over Java IDE's I've had the dubious pleasure of trying out. 
  2. The fact that it was obviously Visual Studio .NET with a few minor improvements like a most excellent colouring of the current line slightly different so you knew where you were even with wordwrap and/or a long line.  Refactoring and code templates like what is coming out in Whidbey/Visual Studio .NET 2005 was also already in it and working well.
  3. My java app compiled and ran - the only difference between it and the equivalent C# app?  For some stupid reason Java's ArrayList is in the java.utils namespace rather than a collections namespace.
  4. Eclipse looks correct on Windows XP.  WHAT I hear you yell...  Well, it's true.  Visual Studio looks like it's on 2000 even if you change the theme, it's just poorer integration.

Now for the negatives I've noticed so far just playing around:

  1. No foreach on objects.  This one is really getting to me.  For just does not cut it!!!
  2. Threading doesn't use callbacks - you have to build a new thread class based on the parent class of Thread.  Seems somewhat of a disconnect from keeping the code nice and modular.
  3. The string type is capitalised.  Yes, I know this is petty.
  4. C# code looks nicer: Getters and setters are more neat and tidy.
  5. Where is delegation in Java?  It just seems to be... missing. 
  6. Interfaces seem to have been thought out more in C# - how do you prevent one being run if the object is addressed as its native type?
  7. Where are enums in Java?  Also... absent without leave.
  8. I can't seem to find struct's either - how to you make a high speed primitive type?
  9. Where are the overloading features again?  I can't find mention of most...  Operator overloading for example.
  10. No versioned GAC.
  11. No attributes for methods.

All this is ignoring the multi-language capability of .NET, and the 'interesting' model of page generation of ASP.NET where objects exist between calls and

Permalink  3 Comments 

Hmm, lots of people out there are searching for Jen Frickell on Google and mysteriously getting to my site because I mentioned her a while back.

The site you're looking for is over there, not here, although she doesn't update as much as she used to, bar special occasions - like April fools day...

Permalink 

Here's a little something that took a couple of hours to write, feedback is as always appreciated!  As I've already been asked: this is all GDI based, no DirectX...  Graphics for it would be appreciated!

Anyway, click here to run GravCave from my server, you need the dot net framework - available from windows update, or I expect it will run okay under mono.  If you wish you can right click and save target as to your local machine - it's 40kb and will run from anywhere.

Another .NET game in the same vein is Chris Sells' Wahoo.  This came about as I was curious why dot net based games weren't slowly appearing yet, so thought I'd see how difficult it was to code a game in: turns out with C# it's much easier than many other languages.

I assume when people realise that they CAN use DirectX with an app that is loaded directly from the net, they'll start using it.

UPDATE: Source code is available on this page on my site

Permalink 

Ever wondered how to double buffer with a Graphics object so your GDI+ based game/control doesn't flicker annoyingly?

Me too.  There's probably a built in method that's easier, but this is how I managed to get it to work smoothly, it's nice and simple and allows you to draw anywhere that offers up the normal CreateGraphics method.

First, set up a bitmap to act as your backbuffer:

Bitmap BackBuffer = new Bitmap(this.ClientSize.Width,this.ClientSize.Height);

Graphics DrawingArea = Graphics.FromImage(BackBuffer);

Next, you want to draw to your graphics object as normal, so DrawingArea.Clear(Color.Black); and such.

Once you've completed drawing the object that you want to smoothly move, simply draw the pre-rendered bitmap over the top of the Graphics object you want to update:

Graphics Viewable = this.CreateGraphics();

Viewable.DrawImageUnscaled(BackBuffer, 0, 0);

You can also use other techniques to increase the performance, such as reusing the backbuffer by defining it in the class you're using it in - this means .NET won't need to recreate it repeatedly.

Permalink 

Okay so I was just wondering why Kunal Das' OutlookMT looked very much like the idea I had for solving the problem, until I find this post whilst searching for a way around the annoying security dialog you get when accessing Outlook from C#:

http://blogs.officezealot.com/Kunal/archives/000503.html

It looks like a suggestion I made on Scoble's site about how I would access Outlook to enable blog integration was the initial inspiration for OutlookMT's solution to Scoble's problem.

I guess this blogging thing really does work...

Permalink 

If your validation isn't working in Asp.net after deploying to a webserver with multiple virtual hosts you might find you need to copy the aspnet_client directory from the default site to the affected site to get the client-side validation working.

A good reason to always do server side validation!

Permalink 

This is my first attempt at an instructional article, so opinions on quality would be great!  Let me know if I made any mistakes too...

It's mainly for all those like Robert Scoble who would like to be able to drag and drop an item to a folder in their Outlook and post it instantly to their Blog, but it also covers web services and talking to Outlook.

Accessing Outlook

The first requirement is to be able to access Outlook.  For those with Outlook 2003 and XP this is relatively easy, thanks to .Net and Microsoft shipping an appropriate assembly with Office.  To install the Office 2003 assembly, you should run the office install and choose .NET Programmability Support.

You might need to use the command prompt to copy Microsoft.Office.Interop.Outlook.dll out of the GAC after installing it so you can add a reference to it, if you can add it as a reference otherwise do so and let me know how!  The Visual Studio add reference dialog doesn't seem to list items in the GAC...

Add an appropriate using clause:

 

Then you should be able to instantiate an Outlook object and make requests of it:

Outlook.Application app = new Outlook.ApplicationClass();

Outlook.NameSpace NS = app.GetNamespace("MAPI");

Outlook.MAPIFolder inboxFld = NS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);

This will give you access to inboxFld, which will allow you to iterate through the contents of the inbox!  You can also change this to iterate through notes, or through calendar entries, tasks, etc. as you want.

For example, to iterate through your mail you can do:

 

Console.WriteLine(t.Subject);

}

To write out all the subjects on the console.  The only annoying thing will be you need to say yes to a security dialog when you access mail items - I'm working on getting around this, it doesn't happen for tasks or notes, etc.

Once you are able to access Outlook, your next objective is to post data to your weblog.  You can avoid duplicates through one of two ways:

  • Keep track of what has been posted by maintaining an ArrayList of articles on your blog and checking before trying to post one.
  • Keep track of what has been posted by changing something in the MailItem's - e.g. - set or clear a flag.

The first method requires keeping a list synchronised with the blog, the second is quickest and easiest, but wouldn't be suited with multiple people possibly posting things.

Posting to your blog

Obviously everyone is using different software to manage their blog.  I can't give an example of every single method, however the simplest from a programmers perspective is if you can access the database of your blog via a webservice.

Building a webservice

Google can supply many examples and tutorials, however an example of doing this is fairly simple.

Choose to add a new webservice to your site, or create an entirely independant project and call it something suitable - ours will be blog because it's an example.

Firstly, you will need to add a few more items to your using list, so you can do XML serialisation of structures and objects.  I will assume your database is MS SQL Server too, so ensure the following are listed in addition to the defaults for a webservice (I forget what they are):

 

 

 

 

 

using System.Web.Services.Protocols;

 

 

You want to be as object oriented as possible when building your webservice, so you should define a NewsItem structure to pass back and forth, you can adjust this to include whatever you need to store in an article:

 

{

public int id;

public string topic;

public string subject;

public string postedby;

public DateTime postedat;

public string content;

 

}

This will allow you to reference rows in your database as objects, a simple organisational benefit that crosses over and permits easy use of methods of the webservice without passing a lot of parameters.  It also allows you to add groups of entries to an ArrayList, which is a big benefit (although there is a problem converting from an object transferred by a webservice and an ArrayList, if you ever do this you will need to iterate through the object and add the entries back to an ArrayList - .net does not support converting from an object[] to an ArrayList).

You can then build your method for adding the article to the database.  I have used the database on my blog as an example, you will obviously need to change the insert statement and connection string to fit your situation.  There is also no exception handling, ideally you should enclose the opening of the connection and the executing of the query in Try Catch blocks.

Note that the XmlInclude for the NewsItem struct is listed, this allows the webservice to accept a newsitem given as a parameter - otherwise if would not know to serialise the structure.

[WebMethod]

[XmlInclude(typeof(NewsItem))]

public void AddArticle(NewsItem newarticle)

{

SqlConnection sqlcn = new SqlConnection("Data Source=(local);" +

"Initial Catalog=NullifyDB;" +

"Integrated Security=SSPI");

sqlcn.Open();

SqlCommand sqlcmd = new SqlCommand("INSERT INTO newsarticle (subject, topic, content, uid) VALUES (@subject, @topic, @content, @postedby);", sqlcn);

sqlcmd.Parameters.Add("@subject", newarticle.subject);

sqlcmd.Parameters.Add("@topic", newarticle.topic);

sqlcmd.Parameters.Add("@content", newarticle.content);

sqlcmd.Parameters.Add("@postedby", 253);

sqlcmd.ExecuteNonQuery();

sqlcn.Close();

}

You should then provide additional methods for anything else you would want to do, such as listing articles, deleting articles, and editing.  For Outlook integration you really only need this method.

Accessing the webservice

Once the web service is up and working, you need to create a web reference to the webservice, this is done in visual studio by right clicking the references box and choosing add web reference.  Lets say there's one at http://webservices.nullify.net/blog.asmx

When you add a web reference Visual Studio will automatically produce a wrapping class that will allow you to easily instantiate the web service as a local object, without worrying about any of the underlying technology.  (I'll only cover synchronous calls here, otherwise this will turn into a full fledged book...)

To access the above web service, you would simply define it as a new object:

net.nullify.webservices.Blog blog = new net.nullify.webservices.Blog();

And you would define our NewsItem scructure that we defined in the webservice:

net.nullify.webservices.NewsItem article = new net.nullify.webservices.NewsItem();

This will allow you to now call methods of the blog object, which will execute directly on your web server, with all the rights of a normal asp.net page - including the ability to insert articles into your database!

Using our imaginary webservice, rather than writing the subject for each MailItem to the console, you can post them to your blog:

article.subject = t.Subject;

article.content = t.Body;

article.topic = "OutlookPost";

 

blog.AddArticle(article);

(Note, this is assuming your webservice has no security, or is protected by asp.net/IIS' own security!)

I hope this post helps someone!

 

public struct NewsItem

 

using System.Xml.Serialization;

 

using System.Web.Services.Description;

 

using System.Web.Services;

 

using System.Data.Common;

 

using System.Data.SqlTypes;

 

using System.Data.SqlClient;

 

foreach (Outlook.MailItem t in inboxFld.Items)
{

 

using Outlook = Microsoft.Office.Interop.Outlook;

(This article has been truncated due to migration to a new database, apologies!  I hope what is here helps and if you have questions there are copies around on the web of this article.)

Permalink 

*********************************** I, CRINGELY - April 1, 2004 Column *********************************** This message is being sent to inform you that Robert X. Cringely's latest column is now available online at: http://www.pbs.org/cringely/ The title and topic of this week's column are as follows: "Timing is Everything" No matter what happens in Microsoft's current slew of court cases, the end will be the same, says Bob: Redmond will win. ******************************** SUBSCRIBE/UNSUBSCRIBE To sign up to receive notification of new "I, Cringely" columns, please visit http://www.pbs.org/cringely/when/, or send an e-mail to listserv@pbs.org, and in the body of the message type "subscribe tellmewhen-l * " (without quotes). (You can substitute your name in place of the *). If you would like to have your name removed from this list, please send an e-mail to listserv@pbs.org, and in the body of the message type "signoff tellmewhen-l" (without quotes). To avoid conflict, be sure to send the message using the same account that you used to sign up. An e-mail confirmation will be sent when you are officially removed from the Cringely list. If you experience difficulty unsubscribing or you're interested in changing e-mail addresses, please send your request to www@pbs.org. ******************************** Copyright 2004 PBS Online. Permalink 

This is being posted from a winforms application using a webservice interfacing with my weblog. Next up, posting from outlook by dropping items into a folder! Permalink 

I finally understand.

The reason I've been unable to write good documentation all this time is who has been reading it.  I write code that describes how to do something to a computer.  That documentation must be excellent syntactically, all encompassing and as flawless as possible.  It must also be complicated as the compiler/interpreter is generally fairly stupid and needs intructions for every step.

I have a tendancy to take that over when documenting the code, what I really need to do is say "This does x" rather than "This does y to z in order to get x to be the desired value, but has to take into account a, b, and c factors".

I finally understand - document simply and briefly to document well...

Permalink 

Do you have an idea for a piece of software that you personally would like?

A tool that would help you in your day to day use of a computer?  Something that would be neat on your smartphone?  An application to store/retrieve a particular type of information on a pocket pc?

Something that solves a problem in your classroom/computer lab/school/university/office/shop floor?

A web site that needs to do something really special?

If you want it more than you want to sell it, let me know your idea. I am willing to solve interesting problems free of charge so long as I keep full rights to resell them! FREE development. You/your company/your school/your university get full rights to keep it and if you so desire distribute a marked copy so long as you don't charge for it I'm open to other relationships too such as sponsored open source, and complete development under contract - ideal if you need something like a website that can answer industry specific questions from your users, or that needs to solve a problem that won't sell to others. Or if you just want to keep your hands on it!

What do I get out of this? There is a chance I could get to build the next winzip.

What do you get out of this? Your problem is solved.  Period.

What can you get out of a really good idea? A split of any profits!

Hopefully this business model will suit anyone, with any amount of money so long as there is a need or an idea, let me know your opinions.

I'm already working on one solution using this idea, and it seems to be working quite well for everyone involved.

Permalink 

Sometime in the next two days I will be turning on the new version of SiteBuilder - the software that runs this site (need to come up with a better name...).

The new version is written in C#, and although I've built it so the urls of everything are the same certain things have changed.

Therefore expect things to break a little... (RSS GUID's for one thing, the admin system, no more workdrive file storage for the moment, no comic management)

Workdrive will be reappearing completely rewritten in .NET in a short while, along with full PIM and Outlook integration.

Update: All done and live!
Another update: And the RSS feed now validates too... Permalink  4 Comments 

I unplugged my headphones, and my whole system crashed! Amazing how these things happen, but to be honest it's the first crash where it hasn't been directly related to hardware failure.

I got sent to https://oca.microsoft.com/EN/Response.asp?SID=77 ("Error Caused by a Device Driver") which is completely useless.

During seperate testing on another machine, I found that this can also result in an immediate crash on login as the system attempts to play a sound.

Lets see how many people get sent here from google...

To workaround for this problem to prevent the system crashing is you need to go into the Sounds and Audio Devices control panel, then change the default devices for playback and sound recording to a present soundcard either in safe mode if you don't have access to the USB audio device, or before removing the device.

Now to find out how to actually submit more details for a microsoft bug report... A simple "What were you doing when this happened?" text box would probably help a ton on the oca.microsoft.com site. Permalink 

I'm just starting looking into the possibility of rebuilding the software this site runs on in C#. It looks like it'll be fairly easy to make a seamless transition from a visitors perspective, but I'll probably need to completely rebuild the admin system. Which isn't a bad thing... Question is, how secure is code written in C#? Will I need to have extensive checks on user input like I do in PHP... Or will I need to have more checks? My guess is less as it's not a scripting language, but it'll be interesting to see how easy it is to break it. Permalink 

I'm sitting here, waiting, wondering about Visual Studio .NET. It works well for any one task, yet I have a fairly small project I'm building to solve a friends problem at their work and it seems too big for Visual Studio. Let me explain: The solution has five projects - the web client, a smart client for one type of user, a smart client for the admin user, an assembly with shared classes, an installer add-on to install the database - and a couple of installers. In total there are barely fifty forms/source files. That isn't too much stuff, I've seen projects with twenty assemblies, resulting in thousands of files. Visual Studio, on opening my small solution opens every file. Slowly. The end result is a single line of tabs for each file so you can't find anything in it and end up ignoring and instead use the solution explorer. You can compile everything in the solution quicker than you can open it initially on my machine. And I have no animations or anything on, it simply takes AGES to load and show everything. I can close it all before I save, but I shouldn't need to - and even then it still insists on doing SOMETHING with every file when you load it. I pity anyone that doesn't start to split their solution into multiple smaller solutions when they get to 100 files... Question is... Will Whidbey really speed things up a bit... Permalink 

It appears that the expensive car (Windows 2003) crashes more often. It looks like the last crash was to do with an ISAPI module and IIS. Windows itself remained up. It bought down the whole of IIS too, not just the web services, and not just the ability to run one type of dynamic page (I don't know which isapi module as of yet, but my guess is asp.net or the gzip compression module as they are the only ones loaded). On the other hand, the tiny debian server with jvds.com has only been down because of a reboot for a kernel vulnerability. The ability to auto-restart after a short delay is built into the OS, but what about IIS - why can't you set an IIS service to auto restart, then if it fails x times wait 20 secs and try again?? Permalink 

7 Days uptime and we're on a roll. The TCO for Windows may be higher, but it is for getting a more expensive car too. Now we get to see if the more expensive car is better than the economy model. Permalink  3 Comments 

Lets hope there's no more patches released. I've finished moving everything across, it's just a case of checking that nothing is using the other server for a few hours before I shut it down. Permalink 

I got my test web service to run on the new server! Since my network provider has blocked ports 135 etc. I have to use frontpage extensions if I wish to access it with visual studio - which was really slow as I only have cable. Would be nice if you could use the tried and trusted ftp to publish projects! (I know I can manually do this, it's just not the same building it locally then ftping it up every time I build something - I'm going to go try script it myself, should be fairly easy...) Permalink  1 Comments 

Firstly, now I'm into the swing of posting stuff, I appear to have become addicted... Someone tell me when it gets rediculous and I need to stop. Some background: Today till friday are days off from my day job as a network admin at a College in the UK. I intended to read up a bit more on my MCSD, relax, go shopping, do some coding, etc. I'm actually spending most of it still doing a migration from cobalt server appliances onto a Windows 2003 Standard box (the Linux VDS' are already up and running, and have been now for 11 days). This is for my hobbyish hosting business. Why I'm writing this however is that I'm actually seriously concerned that I have today off, as part of my responsibilities at work include ensuring everything is secure - and that includes running patches of which it appears the one that showed up for the Windows 2003 Server earlier (I didn't recognise what it was for) appears to actually be a very serious one indeed. I expect to see a vulnerability for this flying around the net within the next few days causing appropriate devastation, and all the other things that ensue. I just have to hope that the firewalls, antivirus, and 'overkill' filtering at work actually holds out against whatever nasty bug gets invented. *sigh* Permalink 

My first almost server wide crash. I opened IE (configuring frontpage extensions) on terminal services and somehow it crashed the whole GUI. The last thing I got to do was send an error report. Not a real crash, as the server was actually okay and still serving pages - but still, a real inconvenience and I couldn't reboot it through the admin interface (it said it was rebooting, then did nothing. Same with trying to use shutdown.exe in a telnet session - I intend to install pskill on it ASAP) so was a right annoyance as I had to ticket a hard reboot. There is no way a Windows server is even remotely close to as stable, or as reliable as a Linux server at the moment, the kernel might be - but the user interface and patching is letting the whole system down. UPDATE: I'm really glad I personally know the customers I've already moved onto this server, if I didn't I'd feel really bad about this - instead I know I can make it up elsewhere, but if I had just moved a business critical website over I would be fuming. Permalink 

I got the new server on the 1/2/2004. I applied security fixes and rebooted it. Today, I found another security update to apply. I apply it and it wants a reboot. With the cobalt box, it would run literally for 200+ days without needing a reboot for a patch. I know I moved away from it for security reasons as the patches weren't coming anymore, but Microsoft really need to fix the rebooting of the machine. You don't need to restart ASP.net whenever you replace a web application, so why do you need to reboot the machine when it's a simple service or process that could be restarted on its own? The ability to replace a binary whilst running has been in Linux for a long time (no idea how long) - Microsoft recently added it to Windows in that it provides volume shadow copy services to permit the same kind of thing. So why does it still need a reboot?????? Anyway, enough of my frustrations. At least Microsoft are releasing patches unlike Sun do for Cobalt raq's. Advantage: It reboots fast. Permalink 

My customers like Windows 2003, well so far every one who's tested their site on it. Everyone (so far) who's tested the ftp thinks it's much faster. The ftp speed difference may be related to the hardware difference and current lack of users, 2ghz is considerable. On to a breakdown with more detail, please feel free to ignore this if you're stopping by for progress updates on the migration - it's more just for those who are interested.
  • The DNS server... Well, works. The configuration is again GUI based, which places restrictions on the speed of doing it, where I'd paste another four lines (or use a for loop!) and change the domain I now have to go through a wizard each and every time. Then go back in to turn on notify!
  • I like the look of the ATRN option on the smtp server. It makes what would have been a very painful thing to set up on Linux fairly easy. I dislike the lack of a basic IMAP4 server to go with the POP3 server though.
  • Permissions are massively better on Windows than on Linux, although the defaults always seem a little lax. The ability to fine grain restrictions can only be considered a benefit. cacls (command line tool) is a nice touch too. I miss the tickbox that Windows 2000 had where you could stop the rights being inherited. I know it's one click deeper, but that's annoying when you have to use it fifty times.
  • I want to script changes to the DNS Server, anyone have any ideas? Preferrably without stopping it, editing the registry, then starting the service again.
  • Why do I have to go through the process of manually doing a million things for each user I create. I want to be able to trigger a batch file to run every user that's created, and to create users based on a template!
Well, that's enough stressed babble from me for the moment, I'm off to try to find an alternative to .htaccess files that works on Windows so my users can configure their own restrictions and settings. Permalink 

To those experiencing strange problems with their hosting, this is as per the e-mail. All databases are now coming off xerxes.nullify.net, mysql.nullify.net redirects to it to continue to provide service without interruption if configured correctly in the first place. The databases on shodan are now in read-only mode, and all users are denied access. You MUST e-mail me with details of what accounts you need created on the new server setup. Your sites are already moved onto the new server unless you are paying, if you're paying they're going to be updated on the new server when I have confirmation of the final DNS change that will be coming sometime soon. Several people I own the domains for and host because they're friends have already been moved over, if they are experiencing problems then they need to contact me for their username and password - I'd mail it but without it you can't download your mail! (I'm trying to set them the same as initially - but if you changed your password I won't know.) Permalink 

I've started the test migration of data to the new windows 2003 server, this could be interesting. First points of note:
  • I prefer editing a config file to wiggling the mouse and using a gui.
  • Terminal services is laggy compared to SSH.
  • Microsoft's telnet server is very laggy compared to SSH, easily worse than a full graphics session over terminal services. Not sure how they managed that.
  • IIS 6 is almost the exact same speed on a 2.4Ghz machine as Apache on a 400Mhz machine.
  • PHP doesn't work so well under Windows, but fastcgi almost fixes this. Might just be the way Windows deals with starting new processes.
All in all, I am not impressed. But there is still time for things to change before I change my mind and get a debian server. Permalink 

Okay, I'm looking for comments primarily from users of the hosting regarding a hosting network rework. At the moment the setup is both overkill, and soon to be underpowered. I'm also concerned about the supportability of Cobalt units. I'm proposing to change the setup to one of the following options: Option 1, most outlay, best performance:
  • One UK Server, from dedipower with copious bandwidth and Windows 2003 Standard (expensive licensing is the biggest outlay) - acting as the main web server, main database server, backup mail relay and primary DNS
  • One US Virtual Dedicated Server, with very little disk space and memory but a decent chunk of bandwidth, running Linux - acting as secondary DNS and the primary mail server, but backed up onto the UK server.
Option 2, low(er) outlay, US only and less reliable:
  • One US Server, with Windows 2003 Web Edition - acting as the primary web server and DB server
  • Two US Virtual Dedicated Servers, running Linux - DNS, MAIL, etc.
There are other options, but those are the main two. Any suggestions, comments, ideas? I'll be getting the VDS as soon as possible as it's a part in every single setup and I wanted a backup mail server in a different datacentre anyway. Update: Just bought the VDS Update: Just bought the Win2k3 server, I'll see how it is for a few days before moving anything. Permalink  2 Comments 

The spammers are at it again, please do not mail me complaining about stuff with stupid addresses @nullify.net. The addresses do not exist, and were sent from the spammers directly, I had no involvement. Less than 25 addresses exist at nullify.net itself, of which only three send mail. Please check the message headers and report the issue to the appropriate abuse@ISP address. Permalink 

For 2004! And it appears that the big spam problem I've been having has been resolved, I do hope permanently. I think Microsoft are likely on the right track with 'charging' people cpu cycles to send spam, but fear that it's not going to affect spammers who use distributed trojans to send it. At least until that's not a possibility. Permalink 

This domain does not send spam. Infact, I go out of my way to prevent spam from entering it, and there are preventative measures should anyone attempt to send it. Recently a spammer sent a batch of messages with a return address at nullify.net that didn't exist, and have never existed. Please understand that a from address is easily faked to look like it came from anywhere, and that you should check the message headers for the true origin. If you have more details to enable me to trace this please IM me over ICQ or MSN. Permalink 

So, I think I've stopped updating subconsciously. I do still exist, I'm just thoroughly learning c# and the .net framework and being impressed that I can run stuff anywhere from linux to windows to my pocket pc without recompiling... Very handy. Permalink 

How do you notify a computer user, in a unobtrusive way that they'll still notice and have the opportunity to look at what you want to tell them? It's easy when it's important, you just show a dialog. But what about when it's not important? A system tray icon is sometimes missed, even flashing. The best I've seen so far are those little transparent popups in the bottom right. Any other ideas? Permalink 

I just rolled up a little application called [winunrar] that allows for extracting rar archives. Rar archives have better compression than zip, and this is a free app. Hopefully it'll be of use to someone, and it should mean that those without winrar, who don't like shareware, or generally want something small should still be able to benefit from rar compression! Permalink  2 Comments 

Looks like Microsoft are listening... Voice Command basically fullfills a half of what I was talking about a little while ago. Permalink 

I think I'm going to give C# a try after seeing Don Box and Chris Anderson do a MSDN TV program on Longhorn, programming in C# as easily as I program a page in PHP, yet writing a tile for the sidebar, and doing tcp/ip comms over http in just a couple of simple lines to make a Windows App post to Don's blog. Impressively simple. It might also be a solution to my long standing problem of decent RAD tools that develop for Pocket PC... .Net framework for Pocket PC should be usable from it. Combined with Mono on Linux, C# may well be the future of software development. I'm annoyed I discarded it as another Visual Basic when I heard about it initially... Permalink 

[Sitebuilder] now supports XML and particularly RSS formatting of pages. By including support in the site creation engine I've inherantly got syndication support for any page I feel like, including the main page. Check out the archive and the headlines in RSS - you can now blogroll my musings and silly observations! I've also started removing posts that when converted to XML don't conform to the standard or do but are unusable. It's easier than editing them and I think I want to start afresh. They'll always be in the iarchive if needed. If you're interested in using [sitebuilder] I can probably wrap you up a version under the GPL, so just [contact] me. Permalink 

Dylan Greene has found some pics of vs.net (and aero). Urls are at his site. Interesting... It looks like phpedit but with bits of dreamweaver. Permalink 

You could probably use a bayesian scheme to categorise music by mood based on the similarities of the compressability over time. It'd take training the system in the first place, but then it would probably be fairly accurate as the compressability is directly related to the music content. Then you could always listen to the right music for the moment... Permalink 

Tablet PC's are impractical. Looking at my PDA I find that hard enough to use with a pen - something bigger must be worse. We need a more natural means of control, the keyboard certainly cuts it for speed so maybe separate the tablet pc's screen into a keyboard (not like normal, but more like a pda's onscreen keyboard where applications don't hide behind it.). Voice recognition would be the most natural, but then you might as well scrap the screen and just have a bluetooth headset with a small flip out screen. Then you could see stuff, and hear stuff - whilst being able to dictate things to it too. Clearly we're not there yet... Permalink  1 Comments 

It's possibly the most twisted and inefficient language in existance, taking up an inordinate amount of storage space. It's not even easy to programmatically work with. Then again it's also almost human readable and you can fix almost all problems using notepad/vi. Grrr. Why can't there be a better portable storage medium... Permalink 

Bluetooth should auto-connect to a particular service when it was within range... So a pocket pc will just connect/sync as you walk in the house... Permalink 

There's loads of adverts on the net, but never for what you actually want... That and it's usually intrusive. I mean, I wouldn't mind being able to pull up on demand (a non-intrusive system, not something that comes up constantly) a selection of adverts for a particular thing, in a particular area, possibly with the ability to search by if they accept mastercard/switch/cheque/cash if they can fix your boiler on a sunday, if they have someone you can talk to about a product available at the moment, etc. A single like ebay but for adverts for anything would be handy. Permalink 

Why do people rely on the clock of the computer that the document is being accessed on? Why not rely on the senders clock... To enforce this it'd need for a small part of the encrypted document to exist only on the senders/intermediaries computer. When the document was requested this part could be requested, and sent without fear since the rest of the document is also required. After the time the part can be destroyed on the senders/intermediaries machine. This leaves a giant loophole - once you open the message, you can always read it, it relies on the software to destroy the part it has recieved after using it to successfully show the document. But I fear THAT loophole is unavoidable if the document is ever to be readable. (The document sent is encrypted so that only the correct reader can open it, and only when it's in its entirety.) Permalink  2 Comments 

I want a copy of a couple of days TV schedule on my PDA, preferrably in a searchable format as a simple listbox. That would be fairly useful... How on earth do you write a today screen plugin? Permalink 

My PDA isn't useful enough sitting on the desk whilst I'm at my machine. It should interact with the computer when I dock it and automagically: - Log into my chat mediums - Notify me of new e-mail rather than my computer, keeping the computer for work - not as a place to press F5 lots on a mail window - Notify me of new events, happenings and other things that would normally have me visiting sites - Be an information source that's not on screen, and therefore not a distraction Perhaps I can virtualise this by loading a page in IE that pulls data off my servers and shows the info? I don't have the first clue about programming on a pocket pc device... Shame you can't compile things in Delphi for it. Permalink 

Folders that show the contents of the inbox filtered would be handy. Permalink 

http://www.theregister.co.uk/content/31/30699.html Join The Registers cancer busters team, download a bit of software, and your computer will be scanning molecules for cancer fighting abilities while your computer is idling. Permalink 

I'm at work at the moment, but I just this morning tried Trillian 2 beta and have to share the good news that it has [jabber] support! I mean, yes - you have to pay for it, but it's well worth it and at only £15 approx it's not that expensive. Check out their website at www.trillian.cc and see if you like the free version. If you do you'll love the pay for version. (Hey, at least they're not paying me for the ad) Permalink 

I'm now the highest possible battle rank in Planetside (A MMOFPS - or massively multiplayer online first person shooter. Bet you didn't know that eh? Game with lots of people is more descriptive...). I still have another three command ranks to go, but what happens when you complete a MMOFPS? *breathes* Do you stop paying the monthly fee and never return, or do you continue paying, but not play except occasionally? A question for the ages... Permalink  2 Comments 

I've built an installer to download and install PuTTY - a free SSH client. It's available from the [putty] page. Permalink 

A CNN article has pictures (which were already in the wild) and some good commentary about half-life 2. It looks like the nda gloves are off. Permalink 

A very good T3 trailer is available at the apple site if you have quicktime. Permalink  2 Comments 

It is coming out. It will be at E3 this year. The NDA will end April 28th. The future is coming this year... And Duke Nukem Forever missed another slot! With Half-Life 2 coming out, pretty much all attention will be on it, and they're in media wind up already so we can expect less than four months of waiting, other people are thinking November to commemorate the first half-life. I don't know, but one thing is certain, the most popular game on the planet will have a sequel. Permalink 

3D Realms decided that they'd play an april fools on everyone. They announced in a very suspicious manner that they were releasing Duke Nukem 3D's source code to the public under the GPL. The april fools are those who think they're kidding... Fileshack have it up here for all to download! and we'll have a mirror at some point. Note that you require the duke nukem content to play the game. Update: Mirrored here. Permalink 

Jen Frickell's site is back. That is all. Smorr.

Permalink 

Not run by me, DonEasy provides a free file storage service like workdrive. If you have any other free file storage sites leave them in the comments! Permalink  1 Comments 

I've marked a number of accounts to no longer have the ability to upload files. Those people have one week to ensure they have a copy of the files before their accounts will be purged. Thanks to everyone that made constructive critisism, I'll be getting the new layout for workdrive up and running when visiting from www.workdrive.com and the billing system will come next month. Read more for a list of users that will have an extra free month for their feedback and assistance. Permalink  6 Comments 

Oh my god, where did they get a frigate from...? Permalink  7 Comments 

I`ve quickly cobbled together a brand new news system for the site, hopefully this should be faster, and more adaptable... Since it`s database driven, it will support searching, and comments soon, so watch out! More to come... Old news is accessible from the Archive link below. Permalink