Friday, June 30, 2006

How to rescue a Delphi project

Delphi has been around a long time and there is a lot of Delphi code around but there aren't so many people around to work on it anymore. Developers are moving onto more modern technologies. To me it looks like the people who continue to maintain this code aren't always the most technically proficient so due to this and the fact that the code is getting long in the tooth means projects are becoming a bit of a mess. These days if I'm asked to look at some Delphi application, it is invariably in a bit of a bad state. So how to deal with this situation? How can these ill projects be rescued? These are the steps I've been taking with the latest bit of code I've been asked to look at. First up, I added all the source to source control. Without source control, you're doomed. There are plenty of source control products around, many of them free. I've been using SourceGear Vault, because it's free for a single user and does averything I need. Next, I attempted to compile the project. The main problem here was libraries I didn't have. Fortunately these were all available free, so I could compile it quite easily. Now it was time to see what the code was actually like. Unsurprisingly it was a bit of a mess. Compiling with hints and warnings on generated several hundred warnings and this was before I noticed somebody had put the {$WARNINGS OFF} compiler directive in several files... Warnings are there to help and can generally be fixed pretty easily, so don't hide them! Fixing these errors took some time but helped to learn about the code. I'd recommend removing all code that is never called and all commented out code, there is no point learning about code that is never actually called. And it's always recoverable from source control. Also searches through the project that throw up commented out code are generally pretty annoying. Another great tool for finding problems in Delphi code is Pascal Analyzer, which finds lots of things that the compiler will miss. Even the trial version has enough functionality to be useful. Reading the code can give you clues about a few things as well. Given that the developers of this code had a habit of leaving in lots of commented out code, my guess is they didn't use any source control. I'm also guessing they started work on this a long time ago, with lots of pointer based operations and references to WinTypes and WinProcs (which if I remember correctly was removed after Delphi 1). So this is where I'm at now. What I need to do now is take this Windows application and convert some parts of it into a web service. This is where it could get interesting...

Tuesday, June 20, 2006

What is System.RuntimeType?

I was trying to get the metadata for a query coming back from SQL Server. I'm not interested in the data, I just wanted to know what the columns were in a particular table. So I was executing a query like so - 'SELECT * FROM blah WHERE 1=0'. When I executed it, I took the DataReader returned and used GetName to get the names of the columns and GetFieldType to find out the types. Perhaps there is a better way to do this but I'm unaware of it. But weirdly, GetFieldType returned System.RuntimeType for every column. I thought WTF is that? A quick Google then a quick Yahoo and quick MSN didn't show up anything useful. The only thing I could find was the comments from the MS Rotor source which said

"RuntimeType is the basic Type object representing classes as found in the system. This type is never creatable by users, only by the system itself. The internal structure is known about by the runtime. __RuntimeXXX classes are created only once per object in the system and support == comparisions."

But this still wasn't much help. So I thought I'd have a hack around. The reason it isn't documented and doesn't show in many searches is because it's an internal class, which makes it a bit surprising I'm seeing at all. But it does have a useful property called UnderlyingSystemType that returns the type I'm interested in. Of course because it's internal I can't access that property. The way round this is via the best thing since sliced bread, reflection. So here is the litle snippet of code I came up with to do just that.
object val = reader.GetFieldType(index);
Type runtimeType = val.GetType();
PropertyInfo propInfo = runtimeType.GetProperty("UnderlyingSystemType");
Type type = (Type)propInfo.GetValue(val, null);

Update - As mentioned in the comments (only took me three years to respond!), this code is actually pointless. The thing I hadn't noticed is that RuntimeType inherits from Type, so although it isn't possible to deal with an instance of RuntimeType directly, you can just treat it as an instance of Type instead.

Sunday, June 18, 2006

The construction of a disco shed

Here are some photos of the construction of my disco shed. I'm very pleased with the final outcome and I learnt a few things on the way. First I discovered that air-raid shelters are very difficult to pull down, especially when the bolts are all rusted in. Cutting through them with a hacksaw takes a flipping long time. Then I learnt that laying a concrete slab is a pretty big job. When I calculated I needed 68 bags of ballast (which is sand mixed with gravel) I thought I'd made a mistake. In fact it turned out I actually needed more. A cement mixer was pretty much essential as well. Also, getting the base level is quite important. Mine is a little bit out so my chair rolls down the slope when I'm sat at my desk. Not so bad that I fly out the door, but if I'd made more of an effort to get the base exactly level I wouldn't have the problem.
Finally, a disco ball is absolutely essential for the modern disco shed...

Thursday, June 15, 2006

The story of an SL-1210

When I was 17, my main ambition in life was to own two Technics SL-1210s and be a world famous DJ. The SL-1210 is a design classic. It has been around in one form or another for over 30 years and it is built to last. Everything about it is industrial strength, except the tone arm which by its nature has to be pretty fragile. The most impressive thing is when you lift up the platter and there isn't any kind of belt, just what looks like a magnet and some coils. I think this was the first direct-drive record deck and although it has been imitated many times, the SL-1210 is still the de facto standard in the world of DJing. But this design comes at a cost. When I was looking to buy one, they cost £400. I scrimped and saved and finally got together the money. In fact I only paid £360, since I was paying in cash. But I only had one, which was pretty useless when you want to be a DJ. Which is where Ali comes in. Ali was a friend of mine at school. I don't remember when he told me he was HIV positive but it was pretty soon after meeting him. He was a haemophiliac and had contracted HIV from a contaminated clotting factor injection, before testing for HIV had begun. The government had compensated all those who had contracted HIV, so Ali was a well-off young man, though one with a potentially terminal illness. So Ali bought himself two Technics decks and even got them put in flight cases, for the professional DJ look. We weren't great friends at shool but we ended up at the same university (Newcastle) doing the same degree so ended up becoming good friends. We also started DJing together, after getting an amp and some very big speakers. We were a good pairing, he did bang bang dance music and I did Indie pop with some mainstream pop music. When I finished uni after four years, Ali had missed a year due to illness and was still trying to finish his degree off. He never got the chance to though. I last saw him during the Christmas holiday of '94. He was in bed and was unable to come out for Christmas drinks, but I presumed it would be another passing illness, like the ones in the past. But not long after he was dead. At his funeral, Ali's dad told me that Ali had left me his decks. After leaving uni, DJing had dried up, not least because Ali wasn't around to get the contacts, since he was always much more gregarious than me. But I obviously wasn't going to say no. So now I had three decks, which was too many even if I was still DJing. So I gave my SL-1210 to another of Ali's friends and kept the other two for myself. Time went on and the DJing thing was obviously never going to happen so I gave one of the decks to my brother Simon, with the proviso he didn't sell it on eBay. And the other one has been mostly been doing nothing but gather dust for the last ten years. CDs and MP3s have replaced records in my life. And now I have a shed and space to spare, so out has come the SL-1210. I cleaned it up and threw on one of my ten-year old records and the deck still works perfectly. So thank you Technics and thank you Ali.

Saturday, June 10, 2006

HTTPS and stupid error messages

Ever seen this dialog box appear in Internet Explorer? Chances are you have. And guess what, most people who see it don't have a clue what it means and just get scared that the website they are on isn't as secure as they thought it was. And know what else? It's wrong most of the time when it appears. It will appear when there is a standard link to a non-secure page. Yeh, the other page may not be secure but the user isn't looking at any content from that page. Since it will appear whenever there is a link to a non-secure page, it's very easy to accidentally add stuff to your page that will cause this to show up. I wonder how much time has been wasted by developers getting rid of this? And IE7 doesn't fix it either...

Wednesday, June 07, 2006

Server-side view state

There was a reason for adding the user-agent string to my ASP.NET error handling code, some errors are browser specific. One bug that has been biting me is view state getting corrupted. View state is a big chunk of base-64 encoded data that gets passed from the server to the browser in a hidden field. The browser does nothing with it (or at least it shouldn't) except pass it back to the server in a post-back. Unfortunately it seems some bug in FireFox causes the data to get corrupted for some of our pages which then causes our app to blow up when the data gets posted back. But more weirdly, there doesn't seem to be any mention of this bug when I Google for it. The conspiracy theorist in me thinks this may have something to do with the geek community not liking to say anything bad about FireFox, but it's more likely that this is is just a very uncommon bug. Anyhoo, there seems to be a pretty good fix for this. This article describes how to store view state server-side. Not only does this solve the problem, it also reduces page size quite dramatically. Of course the downside is that server memory usage is increased.

Monday, June 05, 2006

Handling errors in ASP.NET

It would be nice if my sites never crashed, but unfortunately they occasionally do. First off I don't want users to see the ugly ASP.NET error page, so I add this to web.config. <customErrors mode="RemoteOnly" defaultRedirect="error.html"> <error statusCode="404" redirect="index.aspx" /> </customErrors> I redirect to a HTML page, just in case ASP.NET is so ill it can't display an ASPX page (although whether it will be able to redirect at all is open to question). The exception to this is 404 errors which are redirected to the home page. The code below is what I use to handle unexpected errors. This is added into global.asax. Some people will say log errors to the event log but I prefer logging to the database because it's easier to configure. By default, the ASP.NET user can't write to the Application event log. You can set up your own event log, but again this requires some configuration. Most projects have a database back-end, so I just need to add another table. I also send out an email so if there is a serious problem, I get notified pretty quickly. Note, the error also includes the full URL so if a test server or development machine starts sending out error emails, it's clear it's not a real problem. You could also configure this from web.config. Update - Now logs more information about the error, such as the type of browser making the request and any session data. Any objects being stored in session data must have a sensible ToString() implementation
public static void LogError(string errorMessage)
{
// log error to database
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = ConfigurationSettings.AppSettings["connectionString"];
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO Log VALUES (GETDATE(), @Error)";
command.Parameters.Add("@Error", errorMessage);
command.ExecuteNonQuery();
}
}

// send email
SmtpMail.Send("webserver@initech.com", "doogal@doogal.co.uk", 
"IniTech web server information", errorMessage);
}


protected void Application_Error(Object sender, EventArgs e)
{
// log exceptions
Exception ex = Server.GetLastError().GetBaseException();

StringBuilder errorMessage = new StringBuilder();
errorMessage.AppendFormat("An unexpected error occurred{0}", Environment.NewLine);
errorMessage.AppendFormat("Type: {0}{1}", ex.GetType().ToString(), Environment.NewLine);
errorMessage.AppendFormat("Source: {0}{1}", Request.Url.AbsoluteUri, Environment.NewLine);
errorMessage.AppendFormat("Message: {0}{1}", ex.Message, Environment.NewLine);
errorMessage.AppendFormat("Browser: {0}{1}", Request.UserAgent, Environment.NewLine);
errorMessage.AppendFormat("Stack trace: {0}{1}{2}", Environment.NewLine, ex.StackTrace, Environment.NewLine);
errorMessage.AppendFormat("Session data: {0}", Environment.NewLine);
foreach (string key in Session)
{
  string val = string.Empty;
  if (Session[key] != null)
    val = Session[key].ToString();
    
  errorMessage.AppendFormat("  {0}: {1}{2}", key, val, Environment.NewLine);
}

LogError(errorMessage.ToString());
}

Friday, June 02, 2006

Skype rocks

OK, so I'm about 12 months late but I've only just got round to using Skype on a regular basis and it's great. Most importantly it just works. I've had long conversations with people in the US and Australia and it drops out very rarely. Compared to my experiences with Yahoo Messenger (where conversations tended to consist of 'what was that?', 'you still there?', 'I'll call you again, see if it's any better', 'can you type out whatever you're trying to say', 'I give up, I'll call you on the landline') this is very pleasing. Any negatives? Sending files can sometimes be very slow (but this is still better than MSN which sometimes just refuses to accept files) and I can't seem to find a way to search through past chats. And it's written in Delphi, so perhaps there is life in the old dog yet.