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

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?


Solution 1:

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

Solution 2:

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.

Solution 3:

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

Solution 4:

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