Como determino o caminho real de uma unidade mapeada?

Como determino o caminho real de uma unidade mapeada?

Então, se eu tenho uma unidade mapeada em uma máquina chamada “Z”, como posso usar o .NET para determinar a máquina e o caminho para a pasta mapeada?

O código pode assumir que está sendo executado na máquina com a unidade mapeada.

Eu olhei para objects Path, Directory, FileInfo, mas não consigo encontrar nada.

Também procurei por questões existentes, mas não consegui encontrar o que estou procurando.

Aqui estão algumas amostras de código:

  • Usando P / Invoke

Toda a magia deriva de uma function do 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); 

Exemplo de invocação:

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

Eu expandi a resposta do ibram e criei esta class (que foi atualizada por comentários). Eu provavelmente já o documentei, mas deveria ser auto-explicativo.

 ///  /// 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(), ""); } } 

Não me lembro onde encontrei isso, mas funciona sem p / invoke. É o que a reprise postou antes.

você precisa referenciar 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; } } 

Eu escrevi um método para isso. Ele retorna um caminho UNC se for uma unidade mapeada, caso contrário, ele retornará o caminho inalterado.

 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

Agora você pode usar o método mesmo com caminhos UNC já existentes. A versão acima do método lança uma exceção se receber um caminho 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; } 

Eu acho que você pode usar a chave “Rede” da seção “Usuário atual”, no registro. As unidades mapeadas estão listadas com seu caminho compartilhado no servidor.

Se não houver nenhuma unidade mapeada no sistema, não haverá uma chave “Rede” na seção “Usuário atual”.

Agora, estou usando dessa maneira, sem dll externa nem qualquer outra coisa.

Eu não consegui replicar a resposta de ibram ou Vermis devido ao problema que mencionei em um comentário sob a resposta de Vermis, sobre uma exceção de inicializador de tipo.

Em vez disso, descobri que podia consultar todas as unidades atualmente no computador e, em seguida, percorrê-las, assim:

 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; } } 

Isso funciona para o x64 Windows 7, para o .NET 4. Ele deve ser utilizável no caso de você receber essa exceção mencionada acima.

Eu fiz isso usando o material fornecido pelo MSDN e bits das respostas do ibram ou do Vermis , embora tenha sido um pouco difícil encontrar exemplos específicos no MSDN. Recursos utilizados:

MSDN: Classe Win32_LogicalDisk

MSDN: Namespace System.Management

MSDN: exemplo de consultas 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 traduz uma letra de unidade no caminho para o qual ela se expande.

Observe que isso traduzirá TODAS as letras de unidade, não apenas aquelas mapeadas para conexões de rede. Você precisa saber quais são os caminhos de rede ou analisar a saída para ver quais são a rede.

Aqui está a assinatura do VB

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

E o C # one

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

Você pode usar o WMI para interrogar a coleção Win32_LogicalDrive na sua máquina. Aqui está um exemplo de como fazer isso com scripts . Mudar isso para C # é muito bem explicado em outros lugares.

Código VB.NET ligeiramente modificado do artigo:

 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 é necessário um P / Invoke: convertendo uma letra de unidade mapeada para um caminho de rede usando c #

Esse cara construiu uma class gerenciada para lidar com isso: C # Map Network Drive (API)

Você também pode usar o WMI Win32_LogicalDisk para obter todas as informações necessárias. use o ProviderName da class para obter o caminho UNC.

Semelhante à resposta do ibram com algumas modificações:

 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; } 

No que diz respeito ao Windows, o que é necessário é uma chamada para o WNetGetConnection . Eu não sei de um front-end para isso no .net, então você pode ter que chamá-lo via P / Invoke (felizmente, ele tem apenas um parâmetro, o código P / Invoke não é muito horrível).

Esta postagem descreve como obter o caminho absoluto de uma unidade mapeada para uma pasta local?

Por exemplo, eu tenho uma pasta “c: \ test” e uma unidade “x:” que é mapeada para c: \ test.

Estou procurando uma function que retornará “c: \ test” quando eu passar em “x:”

A resposta é:

O SUBST usa o DefineDosDevice (XP e posterior) para criar o mapeamento do drive / caminho. Você pode usar o QueryDosDevice para obter o caminho de uma unidade 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; } }