Convert absolute path to relative path

Today I needed to convert an absolute path to a relative path based on a specified base path. E.g.

c:\a\b\c -> c:\a\b\c\d\file.txt = d\file.txt
c:\a\b\c -> c:\a\file.txt = ..\..\file.txt
c:\a\b\c -> c:\a\x\file.txt = ..\..\x\file.txt

I am surprised there is nothing in the .NET framework so I had a hunt around and converted the code from the following URL (http://www.vergentsoftware.com/blogs/ckinsman/default.aspx?date=2006-08-07) into C#....

private string RelativePath(string absolutePath, string relativeTo)
string[] absoluteDirectories = absolutePath.Split('\\');
string[] relativeDirectories = relativeTo.Split('\\');

//Get the shortest of the two paths
int length = absoluteDirectories.Length < relativeDirectories.Length ? absoluteDirectories.Length : relativeDirectories.Length;

//Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;

//Find common root
for (index = 0; index < length; index++)
if (absoluteDirectories[index] == relativeDirectories[index])
lastCommonRoot = index;

//If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
throw new ArgumentException("Paths do not have a common base");

//Build up the relative path
StringBuilder relativePath = new StringBuilder();

//Add on the ..
for (index = lastCommonRoot + 1; index < absoluteDirectories.Length; index++)
if (absoluteDirectories[index].Length > 0)

//Add on the folders
for (index = lastCommonRoot + 1; index < relativeDirectories.Length - 1; index++)
relativePath.Append(relativeDirectories[index] + "\\");
relativePath.Append(relativeDirectories[relativeDirectories.Length - 1]);

return relativePath.ToString();


Acer Skoda - I mean Ferrari

I have owned an Acer Ferrari now for approximately 13 months. Previously I had a problem with it freezing randomly. I spent some time trying to reproduce the problem and after a month or two was finally able to reproduce it 100% of the time. I sent my laptop back to Acer armed with exact steps and as a result my laptop was returned to me after only a couple of days.

For the past 2-3 months I have been seeing very rare, random resets. Obviously I blamed Windows, as you do :-) Some software I had to convert AVI to MPEG would always reset the laptop, but I just put that down to dodgy software. More recently I noticed that 7-zip would also reset my laptop *only* if I used ULTRA compression on a file larger than 500MB.

So I tried Winzip, same problem. I installed Vista and tried both Winzip and 7-zip in there, same problem. With steps to reproduce + proof that it was not the OS I felt I could finally send the laptop back for repair without running the risk of it coming back "No fault found".

The problem lies in my opening sentence "I have owned an Acer Ferrari now for approximately 13 months". The problem is that in my quest to find 100% reproducible steps my warranty has expired by about 30 days.

I explained all of this to the guy on the phone at Acer. "If only more customers were like you!" he said, and then continued to tell me that I had to pay for the repair. After talking over me for a few minutes I finally lost my temper and swore at him, something I have never done before.

So, now I am really annoyed! I have an old £400 Hewlett Packard laptop that has been working (slowly) without problems for years now, yet I pay thousands for a laptop with a quality brand such as "Ferrari" and it breaks twice in 13 months! I'd expect the quality to be better, wouldn't you?

What *really* annoys me though is that Acer didn't send me a card telling me my warranty was nearly up and asking if I would like to extend it (which I would have done), and ofcourse I had no idea it was coming to an end because the receipt is.......surprise surprise it is with my accountant who is preparing last year's accounts!

Ferrari I am disappointed. You have put your name to a machine that has the body of an F1 and the inner workings of a cheap old banger!


Someone sent me this on Skype this morning. I think it's really cool, it reminds me of the hardware sprites on the old Commodore 64!

01: Go to any site with lots of images (image search on google is a good one)
02: Once the images appear copy/paste this text into your address bar.

javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i<DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++}setInterval('A()',5 ); void(0);


ECO JumpStart

I've been up quite late working out what I want to cover in the "ECO jump start" document. The trick is to start at a level where the user knows absolutely nothing, and end up where they know enough to decide whether or not they wish to spend some time learning how to use ECO or not.

I think I should start off explaining why multi-tiered app development is a good idea; then I think I should go on to creating a package in a DLL; then onto creating a non-persistent WinForm app using that package; then make it persistent; and so on. My problem is that ECO just does so much!

I'll paste what I have so far at the bottom of this blog entry to get any feedback. As the list progresses the subjects become more advanced and I am worried that the jump start might actually frighten people off by making them feel overwhelmed. Maybe I should just take it so far and then leave the more advanced items out? Maybe I should just include a section at the end of the document explaining what advanced features are available without demonstrating how to use them? Then maybe do an "Advanced ECO" document in which I follow on where I left off and demonstrate the more advanced stuff.

At the moment I am undecided, what do you think? Remember, this document is really intended to server two purposes; it should be there to help new users to understand ECO, and also to act as a promotional ("marketing" if you like) document to boast how good ECO is without scaring people away.

And now the table of contents:

Why create separate layers (GUI / Business / Data access)
An overview of how ECO uses UML as the design tool to sepcify the business layer
Explain that ECO can persist those business objects in a number of ways, the DB being switched in a matter of seconds

Creating a business classes package
Create project
Add references
Configure package settings
Add 2 classes with n--m association
Add properties
Add association
Generate code

Creating a non-persistent WinForm app
Create ECO WinForm project
Add reference
Select packages

Adding some simple GUI
Delete all but rhRoot
(Design support)
Set EcoSpace type
Add DataGrid
Set EcoSpaceType
Add ExpressionHandle (allInstances)
Bind the datagrid to the ExpressionHandle

Adding new objects
Add EcoListActions
Set RootHandle
Add button
Set the ListAction to Add

Drag and drop
Add another ExpressionHandle
Add another DataGrid
Add another "Add" button
Add CurrencyManagerHandle, set parent = DataGrid1
Add ExpressionHandle, expression = "self.AssociationToOtherClass"
Add a DataGrid
Add DragDrop extender
DataGrid2 = DragSource
DataGrid3 = DropTarget
Run + create + drag/drop

Making the objects persistent
Add XML persistence to EcoSpace
Add EcoGlobalActions to form
Connect to RootHandle
Add button
Set GlobalAction to UpdateDatabase
Run, create, save, close, run

Creating a database
Remove the XmlPersistence
Add DB persistence + set configuration
Add connection
Generate DB
Run, create data, save

Evolving a database
Add new properties
Rename a property + set FormerName
Add new super class + move property up
Evolve DB
Run, show same data

Quick prototyping the model
Set AutoForm = True on grids
Run app, show auto forms
Add button
Set GlobalAction = debugger
Run app, show debugger, show access to auto forms

State machines
A simple state machine with triggers
Triggers with paramters
Transitions with guards
Multiple transitions out of a single state with guards (explain how only 1 must be true)
Multiple transitions out of a single state with the same trigger
Nested regions
Concurrent regions
Final state

Multi user
Unloading the cache to see changes by other users
Turning on optimistic locking
Getting conflict information
Locking regions

Remote persistence
Creating the remote persistence server
Registering as a remotable server
Registering as a HTTP remotable server available via IIS
Switching the WinForm to RemotePersistenceClient

Multi user synchronization server
What it is, how it works
Adding it to the remote persistence server
Requesting changes from the WinForm client
Run two apps, show the propagation of changes between them

Create ECO web application
Reference the same model
Explain why we have a PersistenceMapperProvider component (only about caching the mapping information for now)
Show how to generate DB / evolve from PersistenceMapperProvider instead of the EcoSpace

ECO ASP .NET autoforms for protoyping in ASP .NET

Show the ECO WebForm
Recreate the same WinForm gui in a WebForm

ASP .NET performance
Show the pool size in Web.Config
Explain what happens when more people request pages than there are EcoSpaces in the pool
Explain the SessionStrategy
Explain the maximum lifetime setting in Web.Config

ASP .NET multi-user
Explain how EcoSpaces are kept in sync with each other via the PersistenceMapperProvider
Demonstrate how WebForm data is in sync
Demonstrate how the WinForm / WebForm apps do not stay in sync with each other
Migrate the ASP .NET app to use the persistence server
Demonstrate WinForm app reflecting changes to data made in a WebForm app

Overview of each of the ECO components

ECO in code
Switching between Object and IObject
Overview of some of the ECO services
UndoService, In-memory transactions + undo blocks
OCLPS Service
OCL service
OCL action language service

Advanced modeling techniques
HasUserCode = true
OCL derived attributes
Overriding OCL derived attributes in subclasses
Code derived attributes
Code reverse derived attributes
Derived associations
Transient properties
Transient classes
Transient associations
Association classes
Platform independant property types
Delayed fetch properties
Referencing other packages
Extending classes
Adding associations
Adding class constraints
Enforcing in a WinForm application
Enforcing the same constraints in a webform application
Disabling the auto-generated multiplicity constraints
Object versioning
Navigating historical data
Retrieving all historical versions of an object

Advanced database techniques
Reverse engineering a database
Cusomizing the mapping information to fit an existing database
Evolving the structure of an existing database
Using a single model to pull data from multiple databases (including mixed servers) and updating the data too

Disabling BlueTooth on a Pocket PC

We use wireless printing through a COM port over BlueTooth. Having BlueTooth on all of the time can contribute towards energy consumption and cause the battery life on the Pocket PC to deplete faster. Now I disable BT when the application starts, and then re-enable it to print and disable it immediately afters. This adds about 1 second to each print job but it should save the battery power.

private static extern int BthGetMode(out BlueToothRadioMode dwMode);

private static extern int BthSetMode(BlueToothRadioMode dwMode);

public static BlueToothRadioMode BlueToothRadioMode
BlueToothRadioMode result;
BthGetMode(out result);
return result;
if (value != BlueToothRadioMode)


Keeping a Pocket PC awake

My compact framework application imports XML into a local database. As there is so much data to import this can take up to an hour. During development there were no problems with this, but of course during development the Pocket PC is docked in its cradle which provides it with power.

When a Pocket PC is removed from its cradle it manages power differently, just like an unplugged laptop. So every five minutes the Pocket PC would hibernate and the user would have to turn it back on in order for the import to continue. During an hour the user would have to do this approximately twelve times. How annoying, and dangerous too if the employee is driving to their first job.

Anyway, I found the following very useful code on the web and thought I'd point it out as it was so useful!

public class Device
#region Device sleep support
public static extern void SystemIdleTimerReset();

private static int DisableSleepCallsCount = 0;
private static System.Threading.Timer PreventSleepTimer = null;

public static void EnableDeviceSleep()
if (DisableSleepCallsCount == 0)
if (PreventSleepTimer != null)
PreventSleepTimer = null;

public static void DisableDeviceSleep()
if (DisableSleepCallsCount == 1)
TimerCallback keepAlive = new System.Threading.TimerCallback(
delegate(object applicationData)
catch (Exception)
PreventSleepTimer = new System.Threading.Timer(keepAlive, null, 0, 30 * 1000);

Now I can do this

//Do stuff


Dotting the I's and crossing the T's

Ever played "Spot the difference"? I'm sure you have :-)

I'm just looking through an application I have inherited from a Turkish company it was outsourced to. I was just browsing through a 5MB SQL script to generate the DB + stored procs when I saw this....


Does it do anything? Sure it does, but can you see what it is? There are two clues in this post but I wont tell you where!