Menu

Nakov.com logo

Thoughts on Software Engineering

Universal Relative to Physical Path Converter for Windows Forms / ASP.NET / WPF / Console Apps

The .NET Framework does not provide standard functionality for resolving a path relative to the application root to a physical file system path. Thus in ASP.NET Web applications we need to use Server.MapPath(), but in console and Windows Forms / WPF applications we need to rely on the current directory. Moreover if we run a Web application in the “Visual Studio Development Web Server”, the current directory is the root of the Web application, but when we deploy the application in IIS we find that the current directory is different.

.NET developers need functionality that resolves a relative path in “tilde” style to a physical path that works in both Web and desktop scenario. Thus they can always use relative paths like “~/config/users.xml” and don’t need to change anything when moving code from a Web application to desktop application.

Below is the source code in C# of my universal file path resolver that many developers could find useful:

using System;
using System.IO;
using System.Reflection;
using System.Web; // You may need to add a reference to System.Web.dll

/// <summary>
/// Author: Svetlin Nakov
/// URL: https://nakov.com/blog/2009/07/14/universal-relative-to-physical-path-resolver-for-console-wpf-and-aspnet-apps/
/// </summary>
public class UniversalFilePathResolver
{
    /// <summary>
    /// Resolves a relative path starting with tilde to a physical file system path. In Web application
    /// scenario the "~" denotes the root of the Web application. In desktop application scenario (e.g.
    /// Windows Forms) the "~" denotes the directory where the currently executing assembly is located
    /// excluding "\bin\Debug" and "\bin\Release" folders (if present).
    ///
    /// For example: the path "~\config\example.txt" will be resolved to a physical path like
    /// "C:\Projects\MyProject\config\example.txt".
    ///
    /// </summary>
    /// <param name="relativePath">the relative path to the resource starting with "~"</param>
    /// <returns>Full physical path to the specified resource.</returns>
    public static string ResolvePath(string relativePath)
    {
        if (relativePath == null || !relativePath.StartsWith("~"))
        {
            throw new ArgumentException("The path '" + relativePath +
                "' should be relative path and should start with '~'");
        }

        HttpContext httpContext = HttpContext.Current;
        if (httpContext != null)
        {
            // We are in a Web application --> use Server.MapPath to get the physical path
            string fullPath = httpContext.Server.MapPath(relativePath);
            return fullPath;
        }
        else
        {
            // We are in a console / Windows desktop application -->
            // use currently executing assembly directory to find the full path
            Assembly assembly = Assembly.GetExecutingAssembly();
            string assemblyDir = assembly.CodeBase;
            assemblyDir = assemblyDir.Replace("file:///", "");
            assemblyDir = Path.GetDirectoryName(assemblyDir);

            // Remove "bin\debug" and "bin\release" directories from the path
            string applicationDir = RemoveStringAtEnd(@"\bin\debug", assemblyDir);
            applicationDir = RemoveStringAtEnd(@"\bin\release", applicationDir);

            string fullPath = relativePath.Replace("~", applicationDir);
            return fullPath;
        }
    }

    private static string RemoveStringAtEnd(string searchStr, string targetStr)
    {
        if (targetStr.ToLower().EndsWith(searchStr.ToLower()))
        {
            string resultStr = targetStr.Substring(0, targetStr.Length - searchStr.Length);
            return resultStr;
        }
        return targetStr;
    }
}

Note that this class removes automatically the “\Bin\Debug” directory suffix generated by Visual Studio during the compilation so you can rely that “~” denotes the root directory of the application not depending of the project type (Web / Console / Windows Forms / WPF / Class Library / Windows Service / etc.).

Note also that we should use Assemble.CodeBase instead of Assembly.Location because in certain circumstances these locations differs (e.g. if the application runs inside NUnit Runner). The above code of course would work under the assumption that the assembly is stored locally (comming from the file system, not from the network) [as of 13-Nov-2009].

This code does not work for Windows Store Apps in Windows 8 (WinRT developers should use different approach, e.g. using an “embedded resource“).

Comments (5)

5 Responses to “Universal Relative to Physical Path Converter for Windows Forms / ASP.NET / WPF / Console Apps”

  1. anon says:

    Thanks for this utility. Very handy.

  2. Anonymous says:

    What does Path in Path.GetDirectoryName refer to?

  3. Sakthi GS says:

    Thanks Man! Its working.

  4. minamba says:

    Thank you very much man, you saved my day !!!!!!!

RSS feed for comments on this post. TrackBack URL

LEAVE A COMMENT