/Microsoft.Experimental.IO/Microsoft.Experimental.IO/LongPathDirectory.cs

http://longpaths.codeplex.com · C# · 926 lines · 173 code · 62 blank · 691 comment · 40 complexity · 1bd309a2f342eb1cfd45b5a885bc5a86 MD5 · raw file

  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. using Microsoft.Experimental.IO.Interop;
  8. namespace Microsoft.Experimental.IO {
  9. /// <summary>
  10. /// Provides methods for creating, deleting, moving and enumerating directories and
  11. /// subdirectories with long paths, that is, paths that exceed 259 characters.
  12. /// </summary>
  13. public static class LongPathDirectory {
  14. /// <summary>
  15. /// Creates the specified directory.
  16. /// </summary>
  17. /// <param name="path">
  18. /// A <see cref="String"/> containing the path of the directory to create.
  19. /// </param>
  20. /// <exception cref="ArgumentNullException">
  21. /// <paramref name="path"/> is <see langword="null"/>.
  22. /// </exception>
  23. /// <exception cref="ArgumentException">
  24. /// <paramref name="path"/> is an empty string (""), contains only white
  25. /// space, or contains one or more invalid characters as defined in
  26. /// <see cref="Path.GetInvalidPathChars()"/>.
  27. /// <para>
  28. /// -or-
  29. /// </para>
  30. /// <paramref name="path"/> contains one or more components that exceed
  31. /// the drive-defined maximum length. For example, on Windows-based
  32. /// platforms, components must not exceed 255 characters.
  33. /// </exception>
  34. /// <exception cref="PathTooLongException">
  35. /// <paramref name="path"/> exceeds the system-defined maximum length.
  36. /// For example, on Windows-based platforms, paths must not exceed
  37. /// 32,000 characters.
  38. /// </exception>
  39. /// <exception cref="DirectoryNotFoundException">
  40. /// <paramref name="path"/> contains one or more directories that could not be
  41. /// found.
  42. /// </exception>
  43. /// <exception cref="UnauthorizedAccessException">
  44. /// The caller does not have the required access permissions.
  45. /// </exception>
  46. /// <exception cref="IOException">
  47. /// <paramref name="path"/> is a file.
  48. /// <para>
  49. /// -or-
  50. /// </para>
  51. /// <paramref name="path"/> specifies a device that is not ready.
  52. /// </exception>
  53. /// <remarks>
  54. /// Note: Unlike <see cref="Directory.CreateDirectory(System.String)"/>, this method only creates
  55. /// the last directory in <paramref name="path"/>.
  56. /// </remarks>
  57. public static void Create(string path) {
  58. string normalizedPath = LongPathCommon.NormalizeLongPath(path);
  59. if (!NativeMethods.CreateDirectory(normalizedPath, IntPtr.Zero)) {
  60. // To mimic Directory.CreateDirectory, we don't throw if the directory (not a file) already exists
  61. int errorCode = Marshal.GetLastWin32Error();
  62. if (errorCode != NativeMethods.ERROR_ALREADY_EXISTS || !LongPathDirectory.Exists(path)) {
  63. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  64. }
  65. }
  66. }
  67. /// <summary>
  68. /// Deletes the specified empty directory.
  69. /// </summary>
  70. /// <param name="path">
  71. /// A <see cref="String"/> containing the path of the directory to delete.
  72. /// </param>
  73. /// <exception cref="ArgumentNullException">
  74. /// <paramref name="path"/> is <see langword="null"/>.
  75. /// </exception>
  76. /// <exception cref="ArgumentException">
  77. /// <paramref name="path"/> is an empty string (""), contains only white
  78. /// space, or contains one or more invalid characters as defined in
  79. /// <see cref="Path.GetInvalidPathChars()"/>.
  80. /// <para>
  81. /// -or-
  82. /// </para>
  83. /// <paramref name="path"/> contains one or more components that exceed
  84. /// the drive-defined maximum length. For example, on Windows-based
  85. /// platforms, components must not exceed 255 characters.
  86. /// </exception>
  87. /// <exception cref="PathTooLongException">
  88. /// <paramref name="path"/> exceeds the system-defined maximum length.
  89. /// For example, on Windows-based platforms, paths must not exceed
  90. /// 32,000 characters.
  91. /// </exception>
  92. /// <exception cref="DirectoryNotFoundException">
  93. /// <paramref name="path"/> could not be found.
  94. /// </exception>
  95. /// <exception cref="UnauthorizedAccessException">
  96. /// The caller does not have the required access permissions.
  97. /// <para>
  98. /// -or-
  99. /// </para>
  100. /// <paramref name="path"/> refers to a directory that is read-only.
  101. /// </exception>
  102. /// <exception cref="IOException">
  103. /// <paramref name="path"/> is a file.
  104. /// <para>
  105. /// -or-
  106. /// </para>
  107. /// <paramref name="path"/> refers to a directory that is not empty.
  108. /// <para>
  109. /// -or-
  110. /// </para>
  111. /// <paramref name="path"/> refers to a directory that is in use.
  112. /// <para>
  113. /// -or-
  114. /// </para>
  115. /// <paramref name="path"/> specifies a device that is not ready.
  116. /// </exception>
  117. public static void Delete(string path) {
  118. string normalizedPath = LongPathCommon.NormalizeLongPath(path);
  119. if (!NativeMethods.RemoveDirectory(normalizedPath)) {
  120. throw LongPathCommon.GetExceptionFromLastWin32Error();
  121. }
  122. }
  123. /// <summary>
  124. /// Returns a value indicating whether the specified path refers to an existing directory.
  125. /// </summary>
  126. /// <param name="path">
  127. /// A <see cref="String"/> containing the path to check.
  128. /// </param>
  129. /// <returns>
  130. /// <see langword="true"/> if <paramref name="path"/> refers to an existing directory;
  131. /// otherwise, <see langword="false"/>.
  132. /// </returns>
  133. /// <remarks>
  134. /// Note that this method will return false if any error occurs while trying to determine
  135. /// if the specified directory exists. This includes situations that would normally result in
  136. /// thrown exceptions including (but not limited to); passing in a directory name with invalid
  137. /// or too many characters, an I/O error such as a failing or missing disk, or if the caller
  138. /// does not have Windows or Code Access Security (CAS) permissions to to read the directory.
  139. /// </remarks>
  140. public static bool Exists(string path) {
  141. bool isDirectory;
  142. if (LongPathCommon.Exists(path, out isDirectory)) {
  143. return isDirectory;
  144. }
  145. return false;
  146. }
  147. /// <summary>
  148. /// Returns a enumerable containing the directory names of the specified directory.
  149. /// </summary>
  150. /// <param name="path">
  151. /// A <see cref="String"/> containing the path of the directory to search.
  152. /// </param>
  153. /// <returns>
  154. /// A <see cref="IEnumerable{T}"/> containing the directory names within <paramref name="path"/>.
  155. /// </returns>
  156. /// <exception cref="ArgumentNullException">
  157. /// <paramref name="path"/> is <see langword="null"/>.
  158. /// </exception>
  159. /// <exception cref="ArgumentException">
  160. /// <paramref name="path"/> is an empty string (""), contains only white
  161. /// space, or contains one or more invalid characters as defined in
  162. /// <see cref="Path.GetInvalidPathChars()"/>.
  163. /// <para>
  164. /// -or-
  165. /// </para>
  166. /// <paramref name="path"/> contains one or more components that exceed
  167. /// the drive-defined maximum length. For example, on Windows-based
  168. /// platforms, components must not exceed 255 characters.
  169. /// </exception>
  170. /// <exception cref="PathTooLongException">
  171. /// <paramref name="path"/> exceeds the system-defined maximum length.
  172. /// For example, on Windows-based platforms, paths must not exceed
  173. /// 32,000 characters.
  174. /// </exception>
  175. /// <exception cref="DirectoryNotFoundException">
  176. /// <paramref name="path"/> contains one or more directories that could not be
  177. /// found.
  178. /// </exception>
  179. /// <exception cref="UnauthorizedAccessException">
  180. /// The caller does not have the required access permissions.
  181. /// </exception>
  182. /// <exception cref="IOException">
  183. /// <paramref name="path"/> is a file.
  184. /// <para>
  185. /// -or-
  186. /// </para>
  187. /// <paramref name="path"/> specifies a device that is not ready.
  188. /// </exception>
  189. public static IEnumerable<string> EnumerateDirectories(string path) {
  190. return EnumerateDirectories(path, (string)null, SearchOption.TopDirectoryOnly);
  191. }
  192. /// <summary>
  193. /// Returns a enumerable containing the directory names of the specified directory.
  194. /// </summary>
  195. /// <param name="path">
  196. /// A <see cref="String"/> containing the path of the directory to search.
  197. /// </param>
  198. /// <param name="searchOption">
  199. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  200. /// include all subdirectories or only the current directory.
  201. /// </param>
  202. /// <returns>
  203. /// A <see cref="IEnumerable{T}"/> containing the directory names within <paramref name="path"/>.
  204. /// </returns>
  205. /// <exception cref="ArgumentNullException">
  206. /// <paramref name="path"/> is <see langword="null"/>.
  207. /// </exception>
  208. /// <exception cref="ArgumentException">
  209. /// <paramref name="path"/> is an empty string (""), contains only white
  210. /// space, or contains one or more invalid characters as defined in
  211. /// <see cref="Path.GetInvalidPathChars()"/>.
  212. /// <para>
  213. /// -or-
  214. /// </para>
  215. /// <paramref name="path"/> contains one or more components that exceed
  216. /// the drive-defined maximum length. For example, on Windows-based
  217. /// platforms, components must not exceed 255 characters.
  218. /// </exception>
  219. /// <exception cref="PathTooLongException">
  220. /// <paramref name="path"/> exceeds the system-defined maximum length.
  221. /// For example, on Windows-based platforms, paths must not exceed
  222. /// 32,000 characters.
  223. /// </exception>
  224. /// <exception cref="DirectoryNotFoundException">
  225. /// <paramref name="path"/> contains one or more directories that could not be
  226. /// found.
  227. /// </exception>
  228. /// <exception cref="UnauthorizedAccessException">
  229. /// The caller does not have the required access permissions.
  230. /// </exception>
  231. /// <exception cref="IOException">
  232. /// <paramref name="path"/> is a file.
  233. /// <para>
  234. /// -or-
  235. /// </para>
  236. /// <paramref name="path"/> specifies a device that is not ready.
  237. /// </exception>
  238. public static IEnumerable<string> EnumerateDirectories(string path, SearchOption searchOption)
  239. {
  240. return EnumerateDirectories(path, (string)null, searchOption);
  241. }
  242. /// <summary>
  243. /// Returns a enumerable containing the directory names of the specified directory that
  244. /// match the specified search pattern.
  245. /// </summary>
  246. /// <param name="path">
  247. /// A <see cref="String"/> containing the path of the directory to search.
  248. /// </param>
  249. /// <param name="searchPattern">
  250. /// A <see cref="String"/> containing search pattern to match against the names of the
  251. /// directories in <paramref name="path"/>, otherwise, <see langword="null"/> or an empty
  252. /// string ("") to use the default search pattern, "*".
  253. /// </param>
  254. /// <returns>
  255. /// A <see cref="IEnumerable{T}"/> containing the directory names within <paramref name="path"/>
  256. /// that match <paramref name="searchPattern"/>.
  257. /// </returns>
  258. /// <exception cref="ArgumentNullException">
  259. /// <paramref name="path"/> is <see langword="null"/>.
  260. /// </exception>
  261. /// <exception cref="ArgumentException">
  262. /// <paramref name="path"/> is an empty string (""), contains only white
  263. /// space, or contains one or more invalid characters as defined in
  264. /// <see cref="Path.GetInvalidPathChars()"/>.
  265. /// <para>
  266. /// -or-
  267. /// </para>
  268. /// <paramref name="path"/> contains one or more components that exceed
  269. /// the drive-defined maximum length. For example, on Windows-based
  270. /// platforms, components must not exceed 255 characters.
  271. /// </exception>
  272. /// <exception cref="PathTooLongException">
  273. /// <paramref name="path"/> exceeds the system-defined maximum length.
  274. /// For example, on Windows-based platforms, paths must not exceed
  275. /// 32,000 characters.
  276. /// </exception>
  277. /// <exception cref="DirectoryNotFoundException">
  278. /// <paramref name="path"/> contains one or more directories that could not be
  279. /// found.
  280. /// </exception>
  281. /// <exception cref="UnauthorizedAccessException">
  282. /// The caller does not have the required access permissions.
  283. /// </exception>
  284. /// <exception cref="IOException">
  285. /// <paramref name="path"/> is a file.
  286. /// <para>
  287. /// -or-
  288. /// </para>
  289. /// <paramref name="path"/> specifies a device that is not ready.
  290. /// </exception>
  291. public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern) {
  292. return EnumerateFileSystemEntries(path, searchPattern, true, false, SearchOption.TopDirectoryOnly);
  293. }
  294. /// <summary>
  295. /// Returns a enumerable containing the directory names of the specified directory that
  296. /// match the specified search pattern.
  297. /// </summary>
  298. /// <param name="path">
  299. /// A <see cref="String"/> containing the path of the directory to search.
  300. /// </param>
  301. /// <param name="searchPattern">
  302. /// A <see cref="String"/> containing search pattern to match against the names of the
  303. /// directories in <paramref name="path"/>, otherwise, <see langword="null"/> or an empty
  304. /// string ("") to use the default search pattern, "*".
  305. /// </param>
  306. /// <param name="searchOption">
  307. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  308. /// include all subdirectories or only the current directory.
  309. /// </param>
  310. /// <returns>
  311. /// A <see cref="IEnumerable{T}"/> containing the directory names within <paramref name="path"/>
  312. /// that match <paramref name="searchPattern"/>.
  313. /// </returns>
  314. /// <exception cref="ArgumentNullException">
  315. /// <paramref name="path"/> is <see langword="null"/>.
  316. /// </exception>
  317. /// <exception cref="ArgumentException">
  318. /// <paramref name="path"/> is an empty string (""), contains only white
  319. /// space, or contains one or more invalid characters as defined in
  320. /// <see cref="Path.GetInvalidPathChars()"/>.
  321. /// <para>
  322. /// -or-
  323. /// </para>
  324. /// <paramref name="path"/> contains one or more components that exceed
  325. /// the drive-defined maximum length. For example, on Windows-based
  326. /// platforms, components must not exceed 255 characters.
  327. /// </exception>
  328. /// <exception cref="PathTooLongException">
  329. /// <paramref name="path"/> exceeds the system-defined maximum length.
  330. /// For example, on Windows-based platforms, paths must not exceed
  331. /// 32,000 characters.
  332. /// </exception>
  333. /// <exception cref="DirectoryNotFoundException">
  334. /// <paramref name="path"/> contains one or more directories that could not be
  335. /// found.
  336. /// </exception>
  337. /// <exception cref="UnauthorizedAccessException">
  338. /// The caller does not have the required access permissions.
  339. /// </exception>
  340. /// <exception cref="IOException">
  341. /// <paramref name="path"/> is a file.
  342. /// <para>
  343. /// -or-
  344. /// </para>
  345. /// <paramref name="path"/> specifies a device that is not ready.
  346. /// </exception>
  347. public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption)
  348. {
  349. return EnumerateFileSystemEntries(path, searchPattern, true, false, searchOption);
  350. }
  351. /// <summary>
  352. /// Returns a enumerable containing the file names of the specified directory.
  353. /// </summary>
  354. /// <param name="path">
  355. /// A <see cref="String"/> containing the path of the directory to search.
  356. /// </param>
  357. /// <returns>
  358. /// A <see cref="IEnumerable{T}"/> containing the file names within <paramref name="path"/>.
  359. /// </returns>
  360. /// <exception cref="ArgumentNullException">
  361. /// <paramref name="path"/> is <see langword="null"/>.
  362. /// </exception>
  363. /// <exception cref="ArgumentException">
  364. /// <paramref name="path"/> is an empty string (""), contains only white
  365. /// space, or contains one or more invalid characters as defined in
  366. /// <see cref="Path.GetInvalidPathChars()"/>.
  367. /// <para>
  368. /// -or-
  369. /// </para>
  370. /// <paramref name="path"/> contains one or more components that exceed
  371. /// the drive-defined maximum length. For example, on Windows-based
  372. /// platforms, components must not exceed 255 characters.
  373. /// </exception>
  374. /// <exception cref="PathTooLongException">
  375. /// <paramref name="path"/> exceeds the system-defined maximum length.
  376. /// For example, on Windows-based platforms, paths must not exceed
  377. /// 32,000 characters.
  378. /// </exception>
  379. /// <exception cref="DirectoryNotFoundException">
  380. /// <paramref name="path"/> contains one or more directories that could not be
  381. /// found.
  382. /// </exception>
  383. /// <exception cref="UnauthorizedAccessException">
  384. /// The caller does not have the required access permissions.
  385. /// </exception>
  386. /// <exception cref="IOException">
  387. /// <paramref name="path"/> is a file.
  388. /// <para>
  389. /// -or-
  390. /// </para>
  391. /// <paramref name="path"/> specifies a device that is not ready.
  392. /// </exception>
  393. public static IEnumerable<string> EnumerateFiles(string path) {
  394. return EnumerateFiles(path, (string)null, SearchOption.TopDirectoryOnly);
  395. }
  396. /// <summary>
  397. /// Returns a enumerable containing the file names of the specified directory.
  398. /// </summary>
  399. /// <param name="path">
  400. /// A <see cref="String"/> containing the path of the directory to search.
  401. /// </param>
  402. /// <param name="searchOption">
  403. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  404. /// include all subdirectories or only the current directory.
  405. /// </param>
  406. /// <returns>
  407. /// A <see cref="IEnumerable{T}"/> containing the file names within <paramref name="path"/>.
  408. /// </returns>
  409. /// <exception cref="ArgumentNullException">
  410. /// <paramref name="path"/> is <see langword="null"/>.
  411. /// </exception>
  412. /// <exception cref="ArgumentException">
  413. /// <paramref name="path"/> is an empty string (""), contains only white
  414. /// space, or contains one or more invalid characters as defined in
  415. /// <see cref="Path.GetInvalidPathChars()"/>.
  416. /// <para>
  417. /// -or-
  418. /// </para>
  419. /// <paramref name="path"/> contains one or more components that exceed
  420. /// the drive-defined maximum length. For example, on Windows-based
  421. /// platforms, components must not exceed 255 characters.
  422. /// </exception>
  423. /// <exception cref="PathTooLongException">
  424. /// <paramref name="path"/> exceeds the system-defined maximum length.
  425. /// For example, on Windows-based platforms, paths must not exceed
  426. /// 32,000 characters.
  427. /// </exception>
  428. /// <exception cref="DirectoryNotFoundException">
  429. /// <paramref name="path"/> contains one or more directories that could not be
  430. /// found.
  431. /// </exception>
  432. /// <exception cref="UnauthorizedAccessException">
  433. /// The caller does not have the required access permissions.
  434. /// </exception>
  435. /// <exception cref="IOException">
  436. /// <paramref name="path"/> is a file.
  437. /// <para>
  438. /// -or-
  439. /// </para>
  440. /// <paramref name="path"/> specifies a device that is not ready.
  441. /// </exception>
  442. public static IEnumerable<string> EnumerateFiles(string path, SearchOption searchOption)
  443. {
  444. return EnumerateFiles(path, (string)null, searchOption);
  445. }
  446. /// <summary>
  447. /// Returns a enumerable containing the file names of the specified directory that
  448. /// match the specified search pattern.
  449. /// </summary>
  450. /// <param name="path">
  451. /// A <see cref="String"/> containing the path of the directory to search.
  452. /// </param>
  453. /// <param name="searchPattern">
  454. /// A <see cref="String"/> containing search pattern to match against the names of the
  455. /// files in <paramref name="path"/>, otherwise, <see langword="null"/> or an empty
  456. /// string ("") to use the default search pattern, "*".
  457. /// </param>
  458. /// <returns>
  459. /// A <see cref="IEnumerable{T}"/> containing the file names within <paramref name="path"/>
  460. /// that match <paramref name="searchPattern"/>.
  461. /// </returns>
  462. /// <exception cref="ArgumentNullException">
  463. /// <paramref name="path"/> is <see langword="null"/>.
  464. /// </exception>
  465. /// <exception cref="ArgumentException">
  466. /// <paramref name="path"/> is an empty string (""), contains only white
  467. /// space, or contains one or more invalid characters as defined in
  468. /// <see cref="Path.GetInvalidPathChars()"/>.
  469. /// <para>
  470. /// -or-
  471. /// </para>
  472. /// <paramref name="path"/> contains one or more components that exceed
  473. /// the drive-defined maximum length. For example, on Windows-based
  474. /// platforms, components must not exceed 255 characters.
  475. /// </exception>
  476. /// <exception cref="PathTooLongException">
  477. /// <paramref name="path"/> exceeds the system-defined maximum length.
  478. /// For example, on Windows-based platforms, paths must not exceed
  479. /// 32,000 characters.
  480. /// </exception>
  481. /// <exception cref="DirectoryNotFoundException">
  482. /// <paramref name="path"/> contains one or more directories that could not be
  483. /// found.
  484. /// </exception>
  485. /// <exception cref="UnauthorizedAccessException">
  486. /// The caller does not have the required access permissions.
  487. /// </exception>
  488. /// <exception cref="IOException">
  489. /// <paramref name="path"/> is a file.
  490. /// <para>
  491. /// -or-
  492. /// </para>
  493. /// <paramref name="path"/> specifies a device that is not ready.
  494. /// </exception>
  495. public static IEnumerable<string> EnumerateFiles(string path, string searchPattern) {
  496. return EnumerateFileSystemEntries(path, searchPattern, false, true, SearchOption.TopDirectoryOnly);
  497. }
  498. /// <summary>
  499. /// Returns a enumerable containing the file names of the specified directory that
  500. /// match the specified search pattern.
  501. /// </summary>
  502. /// <param name="path">
  503. /// A <see cref="String"/> containing the path of the directory to search.
  504. /// </param>
  505. /// <param name="searchPattern">
  506. /// A <see cref="String"/> containing search pattern to match against the names of the
  507. /// files in <paramref name="path"/>, otherwise, <see langword="null"/> or an empty
  508. /// string ("") to use the default search pattern, "*".
  509. /// </param>
  510. /// <param name="searchOption">
  511. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  512. /// include all subdirectories or only the current directory.
  513. /// </param>
  514. /// <returns>
  515. /// A <see cref="IEnumerable{T}"/> containing the file names within <paramref name="path"/>
  516. /// that match <paramref name="searchPattern"/>.
  517. /// </returns>
  518. /// <exception cref="ArgumentNullException">
  519. /// <paramref name="path"/> is <see langword="null"/>.
  520. /// </exception>
  521. /// <exception cref="ArgumentException">
  522. /// <paramref name="path"/> is an empty string (""), contains only white
  523. /// space, or contains one or more invalid characters as defined in
  524. /// <see cref="Path.GetInvalidPathChars()"/>.
  525. /// <para>
  526. /// -or-
  527. /// </para>
  528. /// <paramref name="path"/> contains one or more components that exceed
  529. /// the drive-defined maximum length. For example, on Windows-based
  530. /// platforms, components must not exceed 255 characters.
  531. /// </exception>
  532. /// <exception cref="PathTooLongException">
  533. /// <paramref name="path"/> exceeds the system-defined maximum length.
  534. /// For example, on Windows-based platforms, paths must not exceed
  535. /// 32,000 characters.
  536. /// </exception>
  537. /// <exception cref="DirectoryNotFoundException">
  538. /// <paramref name="path"/> contains one or more directories that could not be
  539. /// found.
  540. /// </exception>
  541. /// <exception cref="UnauthorizedAccessException">
  542. /// The caller does not have the required access permissions.
  543. /// </exception>
  544. /// <exception cref="IOException">
  545. /// <paramref name="path"/> is a file.
  546. /// <para>
  547. /// -or-
  548. /// </para>
  549. /// <paramref name="path"/> specifies a device that is not ready.
  550. /// </exception>
  551. public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, SearchOption searchOption)
  552. {
  553. return EnumerateFileSystemEntries(path, searchPattern, false, true, searchOption);
  554. }
  555. /// <summary>
  556. /// Returns a enumerable containing the file and directory names of the specified directory.
  557. /// </summary>
  558. /// <param name="path">
  559. /// A <see cref="String"/> containing the path of the directory to search.
  560. /// </param>
  561. /// <returns>
  562. /// A <see cref="IEnumerable{T}"/> containing the file and directory names within
  563. /// <paramref name="path"/>.
  564. /// </returns>
  565. /// <exception cref="ArgumentNullException">
  566. /// <paramref name="path"/> is <see langword="null"/>.
  567. /// </exception>
  568. /// <exception cref="ArgumentException">
  569. /// <paramref name="path"/> is an empty string (""), contains only white
  570. /// space, or contains one or more invalid characters as defined in
  571. /// <see cref="Path.GetInvalidPathChars()"/>.
  572. /// <para>
  573. /// -or-
  574. /// </para>
  575. /// <paramref name="path"/> contains one or more components that exceed
  576. /// the drive-defined maximum length. For example, on Windows-based
  577. /// platforms, components must not exceed 255 characters.
  578. /// </exception>
  579. /// <exception cref="PathTooLongException">
  580. /// <paramref name="path"/> exceeds the system-defined maximum length.
  581. /// For example, on Windows-based platforms, paths must not exceed
  582. /// 32,000 characters.
  583. /// </exception>
  584. /// <exception cref="DirectoryNotFoundException">
  585. /// <paramref name="path"/> contains one or more directories that could not be
  586. /// found.
  587. /// </exception>
  588. /// <exception cref="UnauthorizedAccessException">
  589. /// The caller does not have the required access permissions.
  590. /// </exception>
  591. /// <exception cref="IOException">
  592. /// <paramref name="path"/> is a file.
  593. /// <para>
  594. /// -or-
  595. /// </para>
  596. /// <paramref name="path"/> specifies a device that is not ready.
  597. /// </exception>
  598. public static IEnumerable<string> EnumerateFileSystemEntries(string path) {
  599. return EnumerateFileSystemEntries(path, (string)null, SearchOption.TopDirectoryOnly);
  600. }
  601. /// <summary>
  602. /// Returns a enumerable containing the file and directory names of the specified directory.
  603. /// </summary>
  604. /// <param name="path">
  605. /// A <see cref="String"/> containing the path of the directory to search.
  606. /// </param>
  607. /// <param name="searchOption">
  608. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  609. /// include all subdirectories or only the current directory.
  610. /// </param>
  611. /// <returns>
  612. /// A <see cref="IEnumerable{T}"/> containing the file and directory names within
  613. /// <paramref name="path"/>.
  614. /// </returns>
  615. /// <exception cref="ArgumentNullException">
  616. /// <paramref name="path"/> is <see langword="null"/>.
  617. /// </exception>
  618. /// <exception cref="ArgumentException">
  619. /// <paramref name="path"/> is an empty string (""), contains only white
  620. /// space, or contains one or more invalid characters as defined in
  621. /// <see cref="Path.GetInvalidPathChars()"/>.
  622. /// <para>
  623. /// -or-
  624. /// </para>
  625. /// <paramref name="path"/> contains one or more components that exceed
  626. /// the drive-defined maximum length. For example, on Windows-based
  627. /// platforms, components must not exceed 255 characters.
  628. /// </exception>
  629. /// <exception cref="PathTooLongException">
  630. /// <paramref name="path"/> exceeds the system-defined maximum length.
  631. /// For example, on Windows-based platforms, paths must not exceed
  632. /// 32,000 characters.
  633. /// </exception>
  634. /// <exception cref="DirectoryNotFoundException">
  635. /// <paramref name="path"/> contains one or more directories that could not be
  636. /// found.
  637. /// </exception>
  638. /// <exception cref="UnauthorizedAccessException">
  639. /// The caller does not have the required access permissions.
  640. /// </exception>
  641. /// <exception cref="IOException">
  642. /// <paramref name="path"/> is a file.
  643. /// <para>
  644. /// -or-
  645. /// </para>
  646. /// <paramref name="path"/> specifies a device that is not ready.
  647. /// </exception>
  648. public static IEnumerable<string> EnumerateFileSystemEntries(string path, SearchOption searchOption)
  649. {
  650. return EnumerateFileSystemEntries(path, (string)null, searchOption);
  651. }
  652. /// <summary>
  653. /// Returns a enumerable containing the file and directory names of the specified directory
  654. /// that match the specified search pattern.
  655. /// </summary>
  656. /// <param name="path">
  657. /// A <see cref="String"/> containing the path of the directory to search.
  658. /// </param>
  659. /// <param name="searchPattern">
  660. /// A <see cref="String"/> containing search pattern to match against the names of the
  661. /// files and directories in <paramref name="path"/>, otherwise, <see langword="null"/>
  662. /// or an empty string ("") to use the default search pattern, "*".
  663. /// </param>
  664. /// <returns>
  665. /// A <see cref="IEnumerable{T}"/> containing the file and directory names within
  666. /// <paramref name="path"/>that match <paramref name="searchPattern"/>.
  667. /// </returns>
  668. /// <exception cref="ArgumentNullException">
  669. /// <paramref name="path"/> is <see langword="null"/>.
  670. /// </exception>
  671. /// <exception cref="ArgumentException">
  672. /// <paramref name="path"/> is an empty string (""), contains only white
  673. /// space, or contains one or more invalid characters as defined in
  674. /// <see cref="Path.GetInvalidPathChars()"/>.
  675. /// <para>
  676. /// -or-
  677. /// </para>
  678. /// <paramref name="path"/> contains one or more components that exceed
  679. /// the drive-defined maximum length. For example, on Windows-based
  680. /// platforms, components must not exceed 255 characters.
  681. /// </exception>
  682. /// <exception cref="PathTooLongException">
  683. /// <paramref name="path"/> exceeds the system-defined maximum length.
  684. /// For example, on Windows-based platforms, paths must not exceed
  685. /// 32,000 characters.
  686. /// </exception>
  687. /// <exception cref="DirectoryNotFoundException">
  688. /// <paramref name="path"/> contains one or more directories that could not be
  689. /// found.
  690. /// </exception>
  691. /// <exception cref="UnauthorizedAccessException">
  692. /// The caller does not have the required access permissions.
  693. /// </exception>
  694. /// <exception cref="IOException">
  695. /// <paramref name="path"/> is a file.
  696. /// <para>
  697. /// -or-
  698. /// </para>
  699. /// <paramref name="path"/> specifies a device that is not ready.
  700. /// </exception>
  701. public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern) {
  702. return EnumerateFileSystemEntries(path, searchPattern, true, true, SearchOption.TopDirectoryOnly);
  703. }
  704. /// <summary>
  705. /// Returns a enumerable containing the file and directory names of the specified directory
  706. /// that match the specified search pattern.
  707. /// </summary>
  708. /// <param name="path">
  709. /// A <see cref="String"/> containing the path of the directory to search.
  710. /// </param>
  711. /// <param name="searchPattern">
  712. /// A <see cref="String"/> containing search pattern to match against the names of the
  713. /// files and directories in <paramref name="path"/>, otherwise, <see langword="null"/>
  714. /// or an empty string ("") to use the default search pattern, "*".
  715. /// </param>
  716. /// <param name="searchOption">
  717. /// One of the <see cref="SearchOption"/> values that specifies whether the search operation should
  718. /// include all subdirectories or only the current directory.
  719. /// </param>
  720. /// <returns>
  721. /// A <see cref="IEnumerable{T}"/> containing the file and directory names within
  722. /// <paramref name="path"/>that match <paramref name="searchPattern"/>.
  723. /// </returns>
  724. /// <exception cref="ArgumentNullException">
  725. /// <paramref name="path"/> is <see langword="null"/>.
  726. /// </exception>
  727. /// <exception cref="ArgumentException">
  728. /// <paramref name="path"/> is an empty string (""), contains only white
  729. /// space, or contains one or more invalid characters as defined in
  730. /// <see cref="Path.GetInvalidPathChars()"/>.
  731. /// <para>
  732. /// -or-
  733. /// </para>
  734. /// <paramref name="path"/> contains one or more components that exceed
  735. /// the drive-defined maximum length. For example, on Windows-based
  736. /// platforms, components must not exceed 255 characters.
  737. /// </exception>
  738. /// <exception cref="PathTooLongException">
  739. /// <paramref name="path"/> exceeds the system-defined maximum length.
  740. /// For example, on Windows-based platforms, paths must not exceed
  741. /// 32,000 characters.
  742. /// </exception>
  743. /// <exception cref="DirectoryNotFoundException">
  744. /// <paramref name="path"/> contains one or more directories that could not be
  745. /// found.
  746. /// </exception>
  747. /// <exception cref="UnauthorizedAccessException">
  748. /// The caller does not have the required access permissions.
  749. /// </exception>
  750. /// <exception cref="IOException">
  751. /// <paramref name="path"/> is a file.
  752. /// <para>
  753. /// -or-
  754. /// </para>
  755. /// <paramref name="path"/> specifies a device that is not ready.
  756. /// </exception>
  757. public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
  758. {
  759. return EnumerateFileSystemEntries(path, searchPattern, true, true, searchOption);
  760. }
  761. private static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, bool includeDirectories, bool includeFiles, SearchOption searchOption) {
  762. string normalizedSearchPattern = LongPathCommon.NormalizeSearchPattern(searchPattern);
  763. string normalizedPath = LongPathCommon.NormalizeLongPath(path);
  764. // First check whether the specified path refers to a directory and exists
  765. FileAttributes attributes;
  766. int errorCode = LongPathCommon.TryGetDirectoryAttributes(normalizedPath, out attributes);
  767. if (errorCode != 0) {
  768. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  769. }
  770. return EnumerateFileSystemIterator(normalizedPath, normalizedSearchPattern, includeDirectories, includeFiles, searchOption);
  771. }
  772. private static IEnumerable<string> EnumerateFileSystemIterator(string normalizedPath, string normalizedSearchPattern, bool includeDirectories, bool includeFiles, SearchOption searchOption) {
  773. // NOTE: Any exceptions thrown from this method are thrown on a call to IEnumerator<string>.MoveNext()
  774. Queue<string> directoryQueue = new Queue<string>();
  775. directoryQueue.Enqueue(normalizedPath);
  776. while(directoryQueue.Count > 0)
  777. {
  778. NativeMethods.WIN32_FIND_DATA findData;
  779. normalizedPath = directoryQueue.Dequeue();
  780. // Add subdirectories to search for files
  781. if (searchOption != SearchOption.TopDirectoryOnly)
  782. {
  783. using (SafeFindHandle handle = BeginFind(Path.Combine(normalizedPath, "*"), out findData))
  784. {
  785. int errorCode;
  786. if (handle == null)
  787. {
  788. errorCode = Marshal.GetLastWin32Error();
  789. if (errorCode != NativeMethods.ERROR_FILE_NOT_FOUND && errorCode != 0)
  790. {
  791. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  792. }
  793. }
  794. do
  795. {
  796. string currentFileName = findData.cFileName;
  797. if (IsDirectory(findData.dwFileAttributes))
  798. {
  799. if (!IsCurrentOrParentDirectory(currentFileName))
  800. {
  801. directoryQueue.Enqueue(Path.Combine(normalizedPath, currentFileName));
  802. }
  803. }
  804. }
  805. while (NativeMethods.FindNextFile(handle, out findData));
  806. errorCode = Marshal.GetLastWin32Error();
  807. if (errorCode != NativeMethods.ERROR_NO_MORE_FILES)
  808. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  809. }
  810. }
  811. string path = LongPathCommon.RemoveLongPathPrefix(normalizedPath);
  812. using (SafeFindHandle handle = BeginFind(Path.Combine(normalizedPath, normalizedSearchPattern), out findData))
  813. {
  814. int errorCode;
  815. if (handle == null)
  816. {
  817. errorCode = Marshal.GetLastWin32Error();
  818. if (errorCode != NativeMethods.ERROR_FILE_NOT_FOUND && errorCode != 0)
  819. {
  820. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  821. }
  822. continue;
  823. }
  824. do {
  825. string currentFileName = findData.cFileName;
  826. if (IsDirectory(findData.dwFileAttributes)) {
  827. if (!IsCurrentOrParentDirectory(currentFileName)) {
  828. if (includeDirectories) {
  829. yield return Path.Combine(path, currentFileName);
  830. }
  831. }
  832. }
  833. else {
  834. if (includeFiles) {
  835. yield return Path.Combine(path, currentFileName);
  836. }
  837. }
  838. } while (NativeMethods.FindNextFile(handle, out findData));
  839. errorCode = Marshal.GetLastWin32Error();
  840. if (errorCode != NativeMethods.ERROR_NO_MORE_FILES)
  841. {
  842. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  843. }
  844. }
  845. }
  846. }
  847. private static SafeFindHandle BeginFind(string normalizedPathWithSearchPattern, out NativeMethods.WIN32_FIND_DATA findData) {
  848. SafeFindHandle handle = NativeMethods.FindFirstFile(normalizedPathWithSearchPattern, out findData);
  849. if (handle.IsInvalid) {
  850. int errorCode = Marshal.GetLastWin32Error();
  851. if (errorCode != NativeMethods.ERROR_FILE_NOT_FOUND)
  852. throw LongPathCommon.GetExceptionFromWin32Error(errorCode);
  853. return null;
  854. }
  855. return handle;
  856. }
  857. internal static bool IsDirectory(FileAttributes attributes) {
  858. return (attributes & FileAttributes.Directory) == FileAttributes.Directory;
  859. }
  860. private static bool IsCurrentOrParentDirectory(string directoryName) {
  861. return directoryName.Equals(".", StringComparison.OrdinalIgnoreCase) || directoryName.Equals("..", StringComparison.OrdinalIgnoreCase);
  862. }
  863. }
  864. }