Mapped network drives cannot be listed in C#
Asked Answered
L

2

9

I am trying to list all local drives in my application, and DriveInfo.GetDrives give back the local drive letters, and I need the mapped drives too.

Currently I have: C:, D:, and G: (HDD), E: and F: (CD), and S: and Z: (mapped network drives). (Yes, they are all visible in Windows Explorer, and in Total Commander too.) But only C, D, E, F, G are retrieved programmatically.

I also tried Environment.GetLogicalDrives(), GetLogicalDriveStrings (pInvoke), FindFirstVolume and FindNextVolumen (pInvoke). I tried to find the mapped drive list in the registry. Nothing works, only the HDD and CD letters are retreived.

There isn't any exception coming, no error indicated in the direct WinAPI calls, and now I am stuck. Is it some kind of security setting? Everywhere I look, the people say that the DriveInfo.GetDrives should give back the mapped drives. Is it really true?

I am using Vista Home Pro. The application is running from the local machine, and it is built here too with Visual Studio 2008.

I post what I used, but it is so simple, it cannot be the case that I do something wrong:

foreach (System.IO.DriveInfo di in System.IO.DriveInfo.GetDrives())
    Console.WriteLine(di.Name);

Result: C:\ D:\ E:\ F:\ G:\ Press any key to continue . . .

How can I make it work?

Laomedon answered 13/12, 2009 at 23:28 Comment(2)
Are you running this in user mode, or in a service?Siam
In user mode. Perfectly normal console application, or WinForm app.Laomedon
L
9

OK, I found out how to get the disconnected drives on Vista. Not easy, but it is doable:

First, you'll need the pInvoke definition of the following WinAPI functions:

  • WNetOpenEnum
  • WNetEnumResource
  • WNetCloseEnum

and those need a struct and some enums as well.

Then you need to call those multiple times, and at the end, you get the list. Here is the code, beware, it is long:

    [DllImport("MPR.dll", CharSet = CharSet.Auto)]
    static extern int WNetEnumResource(IntPtr hEnum, ref int lpcCount, IntPtr lpBuffer, ref int lpBufferSize);

    [DllImport("MPR.dll", CharSet = CharSet.Auto)]
    static extern int WNetOpenEnum(RESOURCE_SCOPE dwScope, RESOURCE_TYPE dwType, RESOURCE_USAGE dwUsage,
        [MarshalAs(UnmanagedType.AsAny)][In] object lpNetResource, out IntPtr lphEnum);

    [DllImport("MPR.dll", CharSet = CharSet.Auto)]
    static extern int WNetCloseEnum(IntPtr hEnum);

    public enum RESOURCE_SCOPE : uint
    {
        RESOURCE_CONNECTED = 0x00000001,
        RESOURCE_GLOBALNET = 0x00000002,
        RESOURCE_REMEMBERED = 0x00000003,
        RESOURCE_RECENT = 0x00000004,
        RESOURCE_CONTEXT = 0x00000005
    }
    public enum RESOURCE_TYPE : uint
    {
        RESOURCETYPE_ANY = 0x00000000,
        RESOURCETYPE_DISK = 0x00000001,
        RESOURCETYPE_PRINT = 0x00000002,
        RESOURCETYPE_RESERVED = 0x00000008,
    }
    public enum RESOURCE_USAGE : uint
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    }
    public enum RESOURCE_DISPLAYTYPE : uint
    {
        RESOURCEDISPLAYTYPE_GENERIC = 0x00000000,
        RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001,
        RESOURCEDISPLAYTYPE_SERVER = 0x00000002,
        RESOURCEDISPLAYTYPE_SHARE = 0x00000003,
        RESOURCEDISPLAYTYPE_FILE = 0x00000004,
        RESOURCEDISPLAYTYPE_GROUP = 0x00000005,
        RESOURCEDISPLAYTYPE_NETWORK = 0x00000006,
        RESOURCEDISPLAYTYPE_ROOT = 0x00000007,
        RESOURCEDISPLAYTYPE_SHAREADMIN = 0x00000008,
        RESOURCEDISPLAYTYPE_DIRECTORY = 0x00000009,
        RESOURCEDISPLAYTYPE_TREE = 0x0000000A,
        RESOURCEDISPLAYTYPE_NDSCONTAINER = 0x0000000B
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct NetResource
    {
        public RESOURCE_SCOPE dwScope;
        public RESOURCE_TYPE dwType;
        public RESOURCE_DISPLAYTYPE dwDisplayType;
        public RESOURCE_USAGE dwUsage;
        [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
        public string lpLocalName;
        [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
        public string lpRemoteName;
        [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
        public string lpComment;
        [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPTStr)]
        public string lpProvider;
    }

    static System.Collections.Generic.Dictionary<string, NetResource> WNetResource(object resource)
    {
        System.Collections.Generic.Dictionary<string, NetResource> result = new System.Collections.Generic.Dictionary<string, NetResource>();

        int iRet;
        IntPtr ptrHandle = new IntPtr();
        try
        {
            iRet = WNetOpenEnum(
                RESOURCE_SCOPE.RESOURCE_REMEMBERED, RESOURCE_TYPE.RESOURCETYPE_DISK, RESOURCE_USAGE.RESOURCEUSAGE_ALL,
                resource, out ptrHandle);
            if (iRet != 0)
                return null;

            int entries = -1;
            int buffer = 16384;
            IntPtr ptrBuffer = Marshal.AllocHGlobal(buffer);
            NetResource nr;

            iRet = WNetEnumResource(ptrHandle, ref entries, ptrBuffer, ref buffer);
            while ((iRet == 0) || (entries > 0))
            {
                Int32 ptr = ptrBuffer.ToInt32();
                for (int i = 0; i < entries; i++)
                {
                    nr = (NetResource)Marshal.PtrToStructure(new IntPtr(ptr), typeof(NetResource));
                    if (RESOURCE_USAGE.RESOURCEUSAGE_CONTAINER == (nr.dwUsage
                        & RESOURCE_USAGE.RESOURCEUSAGE_CONTAINER))
                    {
                        //call recursively to get all entries in a container
                        WNetResource(nr);
                    }
                    ptr += Marshal.SizeOf(nr);
                    result.Add(nr.lpLocalName, nr);
                }

                entries = -1;
                buffer = 16384;
                iRet = WNetEnumResource(ptrHandle, ref entries, ptrBuffer, ref buffer);
            }

            Marshal.FreeHGlobal(ptrBuffer);
            iRet = WNetCloseEnum(ptrHandle);
        }
        catch (Exception)
        {
        }

        return result;
    }
    public static System.Collections.Generic.Dictionary<string, NetResource> WNetResource()
    {
        return WNetResource(null);
    }

You've got to call the WNetResource(), and you will get back the list of drives. (and cake :-))

Laomedon answered 14/12, 2009 at 19:3 Comment(0)
J
4

Environment.GetLogicalDrives() and DriveInfo.GetDrives() both returned all my networked drives.

Is your application running as a different user (For example an asp.net website)? If it is, are the drives actually mapped for that user? You might find that the drives are mapped for you but they aren't actually mapped for the user your application is running as.

Jolo answered 13/12, 2009 at 23:33 Comment(4)
No, it is a WinForm application, and I didn't change users. Can this be that the UAC (which I keep turned on) starts my app with the normal user, while the mapped drives are linked to my real (administrator) user? I doubt it... but maybe.Laomedon
I doubt it, you don't generally have to grant administrator access to see file shares. You could always try running the app as an administrator just to see. One thing you could take a look at is System.Security.Principal.WindowsIdentity.GetCurrent().Name which will tell you what user it's currently running as, just to be sure.Jolo
I tried it without UAC, stilll doesn't work. I checked the current user from the app, and it is the same I was using. By the way, the mapped drives are disconnected. I don't think that should mean any difference, but maybe it helps.Laomedon
Yes, if the mapped drive is disconnected that will make a difference. If your network drive is offline then it's not actually attached and hence isn't a logical drive. You have to go into far messier to do that. The only method I've used for this (WNetRestoreConnectionW) has a big "Not available from vista onwards" sign on it's MSDN page so I'm not 100% sure how you'll do this.Jolo

© 2022 - 2024 — McMap. All rights reserved.