以编程方式查找网络位置

介绍

上周,我在互联网上正在努力寻找一个解决方案来解决一个有趣的问题,我正在构建一个“Windows资源管理器”的应用程序时遇到的问题。我无法访问通常在Windows资源管理器的“此PC”节点下看到的网络位置。

在环顾一个好时机之后,我终于找到了我需要的,并为那些有兴趣以编程方式浏览的人创建一个快速帮助库。

背景

为此,我们将只需要的C#的工作知识,它可能是有帮助的如何浏览目录在C#中的一些知识System.IO.DriveInfoSystem.IO.DirectoryInfo类,静态方法和SHELL32 DLL。

走过这个过程

我遇到的第一个挑战是试图找到这些网络位置甚至存储的位置。他们必须是SOMEWHERE,最后。看了一会儿后,我终于发现他们实际上是存储在以下目录中的快捷方式:“ %AppData%/Microsoft/Windows/Network Shortcuts

大!我们找到当前用户的“网络快捷方式”!现在,使用C#以编程方式来访问这个位置。首先,我们不能简单地输入,new System.IO.DirectoryInfo("%AppData%/Microsoft/Windows/Network Shortcuts")因为System.IO.DirectoryInfo类(我们将用于浏览这些快捷方式)没有环境变量的任何概念。我们实际上必须输入new System.IO.DirectoryInfo("C:\Users\<<current_user>>\AppData\Roaming\Microsoft\Windows\Network Shortcuts")  (<<current_user>>在Windows中替换为您的用户),尽管这将导致我们进入正确的位置,但这并不能补偿存储此数据的更改。我们实际想要使用的是枚举System.Environment.SpecialFolder要完成这个,我们会进入  System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData)这是%AppData%我们在Windows资源管理器中使用的“ ”环境变量的编程相当的使用System.IO.Path.Combine,我们可以创建完整的目录路径到我们的网络快捷方式:

 
 
  1. private static string NetworkLocationsPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Network Shortcuts");

现在我们已经保存了NetworkShortcuts的位置并准备使用,接下来,我们需要遍历此目录中的每个快捷方式。为此,如上所述,我们将使用System.IO.DirectoryInfo该类。

 
 
  1. DirectoryInfo networkShortcuts = new DirectoryInfo(NetworkLocationsPath);

这些快捷方式不是文件快捷方式,而是文件夹快捷方式,因此,而不是使用System.IO.DirectoryInfo.GetFiles,我们将改为使用System.IO.DirectoryInfo.GetDirectories现在用快速foreach语句,我们将遍历这个文件夹中的每个“目录”。

 

在查看DirectoryInfo类的一个实例中的其中一个快捷方式的每个属性和方法之后,遇到两个问题。首先,我不知道目录是否是快捷方式,其次,如果实例引用快捷方式,我无法获取快捷方式的目标。

看了一会儿后,我找到了两个解决方案来解决我的问题:

我发现的第一个解决方案是一个精采编码的自定义类,它将从快捷方式中提取目标路径(下面的代码),这个类的好处是我不需要引用Shell32 dll。我将类稍微操作为一个静态类,并隐藏了一些暴露的类和属性,我觉得不一定需要暴露。以下不是我的代码,我已经寻找原始的来源,我把代码从而找不到 - 如果你碰巧知道,请让我知道,我会相应地更新这篇文章。

 
 
  1. using System; using System.Runtime.InteropServices; using System.Text; internal static class ShortcutResolver
  2. { #region Signatures imported from http://pinvoke.net [DllImport("shfolder.dll", CharSet = CharSet.Auto)] private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);
  3.  
  4. [Flags()] enum SLGP_FLAGS
  5. { /// <summary>Retrieves the standard short (8.3 format) file name</summary> SLGP_SHORTPATH = 0x1, /// <summary>Retrieves the Universal Naming Convention (UNC) path name of the file</summary> SLGP_UNCPRIORITY = 0x2, /// <summary>Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded</summary> SLGP_RAWPATH = 0x4
  6. }
  7.  
  8. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct WIN32_FIND_DATAW
  9. { public uint dwFileAttributes; public long ftCreationTime; public long ftLastAccessTime; public long ftLastWriteTime; public uint nFileSizeHigh; public uint nFileSizeLow; public uint dwReserved0; public uint dwReserved1;
  10. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName;
  11. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName;
  12. }
  13.  
  14. [Flags()] enum SLR_FLAGS
  15. { /// <summary> /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, /// the high-order word of fFlags can be set to a time-out value that specifies the /// maximum amount of time to be spent resolving the link. The function returns if the /// link cannot be resolved within the time-out duration. If the high-order word is set /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out /// duration, in milliseconds. /// </summary> SLR_NO_UI = 0x1, /// <summary>Obsolete and no longer used</summary> SLR_ANY_MATCH = 0x2, /// <summary>If the link object has changed, update its path and list of identifiers. /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine /// whether or not the link object has changed.</summary> SLR_UPDATE = 0x4, /// <summary>Do not update the link information</summary> SLR_NOUPDATE = 0x8, /// <summary>Do not execute the search heuristics</summary> SLR_NOSEARCH = 0x10, /// <summary>Do not use distributed link tracking</summary> SLR_NOTRACK = 0x20, /// <summary>Disable distributed link tracking. By default, distributed link tracking tracks /// removable media across multiple devices based on the volume name. It also uses the /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter /// has changed. Setting SLR_NOLINKINFO disables both types of tracking.</summary> SLR_NOLINKINFO = 0x40, /// <summary>Call the Microsoft Windows Installer</summary> SLR_INVOKE_MSI = 0x80
  16. } /// <summary>The IShellLink interface allows Shell links to be created, modified, and resolved</summary> [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] interface IShellLinkW
  17. { /// <summary>Retrieves the path and file name of a Shell link object</summary> void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); /// <summary>Retrieves the list of item identifiers for a Shell link object</summary> void GetIDList(out IntPtr ppidl); /// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary> void SetIDList(IntPtr pidl); /// <summary>Retrieves the description string for a Shell link object</summary> void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); /// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary> void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); /// <summary>Retrieves the name of the working directory for a Shell link object</summary> void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); /// <summary>Sets the name of the working directory for a Shell link object</summary> void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); /// <summary>Retrieves the command-line arguments associated with a Shell link object</summary> void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); /// <summary>Sets the command-line arguments for a Shell link object</summary> void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); /// <summary>Retrieves the hot key for a Shell link object</summary> void GetHotkey(out short pwHotkey); /// <summary>Sets a hot key for a Shell link object</summary> void SetHotkey(short wHotkey); /// <summary>Retrieves the show command for a Shell link object</summary> void GetShowCmd(out int piShowCmd); /// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary> void SetShowCmd(int iShowCmd); /// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary> void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); /// <summary>Sets the location (path and index) of the icon for a Shell link object</summary> void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); /// <summary>Sets the relative path to the Shell link object</summary> void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); /// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary> void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); /// <summary>Sets the path and file name of a Shell link object</summary> void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
  18.  
  19. }
  20.  
  21. [ComImport, Guid("0000010c-0000-0000-c000-000000000046"),
  22. InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IPersist
  23. {
  24. [PreserveSig] void GetClassID(out Guid pClassID);
  25. }
  26.  
  27. [ComImport, Guid("0000010b-0000-0000-C000-000000000046"),
  28. InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IPersistFile : IPersist
  29. { new void GetClassID(out Guid pClassID);
  30. [PreserveSig] int IsDirty();
  31.  
  32. [PreserveSig] void Load([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);
  33.  
  34. [PreserveSig] void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
  35. [In, MarshalAs(UnmanagedType.Bool)] bool fRemember);
  36.  
  37. [PreserveSig] void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
  38.  
  39. [PreserveSig] void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
  40. } const uint STGM_READ = 0; const int MAX_PATH = 260; // CLSID_ShellLink from ShlGuid.h [
  41. ComImport(),
  42. Guid("00021401-0000-0000-C000-000000000046")
  43. ] private class ShellLink
  44. {
  45. } #endregion public static string GetPath(string filename)
  46. {
  47. ShellLink link = new ShellLink();
  48. ((IPersistFile)link).Load(filename, STGM_READ); // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. // ((IShellLinkW)link).Resolve(hwnd, 0) StringBuilder sb = new StringBuilder(MAX_PATH);
  49. WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
  50. ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); return sb.ToString();
  51. }
  52. }

我发现的第二个解决方案是通过Shell32Microsoft Shell控件和自动化 COM类型库中引用该库。要在Visual Studio中添加此项目,请在项目中右键单击“ 引用”,单击“ 添加引用”,展开“ 参考管理器 ”左侧COM选项,单击“ 类型库”,向下滚动到“ Microsoft Shell控件和自动化”,检查复选框,然后单击确定。 

现在,要访问这些信息,我们将使用下面的代码。我设置了这个代码来使用静态方法和变量,以便将它并入到稍后将要介绍的助手类中。

 
 
  1. private static string NetworkLocationsPath { get; } = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Network Shortcuts"); #region Shell Support private static Shell32.Folder _networkLocationsFolder; private static Shell32.Folder NetworkLocationsFolder
  2. { get { if (_networkLocationsFolder == null)
  3. {
  4. Shell32.Shell shell = new Shell32.Shell();
  5. _networkLocationsFolder = shell.NameSpace(NetworkLocationsPath);
  6. } return _networkLocationsFolder;
  7. }
  8. } private static string GetShortCutPath(string name)
  9. { try {
  10. Shell32.FolderItem folderItem = NetworkLocationsFolder.ParseName(name); if (folderItem == null || !folderItem.IsLink) return null;
  11.  
  12. Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink; return link.Path;
  13. } catch { return null;
  14. }
  15. } #endregion

使用外推路径信息,我可以System.IO.DirectoryInfo使用该路径创建一个新的实例。

 

以上是方便的,但是,我想把所有这一切都包装成一个简单的类,它将类似于System.IO.DriveInfo类,这将允许我获取所有的网络位置,并处理所有的名称解析,快捷方式目标提取和有效为我提供一站式服务。

 

我决定在System.IO命名空间中创建这个类,尽管你可以随意改变,但是你认为合适。

 
 
  1. using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; namespace System.IO
  2. { /// <summary> /// Provides access to information on a Network Location. /// </summary> [ComVisible(true)] public sealed class NetworkLocationInfo : ISerializable
  3. { /// <summary> /// Gets a value that indicates whether a network location is ready. /// </summary> public bool IsReady { get { return RootDirectory.Exists; } } /// <summary> /// Gets a value that indicates whether a network location is mapped. /// </summary> public bool IsMapped { get { return ShortcutFile.Exists; } } /// <summary> /// Gets the name of the share, such as \\192.168.100.1\data. /// </summary> public string Name { get { return RootDirectory.FullName; } } /// <summary> /// Gets the share name on the server /// </summary> public string ShareName { get; } /// <summary> /// Gets the name or IP address of the server /// </summary> public string ServerName { get; } /// <summary> /// Gets the share label of a network location /// </summary> public string ShareLabel
  4. { get { return ShortcutFile.Name; } set { if (!ShortcutFile.Exists) throw new FileNotFoundException("Cannot find the network location shortcut file");
  5.  
  6. ShortcutFile.MoveTo(Path.Combine(ShortcutFile.Parent.FullName, value));
  7. }
  8. } /// <summary> /// Gets the root directory of a network location. /// </summary> public DirectoryInfo RootDirectory { get; } private DirectoryInfo _shortcutFile; private DirectoryInfo ShortcutFile { get { if (_shortcutFile == null)
  9. _shortcutFile = FindShortCutFile(); return _shortcutFile;
  10. }
  11. } /// <summary> /// Provides access to information on the specified network location. /// </summary> [SecuritySafeCritical] public NetworkLocationInfo(string networkLocationPath)
  12. : this(networkLocationPath, null) { } private NetworkLocationInfo(string networkLocationPath, DirectoryInfo shortcutFile)
  13. { if (string.IsNullOrWhiteSpace(networkLocationPath)) throw new ArgumentNullException(nameof(networkLocationPath)); if (!networkLocationPath.StartsWith("\\\\")) throw new ArgumentException("The UNC path should be of the form \\\\server\\share"); string root = networkLocationPath.TrimStart("\\"); // TODO: This needs to use a apostrophe (single quote) instead of a double quote, but does not format properly in the article with a single quote. int i = root.IndexOf("\\"); if (i < 0 || i + 1 == root.Length) throw new ArgumentException("The UNC path should be of the form \\\\server\\share");
  14.  
  15. ServerName = root.Substring(0, i);
  16. root = root.Substring(i + 1);
  17. i = root.IndexOf("\\");
  18. ShareName = i < 0 ? root : root.Substring(0, i); if (string.IsNullOrWhiteSpace(ShareName)) throw new ArgumentException("The UNC path should be of the form \\\\server\\share");
  19.  
  20. RootDirectory = new DirectoryInfo(string.Concat("\\\\", ServerName, "\\", ShareName));
  21.  
  22. _shortcutFile = shortcutFile ?? FindShortCutFile();
  23. } private DirectoryInfo FindShortCutFile()
  24. {
  25. DirectoryInfo network = new DirectoryInfo(NetworkLocationsPath); foreach (DirectoryInfo dir in network.EnumerateDirectories())
  26. { string path = GetShortCutPath(dir.Name); if (string.Equals(RootDirectory.FullName, path, StringComparison.OrdinalIgnoreCase)) return dir;
  27. } return new DirectoryInfo(Path.Combine(NetworkLocationsPath, string.Concat(ShareName, " (", ServerName, ")")));
  28. } #region ISerializable methods /// <summary> /// Creates a new instance through deserialization /// </summary> internal NetworkLocationInfo(SerializationInfo info, StreamingContext context)
  29. { if (info == null) throw new ArgumentNullException(nameof(info));
  30.  
  31. ShareName = info.GetString(nameof(ShareName));
  32. ServerName = info.GetString(nameof(ServerName));
  33. RootDirectory = new DirectoryInfo(info.GetString(nameof(RootDirectory)));
  34. _shortcutFile = new DirectoryInfo(info.GetString(nameof(ShortcutFile)));
  35. } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  36. { if (info == null) throw new ArgumentNullException(nameof(info));
  37.  
  38. info.AddValue(nameof(ShareName), ShareName);
  39. info.AddValue(nameof(ServerName), ServerName);
  40. info.AddValue(nameof(RootDirectory), RootDirectory.FullName);
  41. info.AddValue(nameof(ShortcutFile), ShortcutFile.FullName);
  42. } #endregion #region Object methods public override string ToString()
  43. { return Name;
  44. } public override bool Equals(object obj)
  45. { if (obj == null) return false; if (obj is NetworkLocationInfo) return string.Equals(((NetworkLocationInfo)obj).Name, Name, StringComparison.OrdinalIgnoreCase); return false;
  46. } public override int GetHashCode()
  47. { return Name.GetHashCode();
  48. } #endregion #region Static Methods private static string NetworkLocationsPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft", "Windows", "Network Shortcuts"); #region Shell Support private static Shell32.Folder _networkLocationsFolder; private static Shell32.Folder NetworkLocationsFolder
  49. { get { if (_networkLocationsFolder == null)
  50. {
  51. Shell32.Shell shell = new Shell32.Shell();
  52. _networkLocationsFolder = shell.NameSpace(NetworkLocationsPath);
  53. } return _networkLocationsFolder;
  54. }
  55. } private static string GetShortCutPath(string name)
  56. { try {
  57. Shell32.FolderItem folderItem = NetworkLocationsFolder?.ParseName(name); if (folderItem == null || !folderItem.IsLink) return null;
  58.  
  59. Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink; return link.Path;
  60. } catch { return null;
  61. }
  62. } #endregion /// <summary> /// Gets all of the network locations found. /// </summary> public static NetworkLocationInfo[] GetNetworkLocations()
  63. { if (NetworkLocationsFolder == null) return new NetworkLocationInfo[] { };
  64.  
  65. DirectoryInfo networkShortcuts = new DirectoryInfo(NetworkLocationsPath);
  66.  
  67. DirectoryInfo[] subDirectories = networkShortcuts.GetDirectories();
  68.  
  69. NetworkLocationInfo[] locations = new NetworkLocationInfo[subDirectories.Length]; if (subDirectories.Length == 0) return locations; int i = 0; foreach (DirectoryInfo dir in subDirectories)
  70. { string networkLocationPath = GetShortCutPath(dir.Name); if (string.IsNullOrWhiteSpace(networkLocationPath)) continue; try {
  71. NetworkLocationInfo info = new NetworkLocationInfo(networkLocationPath, dir);
  72.  
  73. locations[i++] = info;
  74. } catch { continue; }
  75. } if (i < locations.Length)
  76. Array.Resize(ref locations, i); return locations;
  77. } #endregion }
  78. }

 

有了这个助手类,我们现在可以通过简单的调用浏览我们的网络位置快捷方式 NetworkLocationInfo.GetNetworkLocations()

 

对于我的用途,我正在创建一个具有所有网络位置的树,下面是我如何实现这个帮助类。

 
 
  1. // Load Network Locations foreach (NetworkLocationInfo n in NetworkLocationInfo.GetNetworkLocations())
  2. { if (!n.IsReady) continue;
  3.  
  4. TreeNode aNode = new TreeNode(n.ShareLabel, (int)ImageKeys.NetworkDrive, (int)ImageKeys.NetworkDrive);
  5.  
  6. aNode.Tag = n.RootDirectory;
  7. aNode.ImageKey = "network-location"; try {
  8. GetDirectories(n.RootDirectory.EnumerateDirectories(), aNode);
  9. nodeToAddTo.Nodes.Add(aNode);
  10. } catch { }
  11. }

 

享受快乐编码!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值