How to programmatically discover mapped network drives on system and their server names?
Asked Answered
D

7

28

I'm trying to find out how to programmatically (I'm using C#) determine the name (or i.p.) of servers to which my workstation has current maps. In other words, at some point in Windows Explorer I mapped a network drive to a drive letter (or used "net use w: " to map it). I know how to get the network drives on the system:

DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo d in allDrives)
{
    if (d.IsReady && d.DriveType == DriveType.Network)
    {
    }
}

But the DriveInfo class does not have properties that tell me what server and shared folder the mapped drive is associated with. Is there somewhere else I should be looking?

Dagmar answered 6/7, 2009 at 19:14 Comment(1)
Gosh, I love this place! Ask a question and almost immediately someone checks in with a great answer -- in this case three exhaustive replies containing incredibly useful material. I wish I could mark them all as Accepted. Upvotes all around, tho! Thanks!Dagmar
F
33

Have you tried to use WMI to do it?

using System;
using System.Management;
using System.Windows.Forms;

public static void Main()
{
    try
    {
        var searcher =  new ManagementObjectSearcher(
            "root\\CIMV2",
            "SELECT * FROM Win32_MappedLogicalDisk"); 

        foreach (ManagementObject queryObj in searcher.Get())
        {
            Console.WriteLine("-----------------------------------");
            Console.WriteLine("Win32_MappedLogicalDisk instance");
            Console.WriteLine("-----------------------------------");
            Console.WriteLine("Access: {0}", queryObj["Access"]);
            Console.WriteLine("Availability: {0}", queryObj["Availability"]);
            Console.WriteLine("BlockSize: {0}", queryObj["BlockSize"]);
            Console.WriteLine("Caption: {0}", queryObj["Caption"]);
            Console.WriteLine("Compressed: {0}", queryObj["Compressed"]);
            Console.WriteLine("ConfigManagerErrorCode: {0}", queryObj["ConfigManagerErrorCode"]);
            Console.WriteLine("ConfigManagerUserConfig: {0}", queryObj["ConfigManagerUserConfig"]);
            Console.WriteLine("CreationClassName: {0}", queryObj["CreationClassName"]);
            Console.WriteLine("Description: {0}", queryObj["Description"]);
            Console.WriteLine("DeviceID: {0}", queryObj["DeviceID"]);
            Console.WriteLine("ErrorCleared: {0}", queryObj["ErrorCleared"]);
            Console.WriteLine("ErrorDescription: {0}", queryObj["ErrorDescription"]);
            Console.WriteLine("ErrorMethodology: {0}", queryObj["ErrorMethodology"]);
            Console.WriteLine("FileSystem: {0}", queryObj["FileSystem"]);
            Console.WriteLine("FreeSpace: {0}", queryObj["FreeSpace"]);
            Console.WriteLine("InstallDate: {0}", queryObj["InstallDate"]);
            Console.WriteLine("LastErrorCode: {0}", queryObj["LastErrorCode"]);
            Console.WriteLine("MaximumComponentLength: {0}", queryObj["MaximumComponentLength"]);
            Console.WriteLine("Name: {0}", queryObj["Name"]);
            Console.WriteLine("NumberOfBlocks: {0}", queryObj["NumberOfBlocks"]);
            Console.WriteLine("PNPDeviceID: {0}", queryObj["PNPDeviceID"]);

            if(queryObj["PowerManagementCapabilities"] == null)
                Console.WriteLine("PowerManagementCapabilities: {0}", queryObj["PowerManagementCapabilities"]);
            else
            {
                UInt16[] arrPowerManagementCapabilities = (UInt16[])(queryObj["PowerManagementCapabilities"]);
                foreach (UInt16 arrValue in arrPowerManagementCapabilities)
                {
                    Console.WriteLine("PowerManagementCapabilities: {0}", arrValue);
                }
            }
            Console.WriteLine("PowerManagementSupported: {0}", queryObj["PowerManagementSupported"]);
            Console.WriteLine("ProviderName: {0}", queryObj["ProviderName"]);
            Console.WriteLine("Purpose: {0}", queryObj["Purpose"]);
            Console.WriteLine("QuotasDisabled: {0}", queryObj["QuotasDisabled"]);
            Console.WriteLine("QuotasIncomplete: {0}", queryObj["QuotasIncomplete"]);
            Console.WriteLine("QuotasRebuilding: {0}", queryObj["QuotasRebuilding"]);
            Console.WriteLine("SessionID: {0}", queryObj["SessionID"]);
            Console.WriteLine("Size: {0}", queryObj["Size"]);
            Console.WriteLine("Status: {0}", queryObj["Status"]);
            Console.WriteLine("StatusInfo: {0}", queryObj["StatusInfo"]);
            Console.WriteLine("SupportsDiskQuotas: {0}", queryObj["SupportsDiskQuotas"]);
            Console.WriteLine("SupportsFileBasedCompression: {0}", queryObj["SupportsFileBasedCompression"]);
            Console.WriteLine("SystemCreationClassName: {0}", queryObj["SystemCreationClassName"]);
            Console.WriteLine("SystemName: {0}", queryObj["SystemName"]);
            Console.WriteLine("VolumeName: {0}", queryObj["VolumeName"]);
            Console.WriteLine("VolumeSerialNumber: {0}", queryObj["VolumeSerialNumber"]);
        }
    }
    catch (ManagementException ex)
    {
        MessageBox.Show("An error occurred while querying for WMI data: " + ex.Message);
    }
}

to make it a little easier to get started download WMI Code Creater

Fundus answered 6/7, 2009 at 19:52 Comment(2)
Do you know if there's a way to determine if they're set to reconnect on login?Duro
I figured out how to do determine if it will be reconnected on login (involves registry access, posted as answer below)Duro
F
18

You could use WMI to enumerate and query mapped drives. The following code enumerates mapped drives, extracts the server name portion, and prints that out.

using System;
using System.Text.RegularExpressions;
using System.Management;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(
                "select * from Win32_MappedLogicalDisk");
            foreach (ManagementObject drive in searcher.Get()) {
                Console.WriteLine(Regex.Match(
                    drive["ProviderName"].ToString(),
                    @"\\\\([^\\]+)").Groups[1]);
                }
            }
        }
    }
}

You can find the documentaiton of the Win32_MappedLogicalDisk class here. An intro for accessing WMI from C# is here.

Fumatorium answered 6/7, 2009 at 19:41 Comment(2)
Do you know if there's a way to determine if they're set to reconnect on login?Duro
I figured out how to do determine if it will be reconnected on login (involves registry access, posted as answer below)Duro
D
11

The WMI methods won't tell you whether the drive is set to reconnect on login. When you set a drive to reconnect on login, Windows creates a key under HKCU\Network\. The method below can be used to determine if the drive is set to be remapped at login.

private static bool DriveSetForReconnect(string ComputerName, string DriveLetter)
{
    RegistryKey key = RegistryKey.OpenRemoteBaseKey(RegistryHive.CurrentUser, ComputerName);
    key = key.OpenSubKey("Network\\" + DriveLetter);

    return key != null;
}

HTH!

EDIT: To adapt the WMI solutions to work on any arbitrary machine, you need to change the scope parameter like the code below. You obviously have to have have admin rights on the remote machine.

string scope = string.Format(@"\\{0}\root\CIMV2", ComputerName);

ManagementObjectSearcher searcher =
    new ManagementObjectSearcher(scope,
    "SELECT * FROM Win32_MappedLogicalDisk");
Duro answered 30/8, 2010 at 17:55 Comment(4)
That's a cool addition to the answers to this question, @Tim, and while the original question referred to "current maps", I appreciate your adding this extra information.Dagmar
No problem. Maybe I misunderstood your comment, but this definitely applies to your current maps. Its an extra piece of information that was missing in the WMI info. This tells you whether they will be reconnected when the current user logs back in. The WMI reports all the maps regardless of whether they will be remapped on login. Also I adapted the WMI solution to work over the network on any arbitrary PC to which you have admin rights (primarily applicable to domain environments). I'll add the info to the answer.Duro
I asked the original question because I had an application that needed to find out what network maps it already had access to at that moment; the accepted answer fulfilled that question. What I meant by my comment was that your additional info on the reconnect property makes this question/answer of more general applicability, and increases its value to both me and the developer community.Dagmar
Ah, ok. I completely misunderstood what you were saying. :)Duro
L
5

You unfortunately have to use WinAPI via P/Invoke. It will require using WNetGetUniversalName and the UNIVERSAL_NAME_INFO structure. You check that if expanding the path using GetFullPath does not equal what the Universal Name is for the expanded path, then you know it is mapped. The basic pseudo-code is as follows (0 error checking, bare minimum):

var nfo = new UNIVERSAL_NAME_INFO();
var size = Marshal.SizeOf(nfo);

if (ERROR_MORE_DATA == WNetGetUniversalName(path, InfoLevel.UniversalName, 
    ref nfo, ref size)
{
    var buffer = Marshal.AllocHGlobal(size);
    if (NO_ERROR == WNetGetUniversalName(path, InfoLevel.UniversalName,
                                         buffer, ref size))
    {
        nfo = (UNIVERSAL_NAME_INFO)Marshal.PtrToStructure(buffer,
                                   typeof(UNIVERSAL_NAME_INFO));
    }
}

Here are the P/Invoke declarations, which should help you along your way:

internal class NativeMethods
{
    /// <summary>
    /// The type of structure that the function stores in the buffer.
    /// </summary>
    public enum InfoLevel
    {
        /// <summary>
        /// The function stores a <see cref="UNIVERSAL_NAME_INFO"/> structure in the
        /// buffer.
        /// </summary>
        UniversalName = 1,

        /// <summary>
        /// The function stores a <c>REMOTE_NAME_INFO</c> structure in the buffer.
        /// </summary>
        /// <remarks>
        /// Using this level will throw an <see cref="NotSupportedException"/>.
        /// </remarks>
        RemoteName = 2
    }

    /// <summary>
    /// The <see cref="WNetGetUniversalName(string,int,UNIVERSAL_NAME_INFO,int)"/> function
    /// takes a drive-based path for a network resource and returns an information
    /// structure that contains a more universal form of the name.
    /// </summary>
    /// <param name="lpLocalPath">A pointer to a constant null-terminated string that
    /// is a drive-based path for a network resource.</param>
    /// <param name="dwInfoLevel">The type of structure that the function stores in
    /// the buffer pointed to by the <paramref name="lpBuffer"/> parameter.</param>
    /// <param name="lpBuffer">A pointer to a buffer that receives the structure
    /// specified by the <paramref name="dwInfoLevel"/> parameter.</param>
    /// <param name="lpBufferSize">A pointer to a variable that specifies the size,
    /// in bytes, of the buffer pointed to by the <paramref name="lpBuffer"/> parameter.</param>
    /// <returns>If the function succeeds, the return value is <see cref="NO_ERROR"/>.</returns>
    [DllImport("mpr.dll", CharSet = CharSet.Auto)]
    public static extern int WNetGetUniversalName(
        string lpLocalPath,
        InfoLevel dwInfoLevel,
        ref UNIVERSAL_NAME_INFO lpBuffer,
        ref int lpBufferSize);

    /// <summary>
    /// The <see cref="WNetGetUniversalName(string,int,IntPtr,int)"/> function
    /// takes a drive-based path for a network resource and returns an information
    /// structure that contains a more universal form of the name.
    /// </summary>
    /// <param name="lpLocalPath">A pointer to a constant null-terminated string that
    /// is a drive-based path for a network resource.</param>
    /// <param name="dwInfoLevel">The type of structure that the function stores in
    /// the buffer pointed to by the <paramref name="lpBuffer"/> parameter.</param>
    /// <param name="lpBuffer">A pointer to a buffer that receives the structure
    /// specified by the <paramref name="dwInfoLevel"/> parameter.</param>
    /// <param name="lpBufferSize">A pointer to a variable that specifies the size,
    /// in bytes, of the buffer pointed to by the <paramref name="lpBuffer"/> parameter.</param>
    /// <returns>If the function succeeds, the return value is <see cref="NO_ERROR"/>.</returns>
    [DllImport("mpr.dll", CharSet = CharSet.Auto)]
    public static extern int WNetGetUniversalName(
        string lpLocalPath,
        InfoLevel dwInfoLevel,
        IntPtr lpBuffer,
        ref int lpBufferSize);

    /// <summary>
    /// The <see cref="UNIVERSAL_NAME_INFO"/> structure contains a pointer to a
    /// Universal Naming Convention (UNC) name string for a network resource.
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct UNIVERSAL_NAME_INFO
    {
        /// <summary>
        /// Pointer to the null-terminated UNC name string that identifies a
        /// network resource.
        /// </summary>
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpUniversalName;
    }
}
Loci answered 6/7, 2009 at 19:34 Comment(0)
M
5

I've found yet another way of doing this, which uses part of the technique sixlettervariables posted. I'd love some feedback about the pros and cons of the various techniques. For example, does mine have a downside, a scenario where it won't work, for instance?

[DllImport("mpr.dll")]
static extern uint WNetGetConnection(string lpLocalName, StringBuilder lpRemoteName, ref int lpnLength);

internal static bool IsLocalDrive(String driveName)
{
    bool isLocal = true;  // assume local until disproved

    // strip trailing backslashes from driveName
    driveName = driveName.Substring(0, 2);

    int length = 256; // to be on safe side 
    StringBuilder networkShare = new StringBuilder(length);
    uint status = WNetGetConnection(driveName, networkShare, ref length);

    // does a network share exist for this drive?
    if (networkShare.Length != 0)
    {
        // now networkShare contains a UNC path in format \\MachineName\ShareName
        // retrieve the MachineName portion
        String shareName = networkShare.ToString();
        string[] splitShares = shareName.Split('\\');
        // the 3rd array element now contains the machine name
        if (Environment.MachineName == splitShares[2])
            isLocal = true;
        else
            isLocal = false;
    }

    return isLocal;
}

This is called from this code:

DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives)
{
    bool isLocal = IsLocalDrive(drive.Name);
    if (isLocal)
    {
         // do whatever
    }
}
Mcbride answered 3/8, 2009 at 19:27 Comment(0)
S
1

We can also use net use to find IP or Computer Name of mapped network drive

Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/c net use";
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();

string driveName = "Y:";
var line = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                    .Where(x => x.Contains(driveName)).FirstOrDefault();
if (!string.IsNullOrEmpty(line))
{
    var host = line.Substring(line.IndexOf("\\"), line.Substring(line.IndexOf("\\")).IndexOf(" ")).Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
}
Synovitis answered 2/7, 2015 at 7:0 Comment(0)
R
0

Inspired by map network drive path in C# here's another simple method using Scripting objects:

            private static IDictionary<DriveInfo, string> GetMappedNetworkDrives()
        {
            var rawDrives = new IWshRuntimeLibrary.IWshNetwork_Class()
                .EnumNetworkDrives();
            var result = new Dictionary<DriveInfo, string>(
                rawDrives.length / 2);
            for (int i = 0; i < rawDrives.length; i += 2)
            {
                result.Add(
                    new DriveInfo(rawDrives.Item(i)),
                    rawDrives.Item(i + 1));
            }
            return result;
        }

See https://msdn.microsoft.com/en-us/library/t9zt39at(v=vs.84).aspx for details about the IWshNetwork_Class.

Rock answered 21/10, 2015 at 10:38 Comment(1)
@BenPower: You need to add a reference to COM library 'Windows Script Host Object Model' (Interop.IWshRuntimeLibrary).Rock

© 2022 - 2024 — McMap. All rights reserved.