Alright so in my case I don't have some of the tougher edge cases (fullPath and relativePath mixing network map locations, super long filenames). What I ended up doing was creating the class below
public class PathUtil { static public string NormalizeFilepath(string filepath) { string result = System.IO.Path.GetFullPath(filepath).ToLowerInvariant(); result = result.TrimEnd(new [] { '\\' }); return result; } public static string GetRelativePath(string rootPath, string fullPath) { rootPath = NormalizeFilepath(rootPath); fullPath = NormalizeFilepath(fullPath); if (!fullPath.StartsWith(rootPath)) throw new Exception("Could not find rootPath in fullPath when calculating relative path."); return "." + fullPath.Substring(rootPath.Length); } }
It seems to work pretty well. At least, it passes these NUnit tests:
[TestFixture] public class PathUtilTest { [Test] public void TestDifferencesInCapitolizationDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\WindowS\\System32"); Assert.AreEqual(format1, format2); } [Test] public void TestDifferencesDueToBackstepsDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\Program Files\\..\\Windows\\System32"); Assert.AreEqual(format1, format2); } [Test] public void TestDifferencesInFinalSlashDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\windows\\system32\\"); Console.WriteLine(format1); Console.WriteLine(format2); Assert.AreEqual(format1, format2); } [Test] public void TestCanCalculateRelativePath() { string rootPath = "c:\\windows"; string fullPath = "c:\\windows\\system32\\wininet.dll"; string expectedResult = ".\\system32\\wininet.dll"; string result = PathUtil.GetRelativePath(rootPath, fullPath); Assert.AreEqual(expectedResult, result); } [Test] public void TestThrowsExceptionIfRootDoesntMatchFullPath() { string rootPath = "c:\\windows"; string fullPath = "c:\\program files\\Internet Explorer\\iexplore.exe"; try { PathUtil.GetRelativePath(rootPath, fullPath); } catch (Exception) { return; } Assert.Fail("Exception expected"); } }
The test cases rely on certain files existing.. these files are common on most Windows installs but your mileage may vary.