2010-01-14 5 views
35

Comment déterminer le chemin réel d'un lecteur mappé?Comment déterminer le chemin réel d'un lecteur mappé?

Donc, si j'ai un lecteur mappé sur une machine appelée "Z", comment puis-je utiliser. NET déterminer la machine et le chemin pour le dossier mappé?

Le code peut supposer qu'il s'exécute sur la machine avec le lecteur mappé. J'ai regardé les objets Path, Directory, FileInfo, mais je n'arrive pas à trouver quoi que ce soit. J'ai également cherché des questions existantes, mais je n'ai pas trouvé ce que je cherchais.

+1

Découvrez @ réponse de Nick pour une méthode qui n'utilise pas PInvoke ou des bibliothèques spécialisées. – tehDorf

Répondre

17

Voici quelques exemples de code:

Toute la magie provient d'une fonction 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); 

Exemple invocation:

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

J'ai confirmé le code C# du travail de liaison. Je préférerais avoir une version d'importation non-dll, mais mieux que rien du tout. – eschneider

+3

Plutôt que de simplement fournir un lien, pouvez-vous s'il vous plaît fournir un contexte dans votre réponse réelle au cas où le lien deviendrait indisponible? Merci. – Deanna

+2

Dans le cas où ce lien est invalide un jour, la principale chose que vous devez savoir est qu'il utilise WNetGetConnection (vous pouvez le trouver sur MSDN). – eselk

3

QueryDosDevice se traduit par une lettre de lecteur dans le chemin qu'il se dilate à. Notez que cela traduira TOUTES les lettres de lecteur, pas seulement celles qui sont mappées aux connexions réseau. Vous devez déjà savoir quels sont les chemins réseau ou analyser la sortie pour voir quels sont les réseaux.

est ici la signature VB

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

Et le C# un

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

Je n'arrive pas à obtenir ce travail, aussi ce look ne donnera pas le dossier, le pilote mappé est à un serveur et un dossier ... – eschneider

+0

Si vous voulez dire que vous voulez savoir ce chemin comme il apparaît au serveur, alors vous devrez demander au serveur. Cette information n'est pas disponible pour le client. –

+0

Si le lecteur est mappé sur la machine, le code est en cours d'exécution puis il devrait fonctionner. – eschneider

0

En ce qui prend soin de Windows, ce qui est nécessaire est un appel à WNetGetConnection. Je ne connais pas de frontal pour cela dans .NET, donc vous devrez peut-être l'appeler via P/Invoke (heureusement, il n'a qu'un seul paramètre, le code P/Invoke n'est pas trop horrible).

2

Vous pouvez également utiliser WMI Win32_LogicalDisk pour obtenir toutes les informations dont vous avez besoin. utilisez le ProviderName de la classe pour obtenir le chemin UNC.

4

Vous pouvez utiliser WMI pour interroger la collection Win32_LogicalDrive sur votre ordinateur. Here is an example of how to do it with scripting. Changer cela en C# est assez bien expliqué dans d'autres endroits.

légèrement modifié le code VB.NET de l'article:

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

Moyen mort simple d'obtenir le partage réseau de chaque lecteur mappé sans utiliser de bibliothèques spéciales. Cela fonctionne tout de suite dans une application VS Express 2012 pour Windows Form Desktop. – tehDorf

24

Je ne me souviens pas où j'ai trouvé, mais il fonctionne sans p/Invoke. C'est ce que relance posté avant.

vous devez faire référence System.Management.dll:

using System.IO; 
using System.Management; 
Code

:

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

Cela a fonctionné parfaitement pour mes besoins et semble être la solution la plus simple. Je suis surpris de ne pas avoir vu ça ailleurs. – JimDel

+0

Échoue sur Windows 8 lorsque 'path =" C: \\ "' avec une 'ManagementException' introuvable. – Loathing

+0

@Loathing Avez-vous trouvé une solution pour 'ManagementException'? Je frappe aussi cette erreur. Merci. –

35

J'Elargi de la réponse de Ibram et créé cette classe (qui a été mis à jour par des commentaires de commentaire) . Je l'ai probablement plus documenté, mais cela devrait être explicite.

/// <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

bit de code net +1 – deltree

+1

'Convert.ToUInt32 (mo [" DriveType "])' provoque un * L'initialiseur de type pour 'System.Management.ManagementPath' a émis une exception *, savez-vous si ce code fonctionne sous Windows7 ou cela pourrait-il être une stratégie de groupe? –

+1

@JeremyThompson L'exception InnerException pour cette exception (que j'obtiens aussi) est [System.Threading.ThreadAbortException] {"Une exception de type 'System.Threading.ThreadAbortException' a été levée."}. Je n'en connais pas encore la cause, mais je cherche toujours une solution. Je cours Win7 x64. – Hydronium

7

Je pense que vous pouvez utiliser la clé "Réseau" Depuis la Ruche "Utilisateur Actuel", Dans le Registre. Les lecteurs mappés sont répertoriés avec leur chemin d'accès partagé sur le serveur.

S'il n'y a pas de lecteur mappé dans le système, il n'y a pas de clé "Réseau" dans la ruche "Utilisateur actuel". Maintenant, j'utilise de cette façon, pas de DLL externe ni rien d'autre.

5

Je n'ai pas pu répliquer ibram's ou Vermis' en raison du problème que j'ai mentionné dans un commentaire sous la réponse de Vermis, à propos d'une exception d'initialisation de type.

Au lieu de cela, je découvert que je pouvais requête pour tous les lecteurs actuellement sur l'ordinateur, puis boucle à travers eux, comme ceci:

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

Cela fonctionne pour Windows 7 x64, pour 4. .NET Il devrait être utilisable dans le cas où vous obtenez cette exception qui a été mentionnée ci-dessus. Je l'ai fait en utilisant les choses données à partir de MSDN et des bits de ibram's ou Vermis' réponses, même s'il était un peu difficile de trouver des exemples spécifiques sur le MSDN. Ressources utilisées:

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

J'ai écrit une méthode pour cela. Il renvoie un chemin UNC s'il s'agit d'un lecteur mappé, sinon il renvoie le chemin inchangé.

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

Vous pouvez maintenant utiliser la méthode même avec des chemins déjà UNC. La version ci-dessus de la méthode lève une exception si un chemin UNC lui est donné.

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

J'ai trouvé que ça marchait très bien. Propre, court et simple. – JustBaron

+0

Y a-t-il un autre chemin dans le Registre pour trouver ces valeurs? Parce que j'ai trouvé tous sauf un (s'il vous plaît voir capture d'écran): [link] (http://imgur.com/AxA9FJN) – kamp

+1

Il semble que la solution la plus simple, qui fonctionne pour le framework .Net antérieur (comme 2.0) où aucun "System.Management" "namespace encore et cela fonctionne sans bibliothèques supplémentaires. Il n'a besoin que d'un espace de noms utilisé "Microsoft.Win32". –

2

similaires à la réponse de Ibram avec quelques modifications:

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