【问题标题】:Network folder browser service网络文件夹浏览器服务
【发布时间】:2012-04-06 05:54:24
【问题描述】:

网上有一些与此类似的问题 - 甚至这里有几个,但即使提问者看起来很高兴,我也找不到真正满足我需要的问题。

我正在尝试将远程目录浏览功能添加到基于 Web 的管理控制面板(基于 Intranet)。

此时我无需担心安全问题,因为这是在其他地方处理的。

为此,我使用了一个接受服务器名称和共享/文件夹路径作为参数的网络服务。我只需要它返回此路径的子目录(如果有)。

听起来没那么难,是吗?嗯,它是(至少对我来说!)

我需要帮助的唯一一点实际上是为提供的服务器和路径生成目录列表。

感谢所有帮助,但请不要仅仅链接到我可能已经看过但未能获得有效解决方案的网站;其中大多数甚至似乎都没有尝试做标题所暗示的事情。 一些解释也会有所帮助!

干杯

【问题讨论】:

  • “网络文件夹”是什么意思?中小企业份额? NFS? FTP?网络视频?对于这些协议中的每一个,列出一个文件夹是完全不同的......
  • 这些是标准的 Windows Server 共享文件夹及其子文件夹。

标签: c# web-services browser filesystems directory


【解决方案1】:

要枚举 .NET 中指定文件夹的子文件夹,您可以使用例如 DirectoryInfo.EnumerateDirectories 方法。

要枚举某些计算机的共享,如果隐藏的管理共享(如 C$ADMIN$print$ 等对您不重要)或使用 NetShareEnum 枚举所有共享,您可以使用 WNetEnumResource 本机函数.

对应的代码可以是

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.InteropServices;

namespace Subfolders {
    static internal class Native {
        [DllImport ("Netapi32.dll", SetLastError = true)]
        internal static extern uint NetApiBufferFree (IntPtr buffer);

        [DllImport ("Netapi32.dll", CharSet = CharSet.Unicode)]
        internal static extern uint NetShareEnum (
             string serverName,
             int level,
             ref IntPtr bufPtr,
             uint prefmaxlen,
             ref int entriesread,
             ref int totalentries,
             ref int resumeHandle
        );

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

        [DllImport ("MPR.dll", CharSet = CharSet.Auto)]
        internal static extern uint WNetOpenEnum(ResourceScope dwScope, ResourceType dwType, ResourceUsage dwUsage,
            IntPtr lpNetResource, out IntPtr lphEnum);

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

        internal const uint MaxPreferredLength = 0xFFFFFFFF;
        internal const int NerrSuccess = 0;
        internal enum NetError : uint {
            NerrSuccess = 0,
            NerrBase = 2100,
            NerrUnknownDevDir = (NerrBase + 16),
            NerrDuplicateShare = (NerrBase + 18),
            NerrBufTooSmall = (NerrBase + 23),
        }
        internal enum ShareType : uint {
            StypeDisktree = 0,
            StypePrintq = 1,
            StypeDevice = 2,
            StypeIpc = 3,
            StypeSpecial = 0x80000000,
        }
        [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct ShareInfo1 {
            public string shi1_netname;
            public uint shi1_type;
            public string shi1_remark;
            public ShareInfo1 (string sharename, uint sharetype, string remark) {
                shi1_netname = sharename;
                shi1_type = sharetype;
                shi1_remark = remark;
            }
            public override string ToString () {
                return shi1_netname;
            }
        }
        public enum ResourceScope: uint {
            ResourceConnected = 0x00000001,
            ResourceGlobalnet = 0x00000002,
            ResourceRemembered = 0x00000003,
            ResourceRecent = 0x00000004,
            ResourceContext = 0x00000005
        }
        public enum ResourceType: uint {
            ResourcetypeAny = 0x00000000,
            ResourcetypeDisk = 0x00000001,
            ResourcetypePrint = 0x00000002,
            ResourcetypeReserved = 0x00000008,
            ResourcetypeUnknown = 0xFFFFFFFF
        }
        public enum ResourceUsage: uint {
            ResourceusageConnectable = 0x00000001,
            ResourceusageContainer = 0x00000002,
            ResourceusageNolocaldevice = 0x00000004,
            ResourceusageSibling = 0x00000008,
            ResourceusageAttached = 0x00000010,
            ResourceusageAll = (ResourceusageConnectable | ResourceusageContainer | ResourceusageAttached),
            ResourceusageReserved = 0x80000000
        }
        public enum ResourceDisplaytype: uint {
            ResourcedisplaytypeGeneric = 0x00000000,
            ResourcedisplaytypeDomain = 0x00000001,
            ResourcedisplaytypeServer = 0x00000002,
            ResourcedisplaytypeShare = 0x00000003,
            ResourcedisplaytypeFile = 0x00000004,
            ResourcedisplaytypeGroup = 0x00000005,
            ResourcedisplaytypeNetwork = 0x00000006,
            ResourcedisplaytypeRoot = 0x00000007,
            ResourcedisplaytypeShareadmin = 0x00000008,
            ResourcedisplaytypeDirectory = 0x00000009,
            ResourcedisplaytypeTree = 0x0000000A,
            ResourcedisplaytypeNdscontainer = 0x0000000B
        }
        [StructLayout (LayoutKind.Sequential)]
        public struct NetResource {
            public ResourceScope dwScope;
            public ResourceType dwType;
            public ResourceDisplaytype dwDisplayType;
            public ResourceUsage dwUsage;
            [MarshalAs (UnmanagedType.LPTStr)]
            public string lpLocalName;
            [MarshalAs (UnmanagedType.LPTStr)]
            public string lpRemoteName;
            [MarshalAs (UnmanagedType.LPTStr)]
            public string lpComment;
            [MarshalAs (UnmanagedType.LPTStr)]
            public string lpProvider;
        }
    }
    class Program {
        static IEnumerable<string> GetShares(string computerName) {
            var resources = new List<string>();
            IntPtr hEnum = IntPtr.Zero, pResource = IntPtr.Zero;
            try {
                var resource = new Native.NetResource();
                int bufferSize = 163840;
                resource.dwType = Native.ResourceType.ResourcetypeAny;
                resource.dwScope = Native.ResourceScope.ResourceGlobalnet;
                resource.dwUsage = Native.ResourceUsage.ResourceusageContainer;
                resource.lpRemoteName = computerName;
                pResource = Marshal.AllocHGlobal(Marshal.SizeOf(resource));
                Marshal.StructureToPtr (resource, pResource, false);
                uint status = Native.WNetOpenEnum (Native.ResourceScope.ResourceGlobalnet,
                                                   Native.ResourceType.ResourcetypeDisk,
                                                   0,
                                                   pResource,
                                                   out hEnum);
                if (status != 0)
                    return resources;

                int numberOfEntries = -1;
                IntPtr pBuffer = Marshal.AllocHGlobal(bufferSize);
                status = Native.WNetEnumResource (hEnum, ref numberOfEntries, pBuffer, ref bufferSize);
                if (status == Native.NerrSuccess && numberOfEntries > 0) {
                    var ptr = pBuffer;
                    for (int i = 0; i < numberOfEntries; i++, ptr += Marshal.SizeOf(resource)) {
                        resource = (Native.NetResource)Marshal.PtrToStructure (ptr, typeof (Native.NetResource));
                        resources.Add (resource.lpRemoteName.StartsWith (computerName + '\\',
                                                                         StringComparison.OrdinalIgnoreCase)
                                           ? resource.lpRemoteName.Substring (computerName.Length + 1)
                                           : resource.lpRemoteName);
                    }
                }
            } finally {
                if (hEnum != IntPtr.Zero) {
                    Native.WNetCloseEnum (hEnum);
                }
                if (pResource != IntPtr.Zero) {
                    Marshal.FreeHGlobal(pResource);
                }
            }
            return resources;
        }

        static IEnumerable<string> GetAllShares (string computerName) {
            var shares = new List<string> ();
            IntPtr bufPtr = IntPtr.Zero;
            int entriesread = 0;
            int totalentries = 0;
            int resumeHandle = 0;
            int nStructSize = Marshal.SizeOf (typeof (Native.ShareInfo1));
            try {
                uint ret = Native.NetShareEnum (computerName, 1, ref bufPtr,
                    Native.MaxPreferredLength,
                    ref entriesread,
                    ref totalentries,
                    ref resumeHandle);
                if (ret == (uint)Native.NetError.NerrSuccess) {
                    var currentPtr = bufPtr;
                    for (int i = 0; i < entriesread; i++) {
                        var shi1 = (Native.ShareInfo1)Marshal.PtrToStructure (currentPtr, typeof (Native.ShareInfo1));
                        if ((shi1.shi1_type & ~(uint)Native.ShareType.StypeSpecial) == (uint)Native.ShareType.StypeDisktree) {
                            shares.Add (shi1.shi1_netname);
                        }
                        currentPtr = new IntPtr (currentPtr.ToInt32 () + nStructSize);
                    }
                }
            } finally {
                if (bufPtr != IntPtr.Zero)
                    Native.NetApiBufferFree (bufPtr);
            }
            return shares;
        }
        static IEnumerable<string> GetSubdirectories (string root) {
            var dirInfo = new DirectoryInfo (root);
            return (from info in dirInfo.EnumerateDirectories () select info.Name).ToList();
        }
        static void Main () {
            var root = @"\\OK01\Users";
            Console.WriteLine ("Subdirectories of {0}:", root);
            foreach (var dir in GetSubdirectories (root)) {
                Console.WriteLine (dir);
            }

            Console.WriteLine ();
            root = @"\\OK01\Users\Public";
            Console.WriteLine ("Subdirectories of {0}:", root);
            foreach (var dir in GetSubdirectories (root)) {
                Console.WriteLine (dir);
            }

            Console.WriteLine ();
            root = @"\\OK01";
            Console.WriteLine ("All Shares of {0} (inclusive hidden):", root);
            foreach (var shareName in GetAllShares (root)) {
                Console.WriteLine (shareName);
            }

            Console.WriteLine ();
            root = @"\\OK01";
            Console.WriteLine ("Shares of {0}:", root);
            foreach (var shareName in GetShares (root)) {
                Console.WriteLine (shareName);
            }
        }
    }
}

产生类似的输出

Subdirectories of \\OK01\Users:
All Users
ASP.NET v4.0
Default
Default User
MSSQL$SQL2012
Oleg
Public

Subdirectories of \\OK01\Users\Public:
Desktop
Documents
Downloads
Favorites
Libraries
Music
Pictures
Recorded TV
Roaming
Videos

All Shares of \\OK01 (inclusive hidden):
ADMIN$
C$
print$
Q$
Users
Virtual Machines
VMware

Shares of \\OK01:
Users
Virtual Machines
VMware

以上代码经过简化,仅演示如何使用相应的 API。它不包含真正的错误报告。

【讨论】:

  • 修复64位系统上的溢出错误,将GetAllShares中的currentPtr.ToInt32改成ToInt64(我自己没试过,不过我觉得这个改应该也行在 32 位系统上)。
【解决方案2】:

您可以使用here using Interop描述的方法。

我对代码进行了一些修改以提出这一点。 我没有对此进行广泛的测试,所以它可能有错误,但它应该能让你开始。

private List<string> GetSubDirectories(string serverName, string folderPath)
{
        List<string> subDirectories = new List<string>();
        string folder_path = Path.Combine(serverName, folderPath);            
        IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

        WIN32_FIND_DATA findData;

        IntPtr findHandle;

        findHandle = FindFirstFile(folder_path, out findData);
        if (findHandle == INVALID_HANDLE_VALUE)
        {
            int error = Marshal.GetLastWin32Error();
            Console.WriteLine(error.ToString());
            return null;
        }

        do
        {
            try
            {
                if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
                    subDirectories.Add(findData.cFileName);                    
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
        while (FindNextFile(findHandle, out findData));
        FindClose(findHandle);

        return subDirectories;
    }

    public const int FILE_ATTRIBUTE_DIRECTORY = 0x10;

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool FindClose(IntPtr hFindFile);

    [StructLayout(LayoutKind.Sequential)]
    public struct FILETIME
    {
        public uint dwLowDateTime;
        public uint dwHighDateTime;
    };

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct WIN32_FIND_DATA
    {
        public uint dwFileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
        public uint nFileSizeHigh;
        public uint nFileSizeLow;
        public uint dwReserved0;
        public uint dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }


    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

}

你可以这样称呼它:

var subdirectories = GetSubDirectories(@"\\[serverName]", @"[folderPath]\*");

你必须添加“\*”as per MSDN

在网络共享上,您可以使用 lpFileName 的形式 以下:“\服务器\共享*”。但是,您不能使用 lpFileName 指向股份本身;例如,“\Server\Share”不是 有效。

【讨论】:

  • 谢谢 - 只要我有共享名称,这似乎就可以工作。有没有办法也列出股票,或者这完全不同?
  • 要枚举共享,您需要一个方法为described here
  • 这是最适合我的解决方案。感谢所有建议。
【解决方案3】:

嗯,实际上可以使用NetShareEnum Win32API function 来完成。

但这里是 .NET 包装类,用于枚举本地和远程机器上的网络共享,并将本地文件路径转换为 ​​UNC 路径。请看文章Network Shares and UNC paths

【讨论】:

  • 我看过那篇文章,但它不能满足我的需要。我不想枚举本地计算机上的共享,我想知道远程计算机上的共享 - 这就是需要服务器名称的原因。
  • 你见过ShareCollection类的GetShares静态方法吗?
  • 我现在有! :$ 谢谢 - 我现在正在研究不同的选项。
【解决方案4】:

不确定我们是否能做到这一点。我们遇到了类似的问题,但最终通过提供共享路径 (\SERVERNAME\FOLDER) 解决了它。

最重要的网络服务应该使用具有完全权限的帐户来访问目录,否则与权限相关的异常将被抛出给调用客户端。

【讨论】:

    猜你喜欢
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2012-10-30
    • 2014-04-08
    • 1970-01-01
    • 2012-07-20
    相关资源
    最近更新 更多