web-dev-qa-db-ger.com

Wie bestimme ich den tatsächlichen Pfad eines zugeordneten Laufwerks?

Wie bestimme ich den tatsächlichen Pfad eines zugeordneten Laufwerks?

Wenn ich also ein zugeordnetes Laufwerk auf einem Computer namens "Z" habe, wie kann ich dann mithilfe von .NET den Computer und den Pfad für den zugeordneten Ordner ermitteln?

Der Code kann davon ausgehen, dass er auf dem Computer mit dem zugeordneten Laufwerk ausgeführt wird.

Ich habe mir Path, Directory und FileInfo-Objekte angesehen, kann aber nichts finden.

Ich habe auch nach vorhandenen Fragen gesucht, konnte aber nicht finden, wonach ich suche.

42
Eric Schneider

Hier einige Codebeispiele:

Die ganze Magie stammt von einer Windows-Funktion:

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

Beispielaufruf:

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

Ich habe die Antwort von Ibram erweitert und diese Klasse erstellt (die per Kommentar-Feedback aktualisiert wurde). Ich habe es wahrscheinlich über dokumentiert, sollte aber selbsterklärend sein.

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

Ich kann mich nicht erinnern, wo ich das gefunden habe, aber es funktioniert ohne p/invoke. Es ist das, was rerun zuvor gepostet hat.

sie müssen auf System.Management.dll verweisen:

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: Durch das explizite Ausführen von als Administrator werden zugeordnete Laufwerke nicht angezeigt. Hier eine Erklärung zu diesem Verhalten: https://stackoverflow.com/a/11268410/448100 (Kurz: Administrator hat einen anderen Benutzerkontext, also keinen Zugriff auf zugeordnete Laufwerke von normal Nutzer)

29
ibram

Ich habe dafür eine Methode geschrieben. Wenn es sich um ein zugeordnetes Laufwerk handelt, wird ein UNC-Pfad zurückgegeben. Andernfalls wird der Pfad unverändert zurückgegeben. 

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

Sie können die Methode jetzt auch mit bereits UNC-Pfaden verwenden. Die obige Version der Methode löst eine Ausnahme aus, wenn ein UNC-Pfad angegeben wird.

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

Ich denke, Sie können die "Network" -Taste aus der "Current User" -Bibliothek in der Registrierung verwenden.

Wenn im System kein zugeordnetes Laufwerk vorhanden ist, gibt es keine "Network" -Eingabe in der "Current User" -Struktur.

Jetzt verwende ich auf diese Weise keine externe DLL oder irgendetwas anderes.

7
eFarzad

Ich konnte die Antwort von ibram oder Vermis ' aufgrund eines Problems, das ich in einem Kommentar unter Vermis' Antwort über eine Typ-Initialisierungsausnahme erwähnt habe, nicht replizieren.

Stattdessen entdeckte ich, dass ich alle Laufwerke abfragen könnte, die sich derzeit auf dem Computer befinden, und diese dann wie folgt durchlaufen:

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

Dies funktioniert für x64 Windows 7 für .NET 4. Es sollte verwendet werden, falls Sie die oben erwähnte Ausnahme erhalten.

Ich habe dies mit dem Zeug von MSDN und Bits von ibram's oder Vermis ' Antworten getan, obwohl es etwas schwierig war, auf der MSDN bestimmte Beispiele zu finden. Verwendete Ressourcen:

MSDN: Win32_LogicalDisk-Klasse

MSDN: System.Management-Namespace

MSDN: Beispiel für WMI-Abfragen :

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 übersetzt einen Laufwerksbuchstaben in den Pfad, zu dem er erweitert wird. 

Beachten Sie, dass dies ALLE Laufwerksbuchstaben übersetzt, nicht nur die, die Netzwerkverbindungen zugeordnet sind. Sie müssen bereits wissen, welche Netzwerkpfade sind, oder die Ausgabe analysieren, um zu sehen, welche Netzwerkpfade sind.

Hier ist die Signatur VB

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

Und das C # eins

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

Sie können WMI verwenden, um die Win32_LogicalDrive-Auflistung auf Ihrem Computer abzufragen. Hier ist ein Beispiel, wie man das mit Scripting macht . Die Umstellung auf C # wird an anderen Stellen ziemlich gut erklärt.

Leicht veränderter VB.NET-Code aus dem Artikel:

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

Anscheinend ist ein P/Invoke erforderlich: Konvertieren eines zugeordneten Laufwerkbuchstaben in einen Netzwerkpfad mit C #

Dieser Typ hat eine verwaltete Klasse entwickelt, um damit umzugehen: C # Map Network Drive (API)

2
Rubens Farias

Sie können auch WMI Win32_LogicalDisk verwenden, um alle benötigten Informationen abzurufen. Verwenden Sie den ProviderName aus der Klasse, um den UNC-Pfad abzurufen.

2
rerun

Ähnlich wie die Antwort von ibram mit einigen Modifikationen:

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

Dieses post beschreibt, wie man den absoluten Pfad eines Laufwerks erhält, das einem lokalen Ordner zugeordnet ist. 

Zum Beispiel habe ich einen "c:\test" -Ordner und ein "x:" -Laufwerk c:\test zugeordnet.

Ich bin auf der Suche nach einer Funktion, die "c:\test" zurückgibt, wenn ich .__ übergeben habe. "x:"

Die Antwort ist:

SUBST verwendet DefineDosDevice (XP und höher) zum Erstellen des Laufwerks/Pfads Kartierung. Mit dem QueryDosDevice können Sie den Pfad eines SUBSTed .__ abrufen. Fahrt:

[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

Für Windows ist ein Aufruf von WNetGetConnection erforderlich. Ich kenne kein Frontend für das in .NET, daher müssen Sie es möglicherweise über P/Invoke aufrufen (zum Glück hat es nur einen Parameter, der P/Invoke-Code ist nicht zu schrecklich).

0
Jerry Coffin