There are a number of resources out there that attempt to explain how to get the network path of a mapped drive letter or path. However, I found most of them to be incomplete and/or full of errors.
For example, my attempts to do this with the ManagementObject and ManagementPath classes always failed with an exception like:
The type initializer for ‘System.Management.ManagementPath’ threw an exception. This might have been because I was working from a VM or maybe some other OS issue.
The solution I found that always worked across multiple environments involved the use of PInvoke and a dll import. True, it would be better if this could be done reliably with the .NET framework without needing to drill down into the Windows API, but until I find a better solution (or someone points out a better way in the comments here), I’ll be documenting this for others who might need it and for future reference.
This method accepts an absolute path (which could be on a mapped drive or on a local drive) and returns the equivalent UNC path (if exists).
If the absolute path cannot be converted to a UNC path, the absolute path is returned unchanged.
[DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern int WNetGetConnection( [MarshalAs(UnmanagedType.LPTStr)] string localName, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName, ref int length); public static string GetUNCPath(string originalPath) { StringBuilder sb = new StringBuilder(512); int size = sb.Capacity; if (originalPath.Length > 2 && originalPath[1] == ':') { char c = originalPath[0]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { int error = WNetGetConnection(originalPath.Substring(0, 2), sb, ref size); if (error == 0) { DirectoryInfo dir = new DirectoryInfo(originalPath); string path = Path.GetFullPath(originalPath).Substring(Path.GetPathRoot(originalPath).Length); return Path.Combine(sb.ToString().TrimEnd(), path); } } } return originalPath; }
Remember to add a using statement for System.Runtime.InteropServices (contains the DllImportAttribute) and any other using statements the rest of your class may need.
Once we have imported mpr.dll the real work is done by calling a Windows API function.
Doing This Without PInvoke
This solution from Stackoverflow looks really interesting but sadly, didn’t work for me. I’m not sure yet why it’s failing on my environment and I intend to research on this later and possibly tweak the code to make it work.
It does not involve using PInvoke.
/// <summary> /// A static class to help with resolving a mapped drive path to a UNC network path. /// If a local drive path or a UNC network path are passed in, they will just be returned. /// </summary> /// <example> /// using System; /// using System.IO; /// using System.Management; // Reference System.Management.dll /// /// // Example/Test paths, these will need to be adjusted to match your environment. /// string[] paths = new string[] { /// @"Z:\ShareName\Sub-Folder", /// @"\\ACME-FILE\ShareName\Sub-Folder", /// @"\\ACME.COM\ShareName\Sub-Folder", // DFS /// @"C:\Temp", /// @"\\localhost\c$\temp", /// @"\\workstation\Temp", /// @"Z:", // Mapped drive pointing to \\workstation\Temp /// @"C:\", /// @"Temp", /// @".\Temp", /// @"..\Temp", /// "", /// " ", /// null /// }; /// /// foreach (var curPath in paths) { /// try { /// Console.WriteLine(string.Format("{0} = {1}", /// curPath, /// MappedDriveResolver.ResolveToUNC(curPath)) /// ); /// } /// catch (Exception ex) { /// Console.WriteLine(string.Format("{0} = {1}", /// curPath, /// ex.Message) /// ); /// } /// } /// </example> public static class MappedDriveResolver { /// <summary> /// Resolves the given path to a full UNC path if the path is a mapped drive. /// Otherwise, just returns the given path. /// </summary> /// <param name="path">The path to resolve.</param> /// <returns></returns> public static string ResolveToUNC(string path) { if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException("The path argument was null or whitespace."); } if (!Path.IsPathRooted(path)) { throw new ArgumentException( string.Format("The path '{0}' was not a rooted path and ResolveToUNC does not support relative paths.", path) ); } // Is the path already in the UNC format? if (path.StartsWith(@"\\")) { return path; } string rootPath = ResolveToRootUNC(path); if (path.StartsWith(rootPath)) { return path; // Local drive, no resolving occurred } else { return path.Replace(GetDriveLetter(path), rootPath); } } /// <summary> /// Resolves the given path to a root UNC path if the path is a mapped drive. /// Otherwise, just returns the given path. /// </summary> /// <param name="path">The path to resolve.</param> /// <returns></returns> public static string ResolveToRootUNC(string path) { if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException("The path argument was null or whitespace."); } if (!Path.IsPathRooted(path)) { throw new ArgumentException( string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.", path) ); } if (path.StartsWith(@"\\")) { return Directory.GetDirectoryRoot(path); } // Get just the drive letter for WMI call string driveletter = GetDriveLetter(path); // Query WMI if the drive letter is a network drive, and if so the UNC path for it using (ManagementObject mo = new ManagementObject()) { mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter)); DriveType driveType = (DriveType)((uint)mo["DriveType"]); string networkRoot = Convert.ToString(mo["ProviderName"]); if (driveType == DriveType.Network) { return networkRoot; } else { return driveletter + Path.DirectorySeparatorChar; } } } /// <summary> /// Checks if the given path is a network drive. /// </summary> /// <param name="path">The path to check.</param> /// <returns></returns> public static bool isNetworkDrive(string path) { if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException("The path argument was null or whitespace."); } if (!Path.IsPathRooted(path)) { throw new ArgumentException( string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.", path) ); } if (path.StartsWith(@"\\")) { return true; } // Get just the drive letter for WMI call string driveletter = GetDriveLetter(path); // Query WMI if the drive letter is a network drive using (ManagementObject mo = new ManagementObject()) { mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter)); DriveType driveType = (DriveType)((uint)mo["DriveType"]); return driveType == DriveType.Network; } } /// <summary> /// Given a path will extract just the drive letter with volume separator. /// </summary> /// <param name="path"></param> /// <returns>C:</returns> public static string GetDriveLetter(string path) { if (String.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException("The path argument was null or whitespace."); } if (!Path.IsPathRooted(path)) { throw new ArgumentException( string.Format("The path '{0}' was not a rooted path and GetDriveLetter does not support relative paths.", path) ); } if (path.StartsWith(@"\\")) { throw new ArgumentException("A UNC path was passed to GetDriveLetter"); } return Directory.GetDirectoryRoot(path).Replace(Path.DirectorySeparatorChar.ToString(), ""); } }
If you want a pure framework based solution, you might want to try this method first and only use the PInvoke method as a last resort.
Leave a Reply