2010-01-14 9 views
35

Come si determina il percorso effettivo di un'unità mappata?Come si determina il percorso effettivo di un'unità mappata?

Quindi, se ho un'unità mappata su una macchina chiamata "Z" come posso utilizzando .NET determinano la macchina e il percorso per la cartella mappata?

Il codice può assumere è in esecuzione sul computer con l'unità mappata.

ho guardato Path, Directory, oggetti FileInfo, ma non può sembrare trovare qualche cosa.

ho anche guardato per le domande già esistenti, ma non riusciva a trovare quello che sto cercando.

+1

Partenza @ risposta di Nick per un metodo che non utilizza PInvoke o qualsiasi biblioteche speciali. – tehDorf

risposta

17

Ecco alcuni esempi di codice:

tutta la magia deriva da una funzione di 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); 

Esempio invocazione:

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

Ho confermato che il codice C# dal collegamento funziona. Preferirei avere una versione di importazione non dll, ma meglio di niente. – eschneider

+3

Anziché limitarsi a fornire un collegamento, è possibile fornire un contesto nella risposta effettiva nel caso in cui il collegamento non sia disponibile? Grazie. – Deanna

+2

Nel caso in cui quel collegamento non sia valido un giorno, la cosa principale che devi sapere è che usa WNetGetConnection (lo puoi trovare su MSDN). – eselk

3

QueryDosDevice traduce una lettera di unità nel percorso che si espande a.

Si noti che questo si traduca tutte le lettere di unità, non solo quelli che sono mappati alle connessioni di rete. È necessario sapere già quali sono i percorsi di rete o analizzare l'output per vedere quali sono di rete.

Ecco la firma VB

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

E il C# uno

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

Non riesco a farlo funzionare, anche questo sembra che non darà la cartella, il driver mappato sono su un server e una cartella ... – eschneider

+0

Se vuoi dire che vuoi sapere quel percorso così come appare al server, allora dovrai chiedere al server. Questa informazione non è disponibile per il cliente. –

+0

Se l'unità è mappata sulla macchina su cui è in esecuzione il codice, dovrebbe funzionare. – eschneider

0

Per quanto se ne frega di Windows, ciò che è necessario è una chiamata a WNetGetConnection. Non conosco un front-end per questo in .NET, quindi potresti doverlo chiamare tramite P/Invoke (fortunatamente, ha solo un parametro, il codice P/Invoke non è troppo terribile).

2

È inoltre possibile utilizzare WMI Win32_LogicalDisk per ottenere tutte le informazioni necessarie. utilizzare il ProviderName dalla classe per ottenere il percorso UNC.

4

È possibile utilizzare WMI per interrogare la raccolta Win32_LogicalDrive sul proprio computer. Here is an example of how to do it with scripting. Cambiare questo aspetto in C# è abbastanza ben spiegato in altri posti.

leggermente modificato il codice VB.NET dall'articolo:

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 
+0

Morto modo semplice per ottenere la condivisione di rete di ciascuna unità mappata senza utilizzare alcuna libreria speciale. Funziona senza problemi in un'applicazione VS Express 2012 per desktop Windows Form. – tehDorf

24

Non ricordo dove ho trovato questo, ma funziona senza P/Invoke. È quello che replica pubblicato prima.

è necessario fare riferimento System.Management.dll:

using System.IO; 
using System.Management; 

codice:

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

Questo ha funzionato perfettamente per le mie esigenze e sembra essere la soluzione più semplice. Sono sorpreso di non averlo visto da nessun'altra parte. – JimDel

+0

Errore in Windows 8 quando 'path =" C: \\ "' con una 'ManagementException' non trovata. – Loathing

+0

@Loathing Hai trovato una soluzione per 'ManagementException'? Sto anche colpendo questo errore. Grazie. –

35

ho ampliato sulla risposta di Ibram e ha creato questa classe (che è stato aggiornato per commenti di feedback) . Probabilmente l'ho già documentato, ma dovrebbe essere auto-esplicativo.

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

neat bit di codice +1 – deltree

+1

'Convert.ToUInt32 (mo [" DriveType "])' provoca un * L'inizializzatore di tipo per 'System.Management.ManagementPath' ha generato un'eccezione *, sai se questo codice funziona su Windows7 o potrebbe essere una politica di gruppo? –

+1

@JeremyThompson InnerException per questa eccezione (che sto ottenendo anche) è [System.Threading.ThreadAbortException] {"È stata generata un'eccezione di tipo" System.Threading.ThreadAbortException ".}}. Non ne conosco ancora la causa, ma sto ancora cercando una soluzione. Sto facendo funzionare Win7 x64. – Hydronium

7

Penso che sia possibile utilizzare il tasto "Rete" Dall'hive "Utente corrente", nel Registro di sistema. Le unità mappate sono elencate con il loro percorso condiviso sul server.

Se nel sistema non è presente un'unità mappata, non vi è alcuna chiave "Rete" nell'hive "Utente corrente".

Ora, sto usando in questo modo, nessuna DLL esterna né altro.

5

Non è stato possibile replicare la risposta ibram's o Vermis' a causa del problema che ho menzionato in un commento sotto la risposta di Vermis, a proposito di un'eccezione di inizializzazione del tipo.

Invece, ho scoperto che potrei query per tutte le unità attualmente sul computer e poi un ciclo tra di loro, in questo modo:

using System.IO; //For DirectoryNotFound exception. 
using System.Management; 


/// <summary> 
/// Given a local mapped drive letter, determine if it is a network drive. If so, return the server share. 
/// </summary> 
/// <param name="mappedDrive"></param> 
/// <returns>The server path that the drive maps to ~ "////XXXXXX//ZZZZ"</returns> 
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; 
    } 
} 

Questo funziona per x64 di Windows 7, per NET 4. Dovrebbe essere utilizzabile nel caso in cui si ottenga quell'eccezione sopra menzionata.

Ho fatto questo utilizzando il materiale fornito da MSDN e bit da ibram's o Vermis' risposte, anche se è stato un po 'difficile trovare esempi specifici sul MSDN. Risorse utilizzate:

MSDN : Win32_LogicalDisk Class

MSDN : System.Management namespace

MSDN : WMI Queries example:

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

Ho scritto un metodo per questo. Restituisce un percorso UNC se si tratta di un'unità mappata, altrimenti restituisce il percorso invariato.

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

EDIT

È ora possibile utilizzare il metodo anche con i percorsi già UNC. La versione precedente del metodo genera un'eccezione se viene fornito un percorso 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; 
} 
+2

Ho trovato che funziona molto bene. Ordinato, breve e semplice. – JustBaron

+0

C'è un altro percorso nel Registro di sistema per trovare questi valori? Perché ne ho trovati tutti tranne uno (vedi screenshot): [link] (http://imgur.com/AxA9FJN) – kamp

+1

Sembra la soluzione più semplice, che funziona per il precedente .Net framework (come 2.0) dove no "System.Management "spazio dei nomi ancora e funziona senza librerie aggiuntive. Ha bisogno solo dello spazio dei nomi usato "Microsoft.Win32". –

2

Simile alla risposta di Ibram con alcune modifiche:

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