.NET Core 2.0 Answer
.NET Core 2.0 has Path.GetRelativePath which can be used like so:
var relativePath = Path.GetRelativePath( @"C:\Program Files\Dummy Folder\MyProgram", @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");
In the above example, the relativePath
variable is equal to Data\datafile1.dat
.
Alternative .NET Answer
@Dave's solution does not work when the file paths do not end with a forward slash character (/
) which can happen if the path is a directory path. My solution fixes that problem and also makes use of the Uri.UriSchemeFile
constant instead of hard coding "FILE"
.
/// <summary>/// Creates a relative path from one file or folder to another./// </summary>/// <param name="fromPath">Contains the directory that defines the start of the relative path.</param>/// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param>/// <returns>The relative path from the start directory to the end path.</returns>/// <exception cref="ArgumentNullException"><paramref name="fromPath"/> or <paramref name="toPath"/> is <c>null</c>.</exception>/// <exception cref="UriFormatException"></exception>/// <exception cref="InvalidOperationException"></exception>public static string GetRelativePath(string fromPath, string toPath){ if (string.IsNullOrEmpty(fromPath)) { throw new ArgumentNullException("fromPath"); } if (string.IsNullOrEmpty(toPath)) { throw new ArgumentNullException("toPath"); } Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath)); Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath)); if (fromUri.Scheme != toUri.Scheme) { return toPath; } Uri relativeUri = fromUri.MakeRelativeUri(toUri); string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase)) { relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); } return relativePath;}private static string AppendDirectorySeparatorChar(string path){ // Append a slash only if the path is a directory and does not have a slash. if (!Path.HasExtension(path) && !path.EndsWith(Path.DirectorySeparatorChar.ToString())) { return path + Path.DirectorySeparatorChar; } return path;}
Windows Interop Answer
There is a Windows API called PathRelativePathToA that can be used to find a relative path. Please note that the file or directory paths that you pass to the function must exist for it to work.
var relativePath = PathExtended.GetRelativePath( @"C:\Program Files\Dummy Folder\MyProgram", @"C:\Program Files\Dummy Folder\MyProgram\Data\datafile1.dat");public static class PathExtended{ private const int FILE_ATTRIBUTE_DIRECTORY = 0x10; private const int FILE_ATTRIBUTE_NORMAL = 0x80; private const int MaximumPath = 260; public static string GetRelativePath(string fromPath, string toPath) { var fromAttribute = GetPathAttribute(fromPath); var toAttribute = GetPathAttribute(toPath); var stringBuilder = new StringBuilder(MaximumPath); if (PathRelativePathTo( stringBuilder, fromPath, fromAttribute, toPath, toAttribute) == 0) { throw new ArgumentException("Paths must have a common prefix."); } return stringBuilder.ToString(); } private static int GetPathAttribute(string path) { var directory = new DirectoryInfo(path); if (directory.Exists) { return FILE_ATTRIBUTE_DIRECTORY; } var file = new FileInfo(path); if (file.Exists) { return FILE_ATTRIBUTE_NORMAL; } throw new FileNotFoundException("A file or directory with the specified path was not found.", path); } [DllImport("shlwapi.dll", SetLastError = true)] private static extern int PathRelativePathTo( StringBuilder pszPath, string pszFrom, int dwAttrFrom, string pszTo, int dwAttrTo);}