web-dev-qa-db-fra.com

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

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

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

Le code peut supposer qu'il est exécuté 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 aussi cherché des questions existantes, mais je n'ai pas trouvé ce que je cherchais.

42
Eric Schneider

Voici quelques exemples de code:

Toute la magie dérive 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 d'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();
21
Mike Marshall

J'ai développé la réponse d'ibram et créé cette classe (qui a été mise à jour pour chaque commentaire). Je l'ai probablement trop documenté, mais ça devrait aller de soi.

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

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

vous devez référencer 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;
   }
}

Update: Exécuter explicitement en tant qu'administrateur ne montrera pas les lecteurs mappés. Voici une explication de ce comportement: https://stackoverflow.com/a/11268410/448100 } _ (En bref: l'administrateur a un contexte d'utilisateur différent, donc aucun accès aux lecteurs mappés. d'utilisateur normal)

29
ibram

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 sans modification. 

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

MODIFIER

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 est attribué.

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

Je pense que vous pouvez utiliser la touche "Réseau" à partir de la ruche "Utilisateur actuel", dans le Registre ... Les lecteurs mappés y sont répertoriés avec leur chemin partagé sur le serveur.

S'il n'y a pas de lecteur mappé dans le système, il n'y a donc 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.

7
eFarzad

Je ne pouvais pas reproduire ibram ou Vermis ' réponse en raison du problème mentionné dans un commentaire sous la réponse de Vermis, à propos d'une exception d'initialisation de type.

Au lieu de cela, j'ai découvert que je pouvais interroger tous les lecteurs actuellement présents sur l'ordinateur, puis les lire en boucle, comme suit:

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 x64 Windows 7, pour .NET 4. Il devrait être utilisable si vous obtenez cette exception mentionnée ci-dessus.

Je l'ai fait en utilisant les éléments fournis par MSDN et des éléments de ibram ou Vermis ' réponses, bien qu'il ait été un peu difficile de trouver des exemples spécifiques sur MSDN. Ressources utilisées:

MSDN: Win32_LogicalDisk Class

MSDN: espace de noms System.Management

MSDN: exemple de requête 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;
    }
}
5
Hydronium

QueryDosDevice traduit une lettre de lecteur dans le chemin auquel elle s'étend. 

Notez que cela traduira TOUTES les lettres de lecteur, pas seulement celles 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.

Voici 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 # one

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

Vous pouvez utiliser WMI pour interroger la collection Win32_LogicalDrive sur votre ordinateur. Voici un exemple de la façon de le faire avec un script . Changer cela en C # est assez bien expliqué dans d'autres endroits.

Code VB.NET légèrement modifié 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
4
Nick

On dirait qu'il faut un P/Invoke: Conversion d'une lettre de lecteur mappée en un chemin réseau à l'aide de C #

Ce gars a construit une classe gérée pour y faire face: Lecteur de carte C # Map (API)

2
Rubens Farias

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

2
rerun

Semblable à la réponse d'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;
}
2
Loathing

Ceci post décris comment obtenir le chemin absolu d’un lecteur mappé sur un dossier local? 

Par exemple, j'ai un dossier "c:\test" et un lecteur "x:" qui est mappé sur c:\test.

Je cherche une fonction qui retournera "c:\test" quand je passerai "X:"

La réponse est:

SUBST utilise DefineDosDevice (XP et versions ultérieures) pour créer le lecteur/chemin cartographie. Vous pouvez utiliser QueryDosDevice pour obtenir le chemin d’un SUBSTed conduire:

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

    }

}
0
Carlos Liu

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

0
Jerry Coffin