¿Cómo determino la ruta real de una unidad mapeada?

¿Cómo determino la ruta real de una unidad mapeada?

Entonces, si tengo una unidad mapeada en una máquina llamada “Z”, ¿cómo puedo usar .NET para determinar la máquina y la ruta para la carpeta mapeada?

El código puede asumir que se está ejecutando en la máquina con la unidad asignada.

Miré Path, Directory, FileInfo Objects, pero no puedo encontrar nada.

También busqué las preguntas existentes, pero no pude encontrar lo que estoy buscando.

Aquí hay algunos ejemplos de código:

  • Usando P / Invoke

Toda la magia se deriva de una función de Windows:

[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); 

Ejemplo de invocación:

 var sb = new StringBuilder(512); var size = sb.Capacity; var error = Mpr.WNetGetConnection("Z:", sb, ref size); if (error != 0) throw new Win32Exception(error, "WNetGetConnection failed"); var networkpath = sb.ToString(); 

Amplié la respuesta de ibram y creé esta clase (que se ha actualizado por comentarios). Probablemente lo haya documentado en exceso, pero debería ser autoexplicativo.

 ///  /// 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. ///  ///  /// 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) /// ); /// } /// } ///  public static class MappedDriveResolver { ///  /// Resolves the given path to a full UNC path if the path is a mapped drive. /// Otherwise, just returns the given path. ///  /// The path to resolve. ///  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); } } ///  /// Resolves the given path to a root UNC path if the path is a mapped drive. /// Otherwise, just returns the given path. ///  /// The path to resolve. ///  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; } } } ///  /// Checks if the given path is a network drive. ///  /// The path to check. ///  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; } } ///  /// Given a path will extract just the drive letter with volume separator. ///  ///  /// C: 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(), ""); } } 

No recuerdo dónde encontré esto, pero funciona sin p / invoke. Es lo que se volvió a publicar antes.

necesita hacer referencia a System.Management.dll :

 using System.IO; using System.Management; 

código:

 public void FindUNCPaths() { DriveInfo[] dis = DriveInfo.GetDrives(); foreach( DriveInfo di in dis ) { if(di.DriveType == DriveType.Network) { DirectoryInfo dir = di.RootDirectory; // "x:" MessageBox.Show( GetUNCPath( dir.FullName.Substring( 0, 2 ) ) ); } } } public string GetUNCPath(string path) { if(path.StartsWith(@"\\")) { return path; } ManagementObject mo = new ManagementObject(); mo.Path = new ManagementPath( String.Format( "Win32_LogicalDisk='{0}'", path ) ); // DriveType 4 = Network Drive if(Convert.ToUInt32(mo["DriveType"]) == 4 ) { return Convert.ToString(mo["ProviderName"]); } else { return path; } } 

He escrito un método para esto. Devuelve una ruta UNC si es una unidad mapeada, de lo contrario devuelve la ruta sin cambios.

 public static string UNCPath(string path) { using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0])) { if (key != null) { path = key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString(); } } return path; } 

EDITAR

Ahora puede usar el Método incluso con rutas UNC ya existentes. La versión anterior del método arroja una excepción si se le da una ruta UNC.

 public static string UNCPath(string path) { if (!path.StartsWith(@"\\")) { using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0])) { if (key != null) { return key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString(); } } } return path; } 

Creo que puede usar la tecla “Red” de la sección “Usuario actual”, en el registro. Las unidades asignadas se enumeran allí con su ruta de acceso compartida en el servidor.

Si no hay una unidad asignada en el sistema, entonces no hay una clave de “Red” en la sección “Usuario actual”.

Ahora, estoy usando esta forma, sin dll externo ni nada más.

No pude replicar la respuesta de ibram o de Vermis debido al problema que mencioné en un comentario bajo la respuesta de Vermis, sobre una excepción de tipo de inicializador.

En su lugar, descubrí que podía consultar todas las unidades actualmente en la computadora y luego recorrerlas de la siguiente manera:

 using System.IO; //For DirectoryNotFound exception. using System.Management; ///  /// Given a local mapped drive letter, determine if it is a network drive. If so, return the server share. ///  ///  /// The server path that the drive maps to ~ "////XXXXXX//ZZZZ" private string CheckUNCPath(string mappedDrive) { //Query to return all the local computer's drives. //See http://msdn.microsoft.com/en-us/library/ms186146.aspx, or search "WMI Queries" SelectQuery selectWMIQuery = new SelectQuery("Win32_LogicalDisk"); ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher(selectWMIQuery); //Soem variables to be used inside and out of the foreach. ManagementPath path = null; ManagementObject networkDrive = null; bool found = false; string serverName = null; //Check each disk, determine if it is a network drive, and then return the real server path. foreach (ManagementObject disk in driveSearcher.Get()) { path = disk.Path; if (path.ToString().Contains(mappedDrive)) { networkDrive = new ManagementObject(path); if (Convert.ToUInt32(networkDrive["DriveType"]) == 4) { serverName = Convert.ToString(networkDrive["ProviderName"]); found = true; break; } else { throw new DirectoryNotFoundException("The drive " + mappedDrive + " was found, but is not a network drive. Were your network drives mapped correctly?"); } } } if (!found) { throw new DirectoryNotFoundException("The drive " + mappedDrive + " was not found. Were your network drives mapped correctly?"); } else { return serverName; } } 

Esto funciona para x64 Windows 7, para .NET 4. Debe ser utilizable en caso de que obtenga la excepción mencionada anteriormente.

Hice esto usando las cosas proporcionadas por MSDN y bits de las respuestas de ibram o Vermis , aunque fue un poco difícil encontrar ejemplos específicos en MSDN. Recursos utilizados:

MSDN: Win32_LogicalDisk Class

MSDN: espacio de nombres System.Management

MSDN: Ejemplo de consultas de WMI :

 using System; using System.Management; class Query_SelectQuery { public static int Main(string[] args) { SelectQuery selectQuery = new SelectQuery("Win32_LogicalDisk"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(selectQuery); foreach (ManagementObject disk in searcher.Get()) { Console.WriteLine(disk.ToString()); } Console.ReadLine(); return 0; } } 

QueryDosDevice traduce una letra de unidad en la ruta a la que se expande.

Tenga en cuenta que esto traducirá TODAS las letras de unidad, no solo las que están asignadas a las conexiones de red. Necesita saber cuáles son las rutas de red o analizar la salida para ver cuáles son las redes.

Aquí está la firma de VB

 Declare Function QueryDosDevice Lib "kernel32" Alias "QueryDosDeviceA" ( ByVal lpDeviceName As String, ByVal lpTargetPath As String, ByVal ucchMax As Integer) As Integer 

Y el C # uno

 [DllImport("kernel32.dll")] static extern uint QueryDosDevice(string lpDeviceName, IntPtr lpTargetPath, uint ucchMax); 

Puede usar WMI para interrogar a la colección Win32_LogicalDrive en su máquina. Aquí hay un ejemplo de cómo hacerlo con scripting . Cambiar esto a C # está bastante bien explicado en otros lugares.

Código VB.NET ligeramente modificado del artículo:

 Public Class Form1 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim strComputer = "." Dim objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Dim colDrives = objWMIService.ExecQuery("Select * From Win32_LogicalDisk Where DriveType = 4") For Each objDrive In colDrives Debug.WriteLine("Drive letter: " & objDrive.DeviceID) Debug.WriteLine("Network path: " & objDrive.ProviderName) Next End Sub End Class 

Parece que necesita una P / Invocación: Convertir una letra de unidad mapeada a una ruta de red usando C #

Este chico creó una clase administrada para manejarlo: C # Map Network Drive (API)

También puede usar WMI Win32_LogicalDisk para obtener toda la información que necesita. use el ProviderName de la clase para obtener la ruta UNC.

Similar a la respuesta de ibram con algunas modificaciones:

 public static String GetUNCPath(String path) { path = path.TrimEnd('\\', '/') + Path.DirectorySeparatorChar; DirectoryInfo d = new DirectoryInfo(path); String root = d.Root.FullName.TrimEnd('\\'); if (!root.StartsWith(@"\\")) { ManagementObject mo = new ManagementObject(); mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", root)); // DriveType 4 = Network Drive if (Convert.ToUInt32(mo["DriveType"]) == 4) root = Convert.ToString(mo["ProviderName"]); else root = @"\\" + System.Net.Dns.GetHostName() + "\\" + root.TrimEnd(':') + "$\\"; } return Recombine(root, d); } private static String Recombine(String root, DirectoryInfo d) { Stack s = new Stack(); while (d.Parent != null) { s.Push(d.Name); d = d.Parent; } while (s.Count > 0) { root = Path.Combine(root, (String) s.Pop()); } return root; } 

En lo que a Windows le importa, lo que se necesita es una llamada a WNetGetConnection . No conozco un front-end para eso en .NET, por lo que puede que tenga que llamarlo a través de P / Invoke (afortunadamente, tiene solo un parámetro, el código P / Invoke no es demasiado horrible).

Esta publicación describe cómo obtener la ruta absoluta de una unidad que está asignada a una carpeta local.

Por ejemplo, tengo una carpeta “c: \ test” y una unidad “x:” que está mapeada a c: \ test.

Estoy buscando una función que devolverá “c: \ test” cuando paso en “x:”

La respuesta es:

SUBST usa DefineDosDevice (XP y posterior) para crear la asignación de unidad / ruta. Puede usar QueryDosDevice para obtener la ruta de una unidad SUBSTed:

 [DllImport("kernel32.dll")] private static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); static String GetPhysicalPath(String path) { if (String.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } // Get the drive letter string pathRoot = Path.GetPathRoot(path); if(String.IsNullOrEmpty(pathRoot)) { throw new ArgumentNullException("path"); } string lpDeviceName = pathRoot.Replace("\\", ""); const String substPrefix = @"\??\"; StringBuilder lpTargetPath = new StringBuilder(260); if (0 != QueryDosDevice(lpDeviceName, lpTargetPath, lpTargetPath.Capacity)) { string result; // If drive is substed, the result will be in the format of "\??\C:\RealPath\". if (lpTargetPath..ToString().StartsWith(substPrefix)) { // Strip the \??\ prefix. string root = lpTargetPath.ToString().Remove(0, substPrefix.Length); result = Path.Combine(root, path.Replace(Path.GetPathRoot(path), "")); } else { // TODO: deal with other types of mappings. // if not SUBSTed, just assume it's not mapped. result = path; } return result; } else { // TODO: error reporting return null; } }