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

Saturday, June 10, 2006
HTTPS and stupid error messages

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.
Subscribe to:
Posts (Atom)