如何确定映射驱动器的实际路径?

Posted

技术标签:

【中文标题】如何确定映射驱动器的实际路径?【英文标题】:How do I determine a mapped drive's actual path? 【发布时间】:2011-01-05 05:48:10 【问题描述】:

如果我在名为“Z”的机器上有一个映射驱动器,我如何使用 .NET 确定映射文件夹的机器和路径?

代码可以假设它在具有映射驱动器的机器上运行。

我查看了 Path、Directory、FileInfo 对象,但似乎找不到任何东西。

我还查找了现有问题,但找不到我要查找的内容。

【问题讨论】:

查看@Nick 对不使用 pinvoke 或任何特殊库的方法的回答。 【参考方案1】:

我扩展了 ibram 的答案并创建了这个课程(已根据评论反馈进行了更新)。我可能已经过度记录了它,但它应该是不言自明的。

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

【讨论】:

Convert.ToUInt32(mo["DriveType"]) 导致 'System.Management.ManagementPath' 的类型初始化程序引发异常,您知道此代码是否适用于 Windows7 还是组策略? @JeremyThompson 这个异常的 InnerException(我也得到了)是 [System.Threading.ThreadAbortException] “抛出了 'System.Threading.ThreadAbortException' 类型的异常。”。我还不知道造成这种情况的原因,但仍在寻找解决方案。我正在运行 Win7 x64。 +1;我最终使用了这段代码并做了一些小的改动: 1. 使类和方法成为静态的; 2. mo["DriveType"] 可以直接转换为uint,然后转换为System.IO.DriveType。这样我就不必处理那些神奇的数字了。 3. 将 mo 放入 using 语句中。 mo = null doesn't really help the GC【参考方案2】:

我不记得我在哪里找到的,但它没有 p/invoke 可以工作。这是rerun之前发布的。

你需要参考System.Management.dll

using System.IO;
using System.Management;

代码:

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;
   

更新: 以管理员身份显式运行不会显示映射的驱动器。以下是对此行为的解释: https://***.com/a/11268410/448100 (简而言之:管理员具有不同的用户上下文,因此无法访问普通用户的映射驱动器)

【讨论】:

这非常适合我的需求,似乎是最简单的解决方案。我很惊讶我在其他任何地方都没有见过这个。 path = "C:\\" 找不到ManagementException 时在Windows 8 上失败。 @Loathing 你找到ManagementException 的解决方案了吗?我也遇到了这个错误。谢谢。 @kurifodo 解决方案是去掉反斜杠,只使用C:。我将在下面的新答案中发布我实际使用的代码。 @ibram 当程序以管理员身份运行时,这在 Windows 10 中不起作用。如果您想在程序以管理员身份运行并跨不同平台工作时查找信息,可能需要 pinvoke。【参考方案3】:

以下是一些代码示例:

Using P/Invoke

所有的魔法都源自一个 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);

调用示例:

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

【讨论】:

我已确认链接中的 C# 代码有效。我宁愿有一个非 dll 导入版本,但总比没有好。 除了提供链接之外,您能否在实际答案中提供一些上下文,以防链接不可用?谢谢。 万一该链接某天无效,您需要知道的主要是它使用了 WNetGetConnection(您可以在 MSDN 上找到)。 这里保存的 github gist 以防网站停止工作:gist.github.com/LuciferSam86/ea047f07ff95aa976b058848396f1be1【参考方案4】:

我为此编写了一个方法。如果它是映射驱动器,则返回 UNC 路径,否则返回路径不变。

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;

编辑

您现在可以使用该方法,即使已有 UNC 路径。如果给定 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;

【讨论】:

我发现这非常好用。整洁、简短、简单。 注册表中是否有其他路径可以找到此值?因为我只找到了一个(请看截图):link 它看起来是最简单的解决方案,适用于早期的 .Net 框架(如 2.0),其中还没有“System.Management”命名空间,并且无需其他库即可使用。它只需要使用“Microsoft.Win32”的命名空间。 此解决方案对 Windows 10 无效【参考方案5】:

我认为您可以使用注册表中“当前用户”配置单元中的“网络”键。 映射的驱动器与它们在服务器上的共享路径一起列出。

如果系统中没有映射驱动器,则“当前用户”Hive 中没有“网络”键。

现在,我用的是这种方式,没有外部dll,也没有其他任何东西。

【讨论】:

对 Windows 10 无效【参考方案6】:

您可以使用 WMI 来询问您计算机上的 Win32_LogicalDrive 集合。 Here is an example of how to do it with scripting。将其更改为 C# 在其他地方得到了很好的解释。

对文章中的 VB.NET 代码稍作修改:

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

【讨论】:

在不使用任何特殊库的情况下获取每个映射驱动器的网络共享的简单方法。这在 VS Express 2012 for Desktop Windows Form 应用程序中开箱即用。 虽然这确实有效,但至少在网络驱动器上的 Windows 10 上会失败。如果您删除“DriveType = 4”,您将获得所有本地驱动器的列表,但没有映射的网络驱动器。【参考方案7】:

我无法复制 ibram's 或 Vermis' 的答案,因为我在 Vermis 的答案下的评论中提到了关于类型初始化程序异常的问题。

相反,我发现我可以查询计算机上当前的所有驱动器,然后循环遍历它们,如下所示:

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;
    

这适用于 x64 Windows 7,适用于 .NET 4。如果您遇到上面提到的异常,它应该是可用的。

我使用 MSDN 提供的内容和来自 ibram's 或 Vermis' 答案的位来完成此操作,尽管在 MSDN 上找到具体示例有点困难。使用的资源:

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;
    

【讨论】:

【参考方案8】:

QueryDosDevice 将驱动器号转换为它扩展的路径。

请注意,这将翻译所有驱动器号,而不仅仅是映射到网络连接的驱动器号。您需要已经知道哪些是网络路径,或者解析输出以查看哪些是网络。

这是 VB 签名

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

还有 C# 的

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

【讨论】:

我无法让它工作,而且看起来它不会给文件夹,映射的驱动程序是一个服务器和一个文件夹...... 如果你的意思是你想知道它出现在服务器上的路径,那么你需要询问服务器。客户无法获得该信息。 如果驱动器映射到运行代码的机器上,那么它应该可以工作。 您将在驱动器映射到的机器上返回 \\server\share。服务器可能有别名为 c:\foo\bar\whatever 的共享,如果需要,则必须询问服务器。【参考方案9】:

与 ibram 的回答类似,但做了一些修改:

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;

【讨论】:

【参考方案10】:

似乎需要一个 P/Invoke:Converting a mapped drive letter to a network path using C#

这家伙建了一个托管类来处理它:C# Map Network Drive (API)

【讨论】:

看起来这允许您通过代码映射或取消映射驱动器,我看不到从映射路径获取路径的任何内容。【参考方案11】:

您还可以使用 WMI Win32_LogicalDisk 来获取您需要的所有信息。使用类中的 ProviderName 来获取 UNC 路径。

【讨论】:

【参考方案12】:

就 Windows 而言,需要调用WNetGetConnection。我不知道 .NET 中的前端,因此您可能必须通过 P/Invoke 调用它(幸运的是,它只有一个参数,P/Invoke 代码并不太糟糕)。

【讨论】:

【参考方案13】:

post 描述了如何获取映射到本地文件夹的驱动器的绝对路径?

例如,我有一个“c:\test”文件夹和一个“x:”驱动器,它是 映射到 c:\test。

我正在寻找一个在我传入时将返回“c:\test”的函数 "x:"

答案是:

SUBST 使用 DefineDosDevice(XP 和更高版本)创建驱动器/路径 映射。您可以使用 QueryDosDevice 来获取 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;

    


【讨论】:

【参考方案14】:

这是一个不管是本地还是远程的解决方案

    private string uncpath_check(string path)
    
        string rval = path;
        string driveprefix = path.Substring(0, 2);
        string unc;

        if (driveprefix != "\\")
        
            ManagementObject mo = new ManagementObject();
            try
            
                mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='0'", driveprefix));
                unc = (string)mo["ProviderName"];
                rval = path.Replace(driveprefix, unc);
            
            catch
            
                throw;
            
        

        if (rval == null)
         rval = path; 

        return rval;
    

【讨论】:

以上是关于如何确定映射驱动器的实际路径?的主要内容,如果未能解决你的问题,请参考以下文章

映射要由服务使用的网络驱动器

如何将ftp服务器映射为本机磁盘

映射要由服务使用的网络驱动器

win10怎么映射网络驱动器,怎么连接共享文件夹

如何隐藏映射网络驱动器??请教一下

求救!!关于映射网络驱动器的问题!!!谢谢啊!知道请告诉我啊!