| |
I was working on an old (classic) ASP page the other day. It was pulling data from an Access database file and using an ODBC driver to get the connection. It was working fine on a Windows 2003 server, but when I pulled the file into a local website on my Windows 7 machine (with Office 2010 beta) it kept failing at the ODBC layer. The reported error message was : Microsoft OLE DB Provider for ODBC Drivers error ‘80004005’ [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified Looks like the driver specified in my connection string couldn’t be found. I was using the following : objConn.Open "DRIVER={Microsoft Access Driver (*.mdb)}; DBQ=c:\inetpub\wwwroot\pstdiscovery.mdb;"
This all looked correct and checking the excellent “ConnectionStrings.com” website they were saying the same thing – strange. It then struck me that I’m using Win 7 and Office 2010, either of which could have changed the ODBC driver or installed a new driver, so checking the “Data Sources (ODBC)” tool I see that the driver also works with .accdb files, so I’m guessing this is an updated driver.
Changing the connection string (adding the *.accdb) was the next step.
objConn.Open "DRIVER={Microsoft Access Driver (*.mdb, *.accdb)}; DBQ=c:\inetpub\wwwroot\pstdiscovery.mdb;"
Testing with this new connection string worked fine - problem solved….

GEO 51.4043502807617: -1.28752994537354
Recently I have been working with DotNetNuke. This is a superb open source CMS platform running on ASP.NET with a SQL back end, simple to install, easy to use and there is a thriving community around it. It is also available in a ‘Professional’ version which costs around £2000 per year and provides additional workflow features, a support contract and various other benefits. I choose not to go for the pro version, just because I don’t see much point in having some aspects of a website/webapp covered with a support agreement, but not others (any third party controls/extensions are not covered, and I had bought the excellent DataSprings Suite and was using multiple components from it). Anyway, when I had everything working the way I wanted, I started to look at performance. There are many websites providing tools and tips around maximizing performance of DNN sites – this includes caching strategies, output compression, turning off none essential ‘housekeeping tasks’ and the like. One of the tweaks (for low traffic sites) was to ‘kick’ the site every few minutes to ensure that IIS does not unload it (and therefore need to spin it up again the next time someone visits – this can take a good few seconds). My initial thoughts were around a scheduled PowerShell command – simple to come up a one liner to request a page and thus keep the web app in memory. (new-object “System.Net.WebClient”).DownloadString(“http://www.website.com/”)
Then I thought I might have it ‘kick’ a few websites, and make it configurable, so I started thinking ‘Windows Service’. It turns out that there are a load of these apps and services available to buy, some targeted specifically at DNN, some more generic. Reluctant to spend $20/year on a service I decided to craft my own.
It is basically a windows service, that reads an XML file for a timeout and a list of urls to kick. It implements a 1 second timer and counts down the timeout value, when it reaches 0 it kicks each of the urls (recording how long the response took). I haven’t done anything with the measured response time, but it would be fairly easy to write out to a DB or file for later analysis…
Here is a .zip file with everything you need – binaries, sample configuration file, install instructions and full source. It is Creative Commons license so knock yourself out.

GEO 51.4043197631836: -1.28760504722595
Yesterday on Leon's Blog, secretGeek, I noticed they had released v3.4 of TimeSnapper. One of the features that caught my eye was the ability to develop/add plugins to it.
I love plugins, I've written plugins for Windows Live Writer, Outlook, dasBlog, and more. Everything should have an SDK or plugin'able architecture. I championed it at work and we were one of the first Archiving Vendors with a 'real' SDK (I've built demo Vista Gadgets, integration scripts, federated search providers and PowerShell commandlets for it).
Anyway, the TimeSnapper plugin model looked really clean and easy to use. Read the one page description and your ready to go (didn't even download the sample code - it was so clear how things worked there was no need).
I wanted a little play around with it, so I though upload a snapshot to TwitPic would be a good idea. Opening a new project in Visual Studio, adding a reference to the ITimeSnapperPlugin.dll, create a new inherited class from ITimeSnapperPlugin and implement the interface :
#region ITimeSnapperPlugin Members
bool ITimeSnapperPlugin.Configurable
{
get { return true; }
}
void ITimeSnapperPlugin.Configure()
{
System.Windows.Forms.Form frm = new TwitPicPluginConfig();
frm.ShowDialog();
}
string ITimeSnapperPlugin.Description
{
get { return "Uploads snapshots to TwitPic"; }
}
string ITimeSnapperPlugin.FriendlyName
{
get { return "TwitPic Plugin"; }
}
object ITimeSnapperPlugin.HandleEvent(TimeSnapperEvent TimeSnapperEvent, EventArgs args)
{
switch (TimeSnapperEvent)
{
case TimeSnapperEvent.SnapshotSaved :
// upload it it to TwitPic
if (IsTimeToUpload())
{
string fileName = ((TimeSnapperPluginAPI.SnapshotSavedEventArgs)(args)).Activity.Filename;
Debug.WriteLine("Uploading " + fileName + " to TwitPic");
XmlDocument xmlDoc = UploadToTwitPic(fileName);
}
break;
default :
Debug.Assert(false, "Should never occur");
break;
}
return null;
}
TimeSnapperMenuItem[] ITimeSnapperPlugin.MenuItems()
{
return null;
}
Guid ITimeSnapperPlugin.PluginID
{
get { return new Guid("50744334-C5A0-44f1-BE64-5BBF32FDA79D"); }
}
TimeSnapperEvent[] ITimeSnapperPlugin.SubscribesTo()
{
return new TimeSnapperEvent[] { TimeSnapperEvent.SnapshotSaved };
}
All that was required was to give it a new Guid and name/description and then subscribe to the 'SnapShotSaved' event and handle the event when it was triggered. To get the image uploaded to TwitPic I used some code from the excellent Yedda Twitter C# Library (just the stuff for posting image data to a url). That all worked a breeze, but it was sending images (and posting to my twitter account) every 10 seconds (and of course it was hard coded to my username/password) - what was needed was a bit of configuration...
Luckily the plugin model provides an excellent and easy way to do this (set the Configurable property to true, and handle the Configure event). A bit more jiggery pokery, one modal dialog and an XML config file later, it was all working (configurable username, password, twitter message and frequency of updates) - although I really should do something better than store the username/password in clear text in an XML file...
If you want the plugin, just drop this dll into your %install%\plugins folder and restart TimeSnapper.
GEO: 51.4043006896973 : -1.28754603862762
Twitter is one of those applications / services that I've had trouble getting to grips with. For me it seems it's like shouting about what you are doing right now to a huge audience that is not listening. Who really cares that @kjhughes is heading to the shops to get some Mint sauce ?? Maybe I'm just not that kind of social animal, maybe I don't have enough friends using it, maybe I should 'but in' to other peoples conversations more, maybe I'm just plain boring... I do see a use for it though (for me). It's a pretty neat way to do some remote control stuff - like set up a Media Center recording, reboot my PC, and also it's a neat way of getting updates, like new blog comment received, TV recording completed and the like.... So, right now I need to get my Outlook addin project completed, but right afterwards I'm planning an app that interfaces to twitter and accepts direct tweets as remote control instructions, and also can update me on specific events. I am also thinking about adding twitter alerts to dasBlog (on comments, posts, errors, daily reports etc) I can see myself getting immersed in this twitter thing... GEO 51.4043197631836: -1.28760504722595
I've been toying around with creating an Outlook addin recently that adds a new panel to the main Outlook inspector window. Actually it is going to be a 'folding' or 'collapsing' windows similar to the docked ToolWindows in Visual Studio. Anyway, the stuff around getting the correct window handle for the Outlook inspector window and resizing it to allow some space to insert my new panel was fairly simple, but I also had to hook into the message look of the window so that I could capture any move or resize messages and so resize things again to accommodate my panel. I had started developing it as an Outlook add-in but to speed things up (and prevent me having to register / unregister the addin between tests etc I decided to develop it as a separate app- I could still hook into Outlook and move things around. The problem came when I was trying to hook the inspector window message loop - it didn't seem to work. I fired up Spy++ and check that the inspector window was getting the messages - which it was - but they were never getting delivered to my hook code. I was using System.Windows.Form.NativeWindow and overriding the WndProc function (see my NativeWindowListener class below). To get the hook in place I was creating a new NativeWindowListener class with the inspector window handle as the parameter. Checking the handles and the delegates etc it all seemed to be correct, I just couldn't fathom why it wasn't getting any of the messages. Then the penny dropped. Outlook is in a separate process... (NativeWindow only works across the current process and window handles that are contained in it). What would be needed in this case is a system wide hook, that requires use of the Win32API and interop. I reckon this is overkill just to save a bit of development pain, so will probably move to something where the Outlook addin dynamically loads the panel in and make something available to have the add-in reload the panel - this should allow me to develop the panel code (the majority of the app) without worrying to much about the Outlook addin side of things. using System;
using System.Windows.Forms;
using System.Text;
namespace Outlook_Subclasser
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class NativeWindowListener : NativeWindow
{
// Constant value was found in the "windows.h" header file.
private const int WM_SIZE = 0x0005;
public NativeWindowListener(IntPtr handle)
{
AssignHandle(handle);
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message msg)
{
switch (msg.Msg)
{
case WM_SIZE:
// do your processing in here
break;
default:
break;
}
base.WndProc(ref msg);
}
}
}
and then to use the class you would find the handle of the window you want to subclass and create a new NativeWindowsListener passing in that handle to the constructor. Note - this does not cover a number of aspects around releasing handles etc - you will want to make sure these are covered off... MyForm myForm = new MyForm(); NativeWindowListener nwl = new NativeWindowListener(myForm.Handle);
GEO 51.4043197631836:-1.28760504722595
I've had a lot of interest in my SMS Gateway app since this post. SMS gateway consist of two components :
SMS Gateway Addin This is an Outlook addin that adds a new toolbar to your Outlook instance. The toolbar allow the user to choose a mobile / cellphone number (from their contacts) and enter a message. When they hit the enter key after the message (or click 'Send') a new Task is created when has a subject of 'SECRETCODE' and the mobile / cellphone number and the details being the text of the message.
At some stage this Outlook Task is synchronized (over ActiveSync / Direct Push) to a Windows mobile device...
SMSGateway This is a small app running on a Windows Mobile device that every 15 seconds checks through the tasks. If it finds any tasks that have a subject beginning with SECRETCODE then it parses out the mobile / cellphone number and sends the message text (from the Task details) to that mobile / cellphone number via SMS. Note: the SECRETCODE word is configurable.
Why develop this ? The purpose of this app was really to allow me to send SMS messages easily from Outlook without having to sign up for (and pay for) a web to SMS service (I already get 100's of free SMS messages with my mobile / cellphone package).
The application is free for anyone to use (drop me a line - in the comments - if you do use it...)
Windows Mobile CAB file (copy the file to your Windows Mobile device and click on it) : SMSGatewayMobile.CAB Setup file for the Outlook 2003 Add-in (Outlook 2007 coming soon) : SMSGatewayAddin2003.msi Source for both the applications : SMSGateway.zip (includes test Outlook 2007 addin code)
I'd be really interested to hear from anyone using this.... post in the comments...
GEO 51.4043197631836: -1.28760504722595
Recently I have wanted to be able to send SMS messages to my contacts without having to pick up my phone and mess around with the mini keyboard (I wanted to do it directly from PC, but have the message still send by the phone...
There are solutions around that allow you to send a SMS from a form on a web page, but these are generally paid for services or limited to XX messages per day - I have a mobile contract with unlimited texting, so why would I want to pay for another service, and I didn't want to be limited in the volume of texts I can send per day. there are also solutions around that require the device to be docked / plugged into the PC, I didn't like this either as it's just too much hassle (and therefore I never do it)...
So I came up with a simple but effective solution that makes use of ActiveSync / push email technology that is built into Windows Mobile devices.
Basically I wrote an Outlook Addin that allows me to choose a contact from a drop down list, type in the message and "send" it - when I say "send" it, what I mean is the request is transferred to the WM6 device which then sends the actual SMS message.
So it goes like this :-
- User chooses the contact from a drop down list of all contacts with mobile numbers.
- User enters the text of the message
- User clicks send
- Outlook Addin creates a new task with a secret keyword followed by the mobile number as the subject and the message in the body of the task.
- (after a few seconds) the new task is synchronized to the WM6 device via push email / ActiveSync
- WM6 device regularly checks the tasks list for a task with a subject that starts with the secret keyword
- The subject line is parsed to get the mobile number the text is to be sent to
- The body is parsed to get the text of the message
- A SMS message is send from the WM6 device.
- The new task is deleted (or marked as complete)
- The change to the task (delete or marked as complete) is synchronized back to the PC via ActiveSync / push email.
I'll have more details, the installers and all the source code available next week...
UPDATE: See the follow up to this article here which includes binary files and full source code.
GEO 51.4043197631836: -1.28760504722595
I was listening to one of Scott Hanselman's podcasts this morning and Scott made a comment about people thinking that agile methodologies were and excuse for sloppy project management (NOTE: Scott wasn't of this opinion, he just stated that some other people thought this).
I am fully bought into SCRUM, and whilst we don't implement if fully at work (we have our own handcrafted way of doing it that really works well for us) I think it brings great benefits. One of the real stumbling blocks with people adopting SCRUM / Agile is getting over the mind set that they can determine the delivery dates and that they can force the development team into delivering everything they want by that date. I truly do not believe that it is possible to fix both scope (requirements) and timescale (delivery dates) in a software project, even having done lots of design up front. I have written before on the perils of this approach...
Giving up all control of dates is something that takes quite a psychological leap of getting used to, but when you are there, you see all the benefits. Let me give you an example...
Think of The Beatles. Their 'products' were (obviously) their songs, they were all passionate about what they were doing (providing great music to the masses) and they did it really well - everyone loved their product. When Lennon and McCartney (and Harrison) sat down to write a song, if they'd had a limited time then the quality would have suffered. They were not limited in time and they produced great things.
That is one of the things you have to accept in agile - crafting great software products is an skill and an art, for the developers sometimes their creative juices flow in abundance, sometimes they don't. To meet strict timescales, something has to give, and that something is scope (or quality, but you don't really want that do you ??...)
GEO 51.4043197631836: -1.28760504722595
Originally I had my website set up to default to my blog (so dasBlog was installed at the website root level instead of in a /blog subfolder). Recently I have been writing some Web Applications and wanted to restructure my website so that there is a subfolder for each app / main area (blog, projects, etc). The problem being that all the links out there point to http://www.kapie.com/2007/12/19/RedirectorHttpModule.aspx and these would all be dead link when moved to http://www.kapie.com/blog/2007/12/19/RedirectorHttpModule.aspx, so I needed to respond with a HTTP 302 to the original links and tell the client that it should temporarily redirect to the new location (changing to a HTTP 301 when I am happy that it is all working). This called for a HttpModule... namespace KSL { public class BlogRedirector : IHttpModule {
private EventHandler onBeginRequest;
public BlogRedirector() { onBeginRequest = new EventHandler(this.HandleBeginRequest); } void IHttpModule.Dispose() { } void IHttpModule.Init(HttpApplication context) { context.BeginRequest += onBeginRequest; }
private void HandleBeginRequest( object sender, EventArgs evargs ) { HttpApplication app = sender as HttpApplication; if ( app != null ) { HttpContext context = app.Context; string host = app.Request.Url.Host; string requestUrl = app.Request.Url.PathAndQuery;
if (requestUrl.Contains(@"/blog/") || requestUrl.Contains(@"/foo1/") || requestUrl.Contains(@"/foo2/") || requestUrl.Contains(@"/foo3/") || requestUrl.Contains(@"/foo4/") || (requestUrl.ToLower() == app.Request.Url.Scheme + @"://" + host + @"/default.aspx")) { return; } else { Uri newURL = new Uri(app.Request.Url.Scheme + @"://" + host + @"/blog" + requestUrl); context.Response.RedirectLocation = newURL.ToString(); context.Response.StatusCode = 302; context.Response.End(); return; } } } } }
This basically allows me to redirect any url that does not contain /blog/ or /foo*/ or is not /default.aspx by sending a 302 status code and adding /blog/ to the url to generate the redirect url. The compiled DLLs were stored in the bin folder off the root and the web.config in the root needed the following adding... <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <httpModules> <add type="KSL.BlogRedirector,BlogRedirector" name="BlogRedirector" /> </httpModules> </system.web> </configuration>
This seemed to work fine some of the time, but was a bit hit and miss.... Further investigation pointed me at the web.config files in the subfolders and that they would inherit the sections from the root web.config. So all the subfolder web.config files got the following added. <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <httpModules> <remove name="BlogRedirector" /> </httpModules> </system.web> </configuration>
GEO 51.4043197631836: -1.28760504722595
Just recently we have been interviewing for a new developer.
I have found it incredulous, the amount of people with considerable experience that cannot do hex / dec conversions. One of the things we do during an interview is to have people write code - this is normally a short 10 minute test where we give them an app with one function that converts a hex string to decimal, we ask them to refactor the function so that it no longer using the API call to do the conversion (they have to do it themselves in code).
We go to great lengths to make sure we have explained the task correctly, sometimes we prompt them earlier in the interview with a questions about converting - 'What is 16 represented in Hex, what is 30 hex in decimal etc). We even provide a working application and allow them to play with it for a few minutes to familiarize themselves with it's intent and operation.
The task is designed to show us the coding style of the candidate, but we've found that instead of looking at their code we're looking at blank pages - there seems to be a glut of people (at least, that I am interviewing) that don't even know how to start. We've had people simply stare at us blankly, people just sit and twiddle a pencil, people get flustered and simply say 'I cannot do it, sorry'
Are we just getting below average or unsuitable candidates or have people forgotten this skill (working in number systems other than base 10). Have today's development tools abstracted all this low level stuff away so much that people just don't need it anymore (and therefore have teaching institutes stopped covering it) ??
GEO 42.2814047728377:-71.5726983547211
I have completed stage one of GeoRss enabling dasBlog.
In the config page I added some options for enabling GeoRss, specifying a default lat/long and enabling ‘integration’ with Google maps. There is also an option to use the default lat/long for any non geocoded posts.
If GeoRss is enabled then the edit entry screen provides textboxes to allow specifying lat/long (populated with defaults from config page).
If the google maps integration is enabled then you’ll get the ‘Show Map’ button and clicking it will display a map which you can move around until you find the location and then click on the location to get the lat/long texboxes populated.
If you have existing non geocoded posts then you can have the default lat/long added to those if you wish.
I puzzled around for ages when trying to display the georss in google (http://maps.google.com/maps?q=yourfeedaddress) – it kept telling me that the feed was invalid. I eventually found that feedburner was adding <atom10:link blah blah /> to the xml which for some reason google maps thinks is invalid. The only way I could find to prevent feedburner adding the atom link was to turn OFF the ‘Browser Friendly’ feature in feedburner.
So – stage 2...
The work I still want to do with this is basically to add macros to get lat/long - fairly easy I guess, and then some way to specify lat/long from Windows Live Writer (and other offline blog clients) – a little more complex. Scott mentioned a geo microformat and from my initial looks seems to be a good route to take - watch this space...
Now it is simply a case of retrofitting the geo info into all my old posts...
I registered for Mix:UK a few weeks ago and today got an email from the Mix team about a social networking tool they are using to help people make the most of the conference.
They are using backnetwork, which allows creation of profiles, blogging, photos, adding friends / colleagues etc (all the usual social networking stuff). It also allows you to add any colleagues / friends automagically from any previous backnetwork events you have been to.
This is certainly a step in the right direction, but it's missing an 'auto-suggest relationship' feature that could analyze your profile (or tags) and suggest other people who have similar interests.
So... my profile is here, anyone interested in any of my tags feel free to contact me and set up a meeting/chat/lunch/coffee/whatever...
Some of the code I have for importing data (from ACT! 2000) to MS CRM creates new 'PhoneCall' activities / objects. The problem is, that it seems MSCRM does not allow you to programmatically modify the 'create date'.
Here is the code I use...
CrmDateTime start = new CrmDateTime();
start.Value = DateTime.Parse("10/08/2005 12:30");
pc.actualstart = start;
pc.scheduledstart = start;
CrmDateTime end = new CrmDateTime();
end.Value = DateTime.Parse("10/08/2005 14:30");
pc.actualend = end;
pc.scheduledend = end;
pc.subject = "Phone call regarding sales of Widgets Q2/2005"); ***
string desc = "Start : " + pc.actualstart.Value + "\n";
desc += "End : " + pc.actualstart.Value + "\n\n"; ***
desc += "The details of the phone call go in here");
pc.description = desc;
pc.regardingobjectid = new Lookup();
pc.regardingobjectid.type = EntityName.contact.ToString();
Guid contactGuid = new Guid(guidOfContactWeTelephoned);
pc.regardingobjectid.Value = contactGuid;
The actualstart and scheduledstart (and ends) get populated with the current datetime (this seems to happen if the time they are set to is in the past).
Note the two lines between the ***'s - this is my solution / workaround and simply include the start/end times as text in the body/description of the phone call object.
So dasBlog 2.0 (running under AS.NET 2.0 and medium trust) is now released. Scott has a great write up on it. I have been running this web site on it for a few weeks now and it's been fine. We sneaked in a couple of last minute features (one of mine was a new macro for rendering the navigation links as an Unordered List instead of a table with one cell per row). I've been spending a fair bit of time on dasBlog recently - extending activity Rss feed and working on georss integration. I have a georss version working with a simple UI allowing the user to specify Lat/Long for each post and then a link to view the feed items in Google maps. I have not checked it in yet as I want to provide a slicker UI for specifying the location (maybe a map that allows you to drag/select the location or enter a zip code and view the location then use that). I also want to provide options for getting the location in when using blogging clients (Windows Live Writer etc), not just the web based 'add entry' UI. There is also some great work going on with Clemens Vaster and other really revamping some of the architecture bits and bringing dasBlog to the next level. Watch this space...
I'm in Western Mass (Westboro, MA) again this week and while that normally means 16 hour days (there is nothing much else to do but sit around a hotel room so why not...), this week I decided to work on a personal project for a bit.
Part of it was some code to check a machines public IP address and update it to DynDNS. Luckily they have an excellent developers / API section that explains everything you need to do this. There are two sections to it, detecting the machines public IP address and updating the hostnames associated with your account, both of which they provide a service for.
It's pretty easy to follow and within an hour or so I had come up with a class to do both tasks. Here is a snippet for the 'CheckIP' function:
public static string CheckIP()
{
// check the current public IP address
string ipString = string.Empty;
WebClient wc = new WebClient();
try
{
string result = wc.DownloadString("http://checkip.dyndns.com");
return ParseCheckResult(result);
}
catch (WebException)
{
ipString = string.Empty;
}
return ipString;
}
Just call a URL (http://checkip.dyndns.com) and it returns your public IP address, there was some parsing of the return text but it is pretty simple. Click the link and see for yourself.
Next was the 'UpdateIP' function, here's the snippet:
public static string UpdateIP(string username, string password, string ipaddr, string hosts)
{
// check the current public IP address against what we want to update to
string updateurl = "http://members.dyndns.org/nic/update?hostname";
string result = string.Empty;
WebClient wc = new WebClient();
string url = string.Format(@"{0}={1}&myip={2}&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG", updateurl, hosts, ipaddr);
try
{
wc.Credentials = new NetworkCredential(username, password);
wc.Headers.Add("User-Agent", "KSL - WHS Updater - 1.0");
It is basically just a case of passing a querystring with all the details to http://members.dyndns.org/nic/update , ensuring you set the credentials to your DynDNS account username and password and specifying a unique User-Agent.
The complete project files (including a small 'user' application to test it) can be found here (61K).
So, this is something I learned on the Certified ScrumMaster course by Danube Technologies.
It really clarified for me that any sense of control over software deadlines is, pretty much, only a 'perceived' sense of control.
Yes, we can strongarm, use peer pressure or rigid authority to make developers work longer hours, but then they spent more time idling during the day. Someone, somewhere has written a great article on it (Note, after spending an hour looking at Joel On Software thinking the link I wanted was there: Link it here when found)
Anyway, the graph - let me explain....
A development team has a normal work rate (given all things are equal, interruptions, bug fix workload, training , yadda yadda...) in this case the work rate is 30 'units' in a 15 day period (the red line). Supposing we 'force' them to up their productivity to 40 units in a 15 day period (dotted green line). They will work at their normal rate initially and then soon recognise that this will not meet the deadline - at that point they 'speed up' (this is the change in the solid green line at the 7 day point).
What you have actually done (most likely) is generated a 'technical debt' - because the way the developers 'sped up' (wrote more code) was by sacrificing some other aspect of the development (quality, test coverage, peer reviews etc).
So you have a 'technical debt' that needs to be made up during the next period. Even if we go back to the normal rate of work we are not going to get 30 units done in the 15 days, oh no, first we have to make up the debt, then start on the 30 units - which will likely need 'speeding up' to meet deadline.
What is worse though, is that now you have set the expectation that the team can deliver 40 units in the 15 day period, so your next schedule had 40 units factored in, and the next, and the next until ..... well, it's like spending more on your credit card every month than you are paying off, at some point you can go no further .
The moral of all of this ? Who knows... get a card with a long 0% balance transfer rate ??
So I just spent two days in London on a 'Certified ScrumMaster' training course run by Danube Technologies.
The instructor was a Dan Rawsthorne and although he liked to reference things to the military, he covered a great deal of topics and had an incredible wealth of experience - some of the real world example really struck home with me.
It seems that a common trait is people just not grasping the whole concept of (basically) handing over control to the development team. There were many questions about where the project manager fitted in, who really managed the team etc etc.
It is tough to grasp if you currently have the perception of 'having control of the process at the moment'. It suddenly hit home for me as I was traveling into London on the train for the second day and thinking about why this current round of QA release testing was taking so long.
According to the QA guys, the build they had been given originally for this release was pretty poor quality (thankfully sorted out now). I was wondering why and thinking back to when this release was being coded and realized that at the time I was pressuring he developers because we were 100 odd hours behind schedule. We managed to get back on track and I 'thought' the reason was because I had beaten/pushed/cajoled/whatever the guys into increasing their work rate.
In reality, what had happened was that I had forced them to deliver early and the way this was done was by sacrificing quality (you cannot change the laws of dynamics - right). The fact was we did not get visibility of that until further down the line in the testing stage.
So, have a think about whether you do actually have control, or are you just exerting force on one particular aspect of the 'whole' that is squeezing/reducing another aspect of the same 'whole'.
When this clicked in my head, it all made sense - the SCRUM methodology became clearer, what needing doing to get it working, why it worked (especially, why it isn't a bad thing to hand 'control' over to the team).
One of the slide deck was about 'technical debt' and it really drives this point (sacrificing something for earlier delivery) home - I'll blog about that later..
Recently I was reviewing the progress I had made with the development team (at work) since to took over responsibility for them around 8 months ago. It is actually pretty impressive (even if I do say so myself):
- Agreed and published a set of common / shared values and mission statement (gives shared ownership and a sense of responsibility)
- Upgraded to Visual Studio 2005 - providing them with the best tools to do their jobs (improves productivity).
- Migrated from Visual SourceSafe to Team Foundation Server which is much more stable and has better branching and merging facilities (improves productivity).
- Implemented an overnight build process and some ownership/responsibility for 'breaking' the overnight build (improves quality).
- Implemented a system that allows each developer to build in one click (improves productivity).
- Implemented automated testing of the overnight builds and any build version that leaves the building to go to customers (improves quality).
- Provided them all with dual monitor configurations and larger LCD screens (improves productivity).
- Automated the build/version number change (improves quality and tracking).
- Implemented new processes for how we ensure bug fixes are rolled into future version (improves productivity and quality).
- Automated most of the help / manual / documentation build process (improves productivity).
- Provided them with 64 bit servers for testing / virtualization (improves productivity).
- Implemented SCRUM (more like a watered down flavour of it) thus giving more visibility to deadlines/timescales and goals/objectives (gives shared ownership and a sense of responsibility)
- Documented a technology vision statement that outlines the technologies, processes and our 'mantra' at a high level while allowing the individuals to interpret the implementation at the next level down. (gives shared ownership and a sense of responsibility)
- Implemented a real bug tracking database (improves quality)
There are two things about this though:
- When I say 'implemented' or 'upgraded' etc what I really mean is the team did it, my involvement in most of these items has been to question if we had it, how could we do it (better), did we need it, how did other people in the industry solve those kind of problems.
- When I say 'improves productivity' this is purely based on a 'gut feel' measurement (and often general consensus from the guys working in/with it), some of the items I know for sure (like the time SourceSafe corrupted on us and two guys spent hours with print outs and pens ticking off the files as we manually merged them, and reducing the several hour version change process where we manually edited the version is 2976429865264264 files and the kicked off a build, to 20 minutes)
This is not me blowing my own trumpet; I didn't really do much of this except ask the questions that kicked of the activity.
When was the last time you questioned what you are doing or compared it to what / how others in the industry are doing it ?
I use dasBlog as the vehicle/technology for this site and currently have it configured at the root of the website (so you go to http://www.kapie.com/blog and you get directly into this blog). This is kind of an unusual setup as most people put it in a /blog subdirectory or virtual directory. I have plans to put a fair bit more static content up so I wanted to change to the standard of /blog subfolder - the problem is that all my current links will fail when I move the content along with all the content google has cached / indexed about the site - Not Good. Scott Hanselman suggested in response to a question I posed on the dasBlog mailing list that writing a HttpModule with a HTTP temporary redirect (HTTP response code 302) might be the way to go and reckoned it might only be a handful of lines of code.... I was pleasantly surprised at how easy it was... I have included the code below in case anyone else might find it useful. It is pretty specific at the moment (takes the requested URL and if it does not include /blog/ then it insert it) but it could easily by made generic (RegEx or the like). Happy to share the actual source files if anyone wants them - drop me a mail/comment. using System; using System.Web; using System.Text; namespace KH { public class Redirector : IHttpModule { private EventHandler onBeginRequest; public Redirector() { onBeginRequest = new EventHandler(this.HandleBeginRequest); } void IHttpModule.Dispose() { } void IHttpModule.Init(HttpApplication context) { context.BeginRequest += onBeginRequest; } private void HandleBeginRequest(object sender, EventArgs evargs) { HttpApplication app = sender as HttpApplication; if (app != null) { string host = app.Request.Url.Host; string requestUrl = app.Request.Url.PathAndQuery; if (requestUrl.IndexOf("/blog/") == -1) { Uri newURL = new Uri(app.Request.Url.Scheme + "://www." + host + "/blog" + requestUrl); app.Context.Response.RedirectLocation = newURL.ToString(); app.Context.Response.StatusCode = 302; app.Context.Response.End(); return; } else { return; } } } } }
This week (so far) has been good - in terms of completing things, productivity and new products. First off, Microsoft finally released PowerShell for Vista. No more having to 'play' on my old lab machine to get to grips with this stuff. There seem to be a number of people reporting failed installs(due to EFS encryption being disabled), just read the comments of the PowerShell blog announcement. Next, we're just coming to the final couple of days of a 'Supporting Exchange 2007, Office 2007 and Vista SPRINT' at work (we use a form of SCRUM as our development process) - all is looking good and we have beta sites lined up. Then, I noticed Eileen's (the most communicative Microsoft employee on the planet) post about Office 2003 to Office 2007 command references. An interactive demo from Microsoft when you can click the toolbars and menus of an Office 2003 application and it tells you how to find the equivalent command/function in Office 2007. I spent some time finding the 10 or so commands I'd been having difficulty with and increased my productivity. Here's her post : http://blogs.technet.com/eileen_brown/archive/2007/01/31/old-to-new-reference-guides.aspx Then late last night (again at work) we just completed our internal testing before sending our Archive One product for Microsoft Platform testing. We are testing against 5 of the 6 platform tests (we don't fit into the 'Managed Code' test category as we make extensive use of MAPI which basically requires C++ / Unmanaged code)
One of the difficulties I have encountered whilst trying to change the mindset of our development team is getting buy in for the concept of 'Committing to backlog items'. This is where the development team get together with the Product Manager(s) in a time boxed (8 hour) meeting (split into 2 sets of 4 hours, the first one to discuss requirements and the second one to discuss / agree what they will commit to doing in the 30 day Sprint). I've been thinking about this a lot recently - I really want the guys to fully embrace SCRUM (and they want to also) but this particular concept is proving a difficult hurdle to overcome. I think that the root of this is that the team had been working in a classic Waterfall methodology (and had been burnt a number of times on late delivery of features / releases). The word commit seems to be the problem. - Their current understanding / interpretation of commit is : "it must be done or we're going to take heat over late delivery".
- Whereas the agile understanding / interpretation of commit is : "we will do everything we reasonably can to deliver the feature / release"
It's actually quite a big step / leap of faith if you have only been used to "it must be done by dd/mm/yy". I like it's going to require me forcing the issue so that we do one Sprint in this manner and then demonstrates to everyone that there is no blamestorming if we're late or drop features The reason (I suspect) that it works is that with SCRUM you get continual and accurate feedback from the daily Sprint meetings, so if things do go off track then it's visible and corrective action can be taken earlier.
At work I have been the champion of an Integration Framework for our Archive One product. This is a set of API's, events and open file formats embedded into the Archive One products that allow customization 'around the edge'. The kind of thing that can be done with the Integration Framework is running searches, interworking with the AD schema, running archive policies, integration into Network Management Systems or Document Management Systems and the like - it really opens the product to other business systems as either the slave (other systems controlling Archive One) or the master (other systems being controlled by Archive One). We have been writing up the API documentation recently and had initially started this as a (Word) document we would publish to customers and developers. I looked into NDoc which now seems dead (at least for VS2005 and .NET2.0) and then came across Sandcastle. Sandcastle is Microsoft's answer to NDoc, it allows the collation and generation of documentation from the XML comments embedded in your code Right now it is at CTP stage and is command line driven - a few projects have sprung up which provide a UI for it (backing on to the CLI). One is a VS2005 add in (which I installed but it wasn't immediately obvious how to use it, so I didn't really pursue) and other is SandcastleGUI - this provides a single form where you choose the options, point it to the folders containing the Sandcastle app, the code DLLs and where you want the docs to end up and then simply kick it off. When you hit 'Start Documenting' it closes the form and fires off a command prompt window (batch files) which in turn fires off other command prompt windows until it's collated everything and built the documentation. It allows you to generate html documents - a whole website, multiple pages, images etc or a compiled help file (.chm) or even both if you wish. Another of the options is 'Online MSDN Links' this provides a MSDN link for every object type it can find in the base class library. Be warned enabling this option means it has to reflect through about 14500 classes / objects in the framework so that they can be indexed and used if found in your app / library. Enabling this option increases the time to generate from about 1 minute (on my Dual Core laptop) to about 15 minutes - I'd recommend disabling this option whilst developing the app / library and only enable it when you are doing the final or release build. The resulting documentation is really good quality - pretty much the same format as MSDN documentation. For the effort of a few extra XML comment lines in your code this provides great value and a very comprehensive resulting document. The easiest way I have found to integrate this into VS2005 is to set it up as an External Tool. I create a folder under the solution folder named 'Documentation', I run SandcastleGUI manually the first time around, do all the required configuration and then save the settings to a file in the solution directory (don't save it in the 'Documentation' folder as that is cleared out with each Sandcastle run. I have an 'External Tool' configured which points to SandcastleGUI with parameters of : /document $(ProjectDir)\settings.SandcastleGUI (I called the settings file 'settings.SandcastleGUI') and called the tool : Sandcastle This Project This allows me to simply click this tool and it will go away and create all the documentation (with my chosen parameters) for the project. As I mentioned this is CTP at the moment - I look forward to the release version, hopefully fully integrated into Visual Studio and stylesheets that exactly mimic MSDN.
At work we are heavily into MAPI (we provide email archiving solutions). MAPI is Microsoft's mainstream supported and recommended protocol for accessing Exchange. There are currently two main versions of MAPI - 'Exchange MAPI' and 'Outlook MAPI'. Exchange MAPI is the version that is installed when you install Exchange System Manager (ESM) and is the recommended version to use for enterprise applications that work with Exchange. Outlook MAPI is the version that is installed when you install Outlook - it is NOT recommended for enterprise applications. Now, there are also two types of PST files - the original Outlook 97-2002 version and the newer Outlook 2003 Unicode version (Outlook 2003 supports both versions, but no other version of Outlook supports the Unicode version). The problem is that the ESM version of MAPI does not support the 2003 / Unicode version of PST files either. This is real pain, meaning that we have to use the ESM version (if we want to go with Microsoft's best practices) for our application, but if we want to support the Unicode PSTs we need to somehow use the Outlook version (NOTE: Microsoft absolutely do NOT support both versions of MAPI installed on the same machine). Apparently the new MAPI 2007 version (that will be downloadable for use with Exchange 2007) was initially slated to support both, but, I've since heard this will not be the case. So currently (and possibly in Exchange / Outlook 2007), the only way to work with Unicode PSTs is via Outlook MAPI.
In the first version of my GPS Library software (written in VB6) I had a 'Protocol' property that indicated which protocol we were using (for example Garmin, Magellan, NMEA etc). Then, every time we needed to do something at the protocol layer (Sending or Receiving data) we had an if statement :
If gps.Protocol = GpsProtocol.Garmin Then ' Do it this way EsleIf gps.Protocol = GpsProtocol.Magellan Then ' Do it that way Else ' Do it the other way
Horrible and messy !! Adding a new protocol meant going back to every function and amending it.
In the new .NET version, it's much more object oriented and I've used a number of software design patterns.
To solve the above problem I used a Factory pattern. The Factory pattern allows you to create objects but let the client decide what type those objects should be. So, I can basically let the client set the Protocol property and then, depending on that setting create a different object for each protocol.
I start with a base class that all the 'codec' classes inherit from, then in the 'creation' (when the client creates the object) I look at the Protocol property and choose which concrete class to return:
GpsCodec is the base class and the concrete classes are GarminCodec, MagellanCodec etc. I have a static method in the base class called 'CreateCodec' that takes a 'Protocol' parameter. So it looks like this
public static GpsCodec CreateCodec(GpsProtocol protocol) { switch (protocol) { case GpsProtocol.Garmin: return new GarminCodec(); break; case GpsProtocol.Magellan: return new MagellanCodec(); break; default: return null; // error break; } }
So now I can simply create a codec class based on the Protocol property and use that for all the communication to / from the device. Adding a new protocol is simply a case of creating the new class and modifying the CreateCodec method - much simpler.
Watch this space for the free .NET GPS Library (including source).
In the first version of my GPS Library (written in VB6) I had a big horrible multiple nested if statement that handled the intelligence behind what to do when a new frame of data was received - if we were sending Waypoints to the GPS and the frame was an Acknowledgement (ACK) then we would send the next one, if it was a Not Acknowledgement (NACK) then we'd retransmit the last one - everything that happened depending on the current context (what we are currently doing). As there were about 20 different things we could be doing at the time and about 20 odd frame types, this was real messy and probably had a Cyclometric Complexity of 9292736592 (or more)...
My .NET version of the GPS Library makes extensive use of Software Design Patterns - to solve the problem above I used a 'State' pattern.
The State pattern is used where your software has multiple states and various actions or stimulations that make it change between states (but it is always going to be in one of the predefined states). I guess it a pretty common pattern for communication systems.
I have a base class called GpsState, that contains all an (overrideable) method for each action and stimulation and each method has a default implementation of something like
throw new Exception("Invalid action for state :" + this.StateName);
All the state classes inherit from this base state class.
Lets take (much simplified) sending Waypoints as the example:-
The GPS object has a reference to the current state class. This starts off in the stateIdle (doing nothing). We then call the stateIdle.WriteWaypoints() function. In this function we send the first Waypoint and change the state to stateWritingWaypoints. The stateWritingWaypoints overrides the 'GotACK' method and provides it own implementation (which is simply to send the next Waypoint). If any of the other methods are called whilst in the stateWritingWaypoints then the implementation from the base class is called and the caller receives an exception. When I've sent the last Waypoint then I change state back to stateIdle so that other actions can be called again
So my class for stateWritingWaypoints handles writing Waypoints and nothing else - this makes the code really clean and simple. The downside is that you end up with many more (small) classes in your code.
Watch this space for the source to the free .NET GPS Library.
About 4 or 5 years ago I wrote a GPS ActiveX control in VB6.0. Although it work really well (and fast), it was difficult to maintain - to be honest it was pretty messy, a large monolith with some real horrible 283654276354 line functions. I sold it as Kapie Systems for a while and had some good success with it, it was mentioned at a couple of GIS conferences etc... Anyway, I had been meaning to update it (to .NET) for sometime, but I wanted to do it right, make it easy to extend (to plug-in other GPS protocols) and easy to maintain. I just got a basic .NET implementation of it working last night (at last !!). It's Garmin propriety protocol only at the moment, but the whole framework is there for extending to other protocols (which I'll be adding shortly). I also added import and export to GPX format, which is a great portable standard - the GIS / Mapping community has been crying out for this for ages. I expect to have something releasable in a few weeks (it will be open source) but if anyone wants an early view or wants to discuss my plans ideas then let me know.
This is the forth (and last for the moment) article on Writing Supportable Software, helping all those customers and support engineers work with our software products and troubleshoot them themselves (i.e. going as far as possible down the troubleshoot path before referring it back to you). You can the first article here... and the second article here... and the third article here... This final article is all about paranoia and the fact that customers (as much as we love them, and need them to pay our salaries) are not to be trusted one little bit... We've all been there, troubleshooting a problem, ask the customer what parameter X is set to, go off down a whole path of investigation, find nothing, scratch our head, check parameter X for ourselves and find it's set to something different (and the whole problem resolution falls into place). It's not that customer purposely try to be difficult, simply that they don't know the system / software as well as you do - quickfire questions about the Primary Master Lookup Port and the Lookup Master Access Port get them all confused - they're probably under pressure for their boss to get it fixed, they've tried a whole load of things, the last time they looked it was set to 5465ABBG_Master but in the confusion they forgot they changed it to Lets_Just_Try_This_Option. They best way to determine what variable / options / configuration your system is using, is to write it out to a trace file, on start up, before a processing run, before you use it (whenever is appropriate - and that means, if it could have been changed between now and last time we wrote it to the log then write it again) If ever there has been a thing that wastes the time of support people it is working on the assumption that, the value the customer told them it was set to was the actual value that it was set to.
This is the third article on Writing Supportable Software, helping all those customers and support engineers work with our software products and troubleshoot them themselves (i.e. going as far as possible down the troubleshoot path before referring it back to you). You can the first article here... and the second article here... This article is all about identifying when and how you are using external systems and services. Most enterprise application work with external technologies or services, for example the company I work for (C2C) build applications that work with Microsoft Exchange Server, maybe you consume a particular web service or make LDAP calls. Whilst you can reasonably expect the customer to provide a stable working environment for your system / software, life is not always that rosy. I've worked with Enterprise systems that have been dependant on SQL Server, Exchange, LDAP, Web Services, VoIP gateways and many other systems... For experience, there is nothing that irks a customer more than a vendor who points the finger blindly at some part of the environment or system infrastructure. As a support guy for a vendor you have to take the pain and go that extra mile to hold the customers hand and demonstrate how / why / where the environment is going wrong. Developers can make this considerably easier by identifying (in a log file or the like) exactly what services your calling out to - and how long it's taking. I can't tell you how many times I've been troubleshooting an issue only to eventually end up some service the software was expecting to just work was not just working - but that's only half the battle, when you've found that you then need to demonstrate to the customer that it's the service that is not working (and if it was working then your system would be working also) A great example of this is customers have some kind of screwed up AD implementation, so we'll make a LDAP call thinking it's going to the local DC but in reality it finding some old dusty DC in Alaska on the end of a Dial Up connection and it will time out. Another would be a system making calls to a SQL server but the machine making the calls cannot connect to the SQL Server, maybe because there is a firewall inbetween (and the SQL network port 1433 ?? is locked down) So, when writing your code, identify these external service and provide the verbosity in your log files or your application status screen that allow the support engineers to determine which services are failing (and to demonstrate the fact to the customer) I haven't even touched on how often we find that our systems / software stops working, customer calls and you spend hours troubleshooting only to find that the customer took a service offline, or decommissioned a server, or added a firewall - you get the idea...
This is the second article on Writing Supportable Software, helping all those customers and support engineers work with our software products and troubleshoot them themselves (i.e. going as far as possible down the troubleshoot path before referring it back to you).
You can the first article here...
This article is all about what you do (what you make your software do) when it fails. So, we all know that software has bugs from time to time, one of the keys to making it Supportable is providing a clean option of recovery from any error / bug / crash...
This is all about thinking in manner of two stage commits. So for a two stage commit system you fire off all the individual components of a transaction, they all report back that they completed successfully and then you do the final commit of the whole thing...
Using two stage commits for all the data moving, updating of entities is essential in building supportable software and for general customer satisfaction.
I worked with an application once that wrote (a lot of) data to the file system (tens of gigabytes per process run). The original design was to stage files in the same folder that we stored the data long term and we had no effective way of doing two stage commits - the result was that when a process run failed we left the part processed data files in the long term storage folder along with all the correctly processed data - all the filenames were of a similar format so it was hugely difficult for the customer or support guys to filter out that part processed data and remove it - what's worse is that we would reprocess the unfinished process run, if it failed again then there would be another set of part processed files with the same data in it - etc etc...
The end result of this was lots of spurious, part processed data files on the customers system, taking up hundreds of gigabytes of disk space and we couldn't clear it out.
When I noticed this, I had the dev team change it to a two stage commit process (preventing it happening again) and had to write a trawler application to go back in and clean up the part processed files.
That is just one example, the general idea is to make sure that any failures / back outs / crashes clean up in an intelligent way or that the part processed data is stored / tagged in such a way as to be easily identifiable and 'cleanable' by your support guys or the customer themselves.
At work (C2C) I'm responsible for the development team and the technical support team and it proves to be a surprisingly difficult task maintaining a balance between their respective objectives - the two extremes are : - Support has little or no contact with development, bug escalations are done in a very formal way and the overall drive is to ensure development are not distracted by support work.
- Support has unhindered access to development, can draw on their expertise / knowledge whenever they like, the overall drive being to have happy customers (via quick and accurate resolutions)
Neither of these work well in a business - with the first you sacrifice customer satisfaction for development productivity and in the second it's the other way around... The key is finding the balance, but it's tough finding that balance, and there is no one answer or simple formula - it depends on many factors where (between the two extremes) you find the balance (product quality, support team knowledge, customer expectations etc) One thing that helps is getting the development team is to write Supportable Software - this means developing it in such a way that we maximize the troubleshooting efforts that can be carried out before it has to be escalated to the development team. There are a number of facets to this, I had initially planned to cover them in one article but when I started, it became apparent that there was too much for one article and it should probably be split into each key area in it's own article. The first area (topic for this article) is Tracing / Logging for Maximum Effect Let's kick right off with an example - consider the following trace file output : 10/10/2006 12:34:56.987 LDAP lookup failed with result = -214756544 Yes it tells you exactly what went wrong. A support engineer reading that can clearly see there is a problem with LDAP but nothing else. So what's his next step ? - escalate the case to the development team. Compare that to : 10/10/2006 12:34:56.987 LDAP lookup (' CN=Ken Hughes,DC=c2c,DC=com') to uk.c2c.com failed result = -214756544 A support engineer reading that line can now check : - The server uk.c2c.com is up and working
- Using LDP run the same query and check the results
Maybe the server was down, maybe the query works if the user is logged as an Administrator but not as the account running the software - there is a whole avenue of additional troubleshooting that the support engineer can investigate simply because the trace file was a little more verbose... This is true many different areas :- - Web Services - don't just say it failed, tell the user what URL was being used - support guy can check out the web server and DNS resolution
- SQL stuff - it's probably apparent which server your going to, but what about tracing out the SPROC your using or the SQL query string itself (especially if it's dynamically generated in code)
- Remoting / DCOM - Why settle for CoCreateInstance failed when you could specify the destination server and the class your trying to create
We could all think of more, but the general idea is - make the tracing sufficiently verbose as to allow engineers to investigate why the error occurred. If your using external or third party services (DNS lookups, LDAP, SQL, Exchange, WebServices, DCOM etc etc) then give the details of where your going to and the parameters your using. Bear in mind that as a developer, if your tracing out magic numbers or sparse data that only really make sense to you, then when someone sees it in a trace file it's coming straight back to you....
Singleton Design Pattern, is a solution to a common problem in the software domain where you want to create ONE AND ONLY ONE instance of an object/class. Put simply, it stops client applications creating their own instances of an object and routes all creation of the object through one method. If an instance of the class already exists then that instance is returned, if an instance of the class does not exist then a new instance is created. So, no matter how many clients access the class there is only ever one instance (that all clients access) Here's some sample code of how to achieve a Singleton class : using System; using System.Collections.Generic; using System.Text; namespace KenAndSarah.GpsControl { public class GpsControl { private static GpsControl instance = null; private GpsControl() { // do not allow a public constructor } // call this to get (or create) a reference to the class public static GpsControl GetGpsControl() { if (instance == null) instance = new GpsControl(); return instance; } } } So to clients would simply use it like this GpsControl gps = GpsControl.GetGpsControl(); Any subsequent clients who make the same call get a reference to the already created instance of the class.
So, having recently updated this website to Dasblog 1.9, I decided it was time to check out the source and have a play around. The initial easy step to get involved seemed to be writing some Macros (I wanted to extend the default blogStats() macro to include Total Trackbacks and also add a Trackback Count to each post item in the footer. There is a great intro to Creating Dasblog Macros on their Documentation Site, also googling for Dasblog macros uncovered a few good sites Anyway, I cracked open VS and spent a couple of hours writing some new macros, compiled them, made the requisite changes to my homeTemplate and itemTemplate but they just seemed to be return null strings. Checking the (Dasblog) event log showed that it was failing the find the Type for my Macros. I checked and rechecked everything and was convinced that everything was as it should be. Stumped!! Viewed my DLL in reflector, typenames were all correct - what was I doing wrong ?? A quick post to the Dasblog developers mailing list resulted in someone suggesting that I check if I was running a .NET2 compiled DLL in a .Net1.1 IIS virtual directory - Doh !! Updated the virtual directory to ASP.NET 2.0 (easier than reverting to VS2003 from VS2005), 'touched' the web.config, refreshed the page and 'ta da...' Changes and source code to be rolled out to this web site in the next day or so (more testing to do on how ASP.NET 2.0 works out), 
Three months ago I implemented SCRUM for the development team I manage at work (C2C Systems). So, whilst I agree with some elements of Steve Yegge's comments about getting the best out of developers, I disagree with the blanket statement that all Agile methodologies are no good - I am a great advocate of SCRUM - and my reasons ?? I implemented it and it works !! Here's my comments / thoughts and experiences so far.... So far we have had 5 SPRINTs of between 1 and 2 weeks, and we are just getting to the stage where we have learned all (most of) the lessons and are in a position to begin 'living by it' So, how has it been, what benefits did it bring and is it successful or not. Better communication of project status - the daily SCRUM meetings (15 minutes at the end of each day), involving all developers are really working out well - the developers appreciate being 100% up to date with the status, understanding what everyone else is doing, how much work is still to do, how close we are to meeting the deadline / target. NOTE: We have a 10-15 minute meeting at then end of every day, the status sheet and burndown chart are both updated and published in a prominent position. There seems to be a general feeling that ticking things off the list gives a sense of progress (getting closer to the objective / target) - previously we simply had a big queue of things and it was not easy to gauge how far through we were or easily see how much closer we were getting to completing. Also, as each 'feature' is ticked off, I have more confidence that we can do a snapshot release or put together an early beta for a customer demo - basically it allows additional flexibility. Statistics / Status - I'm a great 'measurer', I love to measure every facet of what we are doing. All these stats are on the status sheet and are visible to everyone, so each person can see how they are doing (accuracy of time estimates, hours worked towards the Sprint / Backlog item, percentage of time spent on the Sprint - as a department we have a target percentage of work towards new software development - the Sprint - figure we need to achieve.) Estimation - as with any project there is a general bias towards overestimating - this is (I believe) reduced with SCRUM - one of the measures on my spreadsheet is the accuracy of the estimates. So, not only is there a measure on the spread of how well the developer estimated the time required but it is also very visible to everyone why we need accurate estimates (so we can effectively plan the Sprints). Early visibility of problems (allowing early corrective action) - this is the opposite of the above, under estimation - if this happens then at the very least I get almost immediate visibility of the issue and can take corrective action (more on this later) Identification of where we are lacking good process - measuring or noting the impediments gives a very good handle on where your processes are falling down, for example if the developers are spending 2 hours chasing down Product Management for answers to ambiguous requirement details then you know you need to improve the requirements process (yes you can see this in normal development lifecycle, but measuring it as an impediment give you good solid / tangible metrics to go Prod Mgmt with to discuss the issue and have them sort it out. Identification of where we were unexpectedly burning time that was not being allocated - what I mean by this is that we were averaging XX% of development time spent on new feature development, after a couple of Sprints we noticed that we had not been including time for: Code reviews, Demo'ing features to Support, Memory leak testing, Measuring performance metrics and the like - all necessary tasks but we had not been measuring them in our 'time towards new feature development'. Links / Useful Info. Control Chaos Mountain Goat Software The SCRUM Alliance Yahoo SCRUM Group A sample SCRUM Excel Spreadsheet that I use...
Quick update on the TaHoGen open source project...
I just tidied up the whole source tree, added all the projects in a logical layout to one solution. I also sorted out the Visual Studio addin with better error handling and added a Setup project so that it can be easily installed by an end user.
Murad's excellent SchemaBrowser was added added and checked in, now allowing us to glean data from databases to use in the code generation. We're closing in on CodeSmith...
Next step is to demonstrate use by including a number of templates.
The problem with the addin (that was so difficult to remedy) was MDA's - difficult to troubleshoot and even more difficult to fix.
When the addin is called by VS it tries to save a reference to the instance of VS that is creating it - this is so we can get a handle on the OutputWindow and add some new panes to it.
These sometimes (intermittently) get blocked by MDA's and the panes do not get created - the panes only show build output text so I added some error handling to continue processing even if we cannot write to the pane.
Anyway, if you want to take it for a test run, you can get it here.
Released today, a new version - get it here. They have added the following (list extracted from the Windows Liver Writer blog post)
The following is a summary of the changes in the Writer 1.0 (Beta) Update:
- Tagging support
- Support for Blogger Beta
- Categories are sorted by name and support scrolling, plus improved support for reading categories from your blog
- Improved startup performance
- Paste is enabled for Title region and TAB/SHIFT+TAB navigation between title and body supported
- Insert hyperlink added to context menu when text is selected
- Title attribute in Insert Link dialog
- Custom date support for Community Server
- Improved keyboard shortcuts for switching views
- Change spell-check shortcut key to F7
- Add ‘png’ to insert image dialog file type filter
- More robust image posting to Live Spaces
- Improved style detection for blogs
- Fixed issues with pasting URLs and links
- Remember last window size and position when opening a new post
- Open post dialog retrieves more than 25 old posts
From what I have noticed :-
- It still takes an age to open.
- There is still no support for adding a file to a post (and having it uploaded / FTP'd), so my 'Insert File (via FTP) plugin' is still valid.
- There is still no support to add new categories (you can choose from existing categories)
- The insert 'task box' does not provide a scrollbar
(so some items get hidden as you create more drafts)
Other things I noticed about the SDK when writing my plugin :
- No way to get a ref to the current blog provider
- No way to get properties for the current blog (this would be great as my plugin would be able to automatically pick up the FTP settings)
- The icon size for displaying on the Insert tab is a crazy 20x18. Why didn't they simply go with a standard size (16x16 for example).
See 'Insert File (via FTP) icon on the embedded image for how it looks when trying to scale a standard image to this custom size - AWFUL.
However, I can't complain, overall I'm pretty happy - it's FREE, it makes my post creating MUCH FASTER and the way it shows me how the post will look on the website (automagically using my website CSS / formatting) is just AMAZING
Blank entry, simply to list out the categories.
|
|
|
|
|
|