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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2067075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:12:27  来源:igfitidea点击:

How do I determine a mapped drive's actual path?

c#.netwindowsvb.net

提问by Eric Schneider

How do I determine a mapped drive's actual path?

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

So if I have a mapped drive on a machine called "Z" how can I using .NET determine the machine and path for the mapped folder?

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

The code can assume it's running on the machine with the mapped drive.

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

I looked at Path, Directory, FileInfo objects, but can't seem to find anything.

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

I also looked for existing questions, but could not find what I'm looking for.

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

采纳答案by Mike Marshall

Here are some code samples:

下面是一些代码示例:

All of the magic derives from a Windows function:

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

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

回答by Rubens Farias

Seems it's need a P/Invoke: Converting a mapped drive letter to a network path using C#

似乎需要一个 P/Invoke:使用 C# 将映射的驱动器号转换为网络路径

This guy built a managed class to deal with it: C# Map Network Drive (API)

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

回答by John Knoeller

QueryDosDevicetranslates a drive letter into the path that it expands to.

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

Note that this will translate ALL drive letters, not just those that are mapped to network connections. You need to already know which are network paths, or to parse the output to see which are network.

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

Here's the VB signature

这是VB签名

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

And the C# one

和 C# 之一

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

回答by Jerry Coffin

As far as Windows cares, what's needed is a call to WNetGetConnection. I don't know of a front-end for that in .NET, so you may have to call it via P/Invoke (fortunately, it has only one parameter, the P/Invoke code isn't too awful).

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

回答by rerun

You can also use WMI Win32_LogicalDisk to get all the information you need. use the ProviderName from the class to get the UNC path.

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

回答by Nick

You can use WMI to interrogate the Win32_LogicalDrive collection on your machine. Here is an example of how to do it with scripting. Changing this over to C# is pretty well explained in other places.

您可以使用 WMI 查询计算机上的 Win32_LogicalDrive 集合。 下面是一个如何使用脚本执行此操作的示例。将其更改为 C# 在其他地方得到了很好的解释。

Slightly modified VB.NET code from the article:

对文章中的 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

回答by ibram

I can't remember where I found this, but it works withoutp/invoke. It's what rerunposted before.

我不记得我在哪里找到的,但它可以在没有p/invoke 的情况下工作。这是之前重新发布的内容。

you need to reference System.Management.dll:

你需要引用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:Explicitly running as administratorwill not show mapped drives. Here is an explanation of this behaviour: https://stackoverflow.com/a/11268410/448100(in short: administrator has a different user context, so no access to mapped drives of normal user)

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

回答by Vermis

I expanded on ibram's answer and created this class (which has been updated per comment feedback). I've probably over documented it, but it should be self-explanatory.

我扩展了 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(), "");
    }
}

回答by eFarzad

I think you can use the "Network" key From the "Current User" Hive, In the Registry. The Mapped Drives Are Listed There With Their Shared Path On Server.

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

If there is no mapped drive in the system, so there is no "Network" Key In The "Current User" Hive.

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

Now, I'm using this way, no external dll nor anything else.

现在,我正在使用这种方式,没有外部 dll 或其他任何东西。

回答by Hydronium

I could not replicate ibram'sor Vermis'answer due to the problem I mentioned in a comment under Vermis' answer, about a type initializer exception.

我无法复制IBRAM的蚓部的回答,由于我在评论中提到下蚓部问题的回答,关于类型的初始化异常。

Instead, I discovered I could query for all the drives currently on the computer and then loop through them, like so:

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

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

This works for x64 Windows 7, for .NET 4. It should be usable in case you're getting that exception that was mentioned above.

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

I did this using the stuff given from MSDN and bits from ibram'sor Vermis'answers, though it was a bit difficult to find specific examples on the MSDN. Resources used:

我这样做是利用MSDN和位给出从东西IBRAM的蚓部的答案,虽然这是一个有点难以找到在MSDN具体的例子。使用的资源:

MSDN : Win32_LogicalDisk Class

MSDN:Win32_LogicalDisk 类

MSDN : System.Management namespace

MSDN:System.Management 命名空间

MSDN : WMI Queries example:

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