PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/System.IO.FileSystem/src/System/IO/WinRTFileSystem.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 678 lines | 441 code | 120 blank | 117 comment | 58 complexity | 5a265d08961a2e227ee105337ca30b60 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.ExceptionServices;
  9. using System.Threading.Tasks;
  10. using Windows.Foundation;
  11. using Windows.Storage;
  12. using Windows.Storage.FileProperties;
  13. using Windows.UI.Core;
  14. using WinRTFileAttributes = Windows.Storage.FileAttributes;
  15. namespace System.IO
  16. {
  17. internal sealed partial class WinRTFileSystem : FileSystem
  18. {
  19. public override int MaxPath { get { return Interop.mincore.MAX_PATH; } }
  20. public override int MaxDirectoryPath { get { return Interop.mincore.MAX_DIRECTORY_PATH; } }
  21. private static System.IO.FileAttributes ConvertFileAttributes(WinRTFileAttributes fileAttributes)
  22. {
  23. //namespace Windows.Storage
  24. //{
  25. // [Flags]
  26. // public enum FileAttributes
  27. // {
  28. // Normal = 0,
  29. // ReadOnly = 1,
  30. // Directory = 16,
  31. // Archive = 32,
  32. // Temporary = 256,
  33. // LocallyIncomplete = 512,
  34. // }
  35. //}
  36. //namespace System.IO
  37. //{
  38. // [Flags]
  39. // public enum FileAttributes
  40. // {
  41. // ReadOnly = 1,
  42. // Hidden = 2,
  43. // System = 4,
  44. // Directory = 16,
  45. // Archive = 32,
  46. // Device = 64,
  47. // Normal = 128,
  48. // Temporary = 256,
  49. // SparseFile = 512,
  50. // ReparsePoint = 1024,
  51. // Compressed = 2048,
  52. // Offline = 4096,
  53. // NotContentIndexed = 8192,
  54. // Encrypted = 16384,
  55. // }
  56. //}
  57. // Normal is a special case and happens to have different values in WinRT and Win32.
  58. // It's meant to indicate the absence of other flags. On WinRT this logically is 0,
  59. // however on Win32 it is represented with a discrete value of 128.
  60. return (fileAttributes == WinRTFileAttributes.Normal) ?
  61. FileAttributes.Normal :
  62. (FileAttributes)fileAttributes;
  63. }
  64. private static WinRTFileAttributes ConvertFileAttributes(FileAttributes fileAttributes)
  65. {
  66. // see comment above
  67. // Here we make sure to remove the "normal" value since it is redundant
  68. // We do not mask unsupported values
  69. return (fileAttributes == FileAttributes.Normal) ?
  70. WinRTFileAttributes.Normal :
  71. (WinRTFileAttributes)(fileAttributes & ~FileAttributes.Normal);
  72. }
  73. public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite)
  74. {
  75. EnsureBackgroundThread();
  76. SynchronousResultOf(CopyFileAsync(sourceFullPath, destFullPath, overwrite));
  77. }
  78. private async Task CopyFileAsync(string sourceFullPath, string destFullPath, bool overwrite)
  79. {
  80. StorageFile file = await StorageFile.GetFileFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath);
  81. string destDirectory, destFileName;
  82. PathHelpers.SplitDirectoryFile(destFullPath, out destDirectory, out destFileName);
  83. StorageFolder destFolder = await StorageFolder.GetFolderFromPathAsync(destDirectory).TranslateWinRTTask(destDirectory, isDirectory: true);
  84. await file.CopyAsync(destFolder, destFileName, overwrite ? NameCollisionOption.ReplaceExisting : NameCollisionOption.FailIfExists).TranslateWinRTTask(sourceFullPath);
  85. }
  86. public override void CreateDirectory(string fullPath)
  87. {
  88. EnsureBackgroundThread();
  89. SynchronousResultOf(CreateDirectoryAsync(fullPath, failIfExists: false));
  90. }
  91. private async Task<StorageFolder> CreateDirectoryAsync(string fullPath, bool failIfExists)
  92. {
  93. if (fullPath.Length >= Interop.mincore.MAX_DIRECTORY_PATH)
  94. throw new PathTooLongException(SR.IO_PathTooLong);
  95. Stack<string> stackDir = new Stack<string>();
  96. StorageFolder workingFolder = null;
  97. string workingPath = fullPath;
  98. // walk up the path until we can get a directory
  99. while (workingFolder == null)
  100. {
  101. try
  102. {
  103. workingFolder = await StorageFolder.GetFolderFromPathAsync(workingPath).TranslateWinRTTask(workingPath, isDirectory: true);
  104. }
  105. catch (IOException) { }
  106. catch (UnauthorizedAccessException) { }
  107. if (workingFolder == null)
  108. {
  109. // we couldn't get the directory, we'll need to create it
  110. string folderName = null;
  111. PathHelpers.SplitDirectoryFile(workingPath, out workingPath, out folderName);
  112. if (String.IsNullOrEmpty(folderName))
  113. {
  114. // we reached the root and it did not exist. we can't create roots.
  115. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_PATH_NOT_FOUND, workingPath);
  116. }
  117. stackDir.Push(folderName);
  118. Debug.Assert(!String.IsNullOrEmpty(workingPath));
  119. }
  120. }
  121. Debug.Assert(workingFolder != null);
  122. if (failIfExists && (stackDir.Count == 0))
  123. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_ALREADY_EXISTS, fullPath);
  124. // we have work to do. if stackDir is empty it means we were passed a path to an existing directory.
  125. while (stackDir.Count > 0)
  126. {
  127. // use CreationCollisionOption.OpenIfExists to address a race conditions when creating directories
  128. workingFolder = await workingFolder.CreateFolderAsync(stackDir.Pop(), CreationCollisionOption.OpenIfExists).TranslateWinRTTask(fullPath, isDirectory: true);
  129. }
  130. return workingFolder;
  131. }
  132. public override void DeleteFile(string fullPath)
  133. {
  134. EnsureBackgroundThread();
  135. SynchronousResultOf(DeleteFileAsync(fullPath));
  136. }
  137. private async Task DeleteFileAsync(string fullPath)
  138. {
  139. try
  140. {
  141. // Note the absence of TranslateWinRTTask, we translate below in the catch block.
  142. StorageFile file = await StorageFile.GetFileFromPathAsync(fullPath).AsTask().ConfigureAwait(false);
  143. await file.DeleteAsync(StorageDeleteOption.PermanentDelete).AsTask().ConfigureAwait(false);
  144. }
  145. catch (Exception exception)
  146. {
  147. // For consistency with Win32 we ignore missing files
  148. if (exception.HResult != HResults.ERROR_FILE_NOT_FOUND)
  149. throw exception.TranslateWinRTException(fullPath);
  150. }
  151. }
  152. public override bool DirectoryExists(string fullPath)
  153. {
  154. EnsureBackgroundThread();
  155. return SynchronousResultOf(DirectoryExistsAsync(fullPath));
  156. }
  157. private async Task<bool> DirectoryExistsAsync(string fullPath)
  158. {
  159. string directoryPath = null, fileName = null;
  160. PathHelpers.SplitDirectoryFile(fullPath, out directoryPath, out fileName);
  161. // Rather than call await StorageFolder.GetFolderFromPathAsync(fullPath); and catch FileNotFoundException
  162. // we try to avoid the exception by calling TryGetItemAsync.
  163. // We can still hit an exception if the parent directory doesn't exist but it does provide better performance
  164. // for the existing parent/non-existing directory case and avoids a first chance exception which is a developer
  165. // pain point.
  166. StorageFolder parent = null;
  167. try
  168. {
  169. parent = await StorageFolder.GetFolderFromPathAsync(directoryPath).TranslateWinRTTask(directoryPath, isDirectory: true);
  170. }
  171. catch (IOException) { }
  172. catch (UnauthorizedAccessException) { }
  173. if (String.IsNullOrEmpty(fileName))
  174. {
  175. // we were given a root
  176. return parent != null;
  177. }
  178. if (parent != null)
  179. {
  180. StorageFolder folder = await parent.TryGetItemAsync(fileName).TranslateWinRTTask(fullPath, isDirectory: true) as StorageFolder;
  181. return folder != null;
  182. }
  183. else
  184. {
  185. // it's possible we don't have access to the parent but do have access to this folder
  186. try
  187. {
  188. StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(fullPath).TranslateWinRTTask(fullPath, isDirectory: true);
  189. return folder != null;
  190. }
  191. catch (IOException) { }
  192. catch (UnauthorizedAccessException) { }
  193. }
  194. return false;
  195. }
  196. public override IEnumerable<string> EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget)
  197. {
  198. // Fill in implementation
  199. return new string[0];
  200. }
  201. public override IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget)
  202. {
  203. // Fill in implementation
  204. return new FileSystemInfo[0];
  205. }
  206. public override bool FileExists(string fullPath)
  207. {
  208. EnsureBackgroundThread();
  209. return SynchronousResultOf(FileExistsAsync(fullPath));
  210. }
  211. private async Task<bool> FileExistsAsync(string fullPath)
  212. {
  213. string directoryPath = null, fileName = null;
  214. PathHelpers.SplitDirectoryFile(fullPath, out directoryPath, out fileName);
  215. if (String.IsNullOrEmpty(fileName))
  216. {
  217. // No filename was provided
  218. return false;
  219. }
  220. // Rather than call await StorageFile.GetFileFromPathAsync(fullPath); and catch FileNotFoundException
  221. // we try to avoid the exception by calling TryGetItemAsync.
  222. // We can still hit an exception if the directory doesn't exist but it does provide better performance
  223. // for the existing folder/non-existing file case and avoids a first chance exception which is a developer
  224. // pain point.
  225. StorageFolder parent = null;
  226. try
  227. {
  228. parent = await StorageFolder.GetFolderFromPathAsync(directoryPath).TranslateWinRTTask(directoryPath);
  229. }
  230. catch (IOException) { }
  231. catch (UnauthorizedAccessException) { }
  232. StorageFile file = null;
  233. if (parent != null)
  234. {
  235. // The expectation is that this API will never throw, thus it is missing TranslateWinRTTask
  236. file = await parent.TryGetItemAsync(fileName).TranslateWinRTTask(fullPath) as StorageFile;
  237. }
  238. else
  239. {
  240. // it's possible we don't have access to the parent but do have access to this file
  241. try
  242. {
  243. file = await StorageFile.GetFileFromPathAsync(fullPath).TranslateWinRTTask(fullPath);
  244. }
  245. catch (IOException) { }
  246. catch (UnauthorizedAccessException) { }
  247. }
  248. return (file != null) ? file.IsAvailable : false;
  249. }
  250. public override FileAttributes GetAttributes(string fullPath)
  251. {
  252. EnsureBackgroundThread();
  253. return SynchronousResultOf(GetAttributesAsync(fullPath));
  254. }
  255. private async Task<FileAttributes> GetAttributesAsync(string fullPath)
  256. {
  257. IStorageItem item = await GetStorageItemAsync(fullPath).ConfigureAwait(false);
  258. return ConvertFileAttributes(item.Attributes);
  259. }
  260. public override DateTimeOffset GetCreationTime(string fullPath)
  261. {
  262. EnsureBackgroundThread();
  263. return SynchronousResultOf(GetCreationTimeAsync(fullPath));
  264. }
  265. private async Task<DateTimeOffset> GetCreationTimeAsync(string fullPath)
  266. {
  267. IStorageItem item = await GetStorageItemAsync(fullPath).ConfigureAwait(false);
  268. return item.DateCreated;
  269. }
  270. public override string GetCurrentDirectory()
  271. {
  272. throw new PlatformNotSupportedException();
  273. }
  274. public override IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory)
  275. {
  276. return new WinRTFileSystemObject(fullPath, asDirectory);
  277. }
  278. public override DateTimeOffset GetLastAccessTime(string fullPath)
  279. {
  280. EnsureBackgroundThread();
  281. return SynchronousResultOf(GetLastAccessTimeAsync(fullPath));
  282. }
  283. private async Task<DateTimeOffset> GetLastAccessTimeAsync(string fullPath)
  284. {
  285. IStorageItem item = await GetStorageItemAsync(fullPath).ConfigureAwait(false);
  286. return await GetLastAccessTimeAsync(item).ConfigureAwait(false);
  287. }
  288. // declare a static to avoid unnecessary heap allocations
  289. private static readonly string[] s_dateAccessedKey = { "System.DateAccessed" };
  290. private static async Task<DateTimeOffset> GetLastAccessTimeAsync(IStorageItem item)
  291. {
  292. BasicProperties properties = await item.GetBasicPropertiesAsync().TranslateWinRTTask(item.Path);
  293. var propertyMap = await properties.RetrievePropertiesAsync(s_dateAccessedKey).TranslateWinRTTask(item.Path);
  294. // shell doesn't expose this metadata on all item types
  295. if (propertyMap.ContainsKey(s_dateAccessedKey[0]))
  296. {
  297. return (DateTimeOffset)propertyMap[s_dateAccessedKey[0]];
  298. }
  299. // fallback to modified date
  300. return properties.DateModified;
  301. }
  302. public override DateTimeOffset GetLastWriteTime(string fullPath)
  303. {
  304. EnsureBackgroundThread();
  305. return SynchronousResultOf(GetLastWriteTimeAsync(fullPath));
  306. }
  307. private async Task<DateTimeOffset> GetLastWriteTimeAsync(string fullPath)
  308. {
  309. IStorageItem item = await GetStorageItemAsync(fullPath).ConfigureAwait(false);
  310. return await GetLastWriteTimeAsync(item).ConfigureAwait(false);
  311. }
  312. private static async Task<DateTimeOffset> GetLastWriteTimeAsync(IStorageItem item)
  313. {
  314. BasicProperties properties = await item.GetBasicPropertiesAsync().TranslateWinRTTask(item.Path);
  315. return properties.DateModified;
  316. }
  317. private static async Task<IStorageItem> GetStorageItemAsync(string fullPath)
  318. {
  319. string directoryPath, itemName;
  320. PathHelpers.SplitDirectoryFile(fullPath, out directoryPath, out itemName);
  321. StorageFolder parent = await StorageFolder.GetFolderFromPathAsync(directoryPath).TranslateWinRTTask(directoryPath, isDirectory: true);
  322. if (String.IsNullOrEmpty(itemName))
  323. return parent;
  324. return await parent.GetItemAsync(itemName).TranslateWinRTTask(fullPath);
  325. }
  326. public override void MoveDirectory(string sourceFullPath, string destFullPath)
  327. {
  328. EnsureBackgroundThread();
  329. SynchronousResultOf(MoveDirectoryAsync(sourceFullPath, destFullPath));
  330. }
  331. private async Task MoveDirectoryAsync(string sourceFullPath, string destFullPath)
  332. {
  333. StorageFolder sourceFolder = await StorageFolder.GetFolderFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath, isDirectory: true);
  334. // WinRT doesn't support move, only rename
  335. // If parents are the same, just rename.
  336. string sourceParent, sourceFolderName, destParent, destFolderName;
  337. PathHelpers.SplitDirectoryFile(sourceFullPath, out sourceParent, out sourceFolderName);
  338. PathHelpers.SplitDirectoryFile(destFullPath, out destParent, out destFolderName);
  339. // same parent folder
  340. if (String.Equals(sourceParent, destParent, StringComparison.OrdinalIgnoreCase))
  341. {
  342. // not the same subfolder
  343. if (!String.Equals(sourceFolderName, destFolderName, StringComparison.OrdinalIgnoreCase))
  344. {
  345. await sourceFolder.RenameAsync(destFolderName).TranslateWinRTTask(destFullPath, isDirectory: true);
  346. }
  347. // else : nothing to do
  348. }
  349. else
  350. {
  351. // Otherwise, create the destination and move each item recursively.
  352. // We could do a copy, which would be safer in case of a failure
  353. // We do a move because it has the perf characteristics that match the win32 move
  354. StorageFolder destFolder = await CreateDirectoryAsync(destFullPath, failIfExists: true).ConfigureAwait(false);
  355. await MoveDirectoryAsync(sourceFolder, destFolder).ConfigureAwait(false);
  356. }
  357. }
  358. private async Task MoveDirectoryAsync(StorageFolder sourceFolder, StorageFolder destFolder)
  359. {
  360. foreach (var sourceFile in await sourceFolder.GetFilesAsync().TranslateWinRTTask(sourceFolder.Path, isDirectory: true))
  361. {
  362. await sourceFile.MoveAsync(destFolder).TranslateWinRTTask(sourceFile.Path);
  363. }
  364. foreach (var sourceSubFolder in await sourceFolder.GetFoldersAsync().TranslateWinRTTask(sourceFolder.Path, isDirectory: true))
  365. {
  366. StorageFolder destSubFolder = await destFolder.CreateFolderAsync(sourceSubFolder.Name).TranslateWinRTTask(destFolder.Path, isDirectory: true);
  367. // Recursively move sub-directory
  368. await MoveDirectoryAsync(sourceSubFolder, destSubFolder).ConfigureAwait(false);
  369. }
  370. // sourceFolder should now be empty
  371. await sourceFolder.DeleteAsync(StorageDeleteOption.PermanentDelete).TranslateWinRTTask(sourceFolder.Path, isDirectory: true);
  372. }
  373. public override void MoveFile(string sourceFullPath, string destFullPath)
  374. {
  375. EnsureBackgroundThread();
  376. SynchronousResultOf(MoveFileAsync(sourceFullPath, destFullPath));
  377. }
  378. private async Task MoveFileAsync(string sourceFullPath, string destFullPath)
  379. {
  380. StorageFile file = await StorageFile.GetFileFromPathAsync(sourceFullPath).TranslateWinRTTask(sourceFullPath);
  381. string destDirectory, destFileName;
  382. PathHelpers.SplitDirectoryFile(destFullPath, out destDirectory, out destFileName);
  383. // Win32 MoveFileEx will return success if source and destination are the same.
  384. // Comparison is safe here as the caller has normalized both paths.
  385. if (!sourceFullPath.Equals(destFullPath, StringComparison.OrdinalIgnoreCase))
  386. {
  387. StorageFolder destFolder = await StorageFolder.GetFolderFromPathAsync(destDirectory).TranslateWinRTTask(destDirectory, isDirectory: true);
  388. await file.MoveAsync(destFolder, destFileName, NameCollisionOption.FailIfExists).TranslateWinRTTask(sourceFullPath);
  389. }
  390. }
  391. public override FileStreamBase Open(string fullPath, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent)
  392. {
  393. EnsureBackgroundThread();
  394. return SynchronousResultOf(OpenAsync(fullPath, mode, access, share, bufferSize, options, parent));
  395. }
  396. private async Task<FileStreamBase> OpenAsync(string fullPath, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent)
  397. {
  398. // When trying to open the root directory, we need to throw an Access Denied
  399. if (PathInternal.GetRootLength(fullPath) == fullPath.Length)
  400. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_ACCESS_DENIED, fullPath);
  401. // Win32 CreateFile returns ERROR_PATH_NOT_FOUND when given a path that ends with '\'
  402. if (PathHelpers.EndsInDirectorySeparator(fullPath))
  403. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_PATH_NOT_FOUND, fullPath);
  404. StorageFile file = null;
  405. // FileMode
  406. if (mode == FileMode.Open || mode == FileMode.Truncate)
  407. {
  408. file = await StorageFile.GetFileFromPathAsync(fullPath).TranslateWinRTTask(fullPath);
  409. }
  410. else
  411. {
  412. CreationCollisionOption collisionOptions;
  413. switch (mode)
  414. {
  415. case FileMode.Create:
  416. collisionOptions = CreationCollisionOption.ReplaceExisting;
  417. break;
  418. case FileMode.CreateNew:
  419. collisionOptions = CreationCollisionOption.FailIfExists;
  420. break;
  421. case FileMode.Append:
  422. case FileMode.OpenOrCreate:
  423. default:
  424. collisionOptions = CreationCollisionOption.OpenIfExists;
  425. break;
  426. }
  427. string directoryPath, fileName;
  428. PathHelpers.SplitDirectoryFile(fullPath, out directoryPath, out fileName);
  429. StorageFolder directory = await StorageFolder.GetFolderFromPathAsync(directoryPath).TranslateWinRTTask(directoryPath, isDirectory: true);
  430. file = await directory.CreateFileAsync(fileName, collisionOptions).TranslateWinRTTask(fullPath);
  431. }
  432. // FileAccess: WinRT doesn't support FileAccessMode.Write so we upgrade to ReadWrite
  433. FileAccessMode accessMode = ((access & FileAccess.Write) != 0) ? FileAccessMode.ReadWrite : FileAccessMode.Read;
  434. // FileShare: cannot translate StorageFile uses a different sharing model (oplocks) that is controlled via FileAccessMode
  435. // FileOptions: ignore most values of FileOptions as they are hints and are not supported by WinRT.
  436. // FileOptions.Encrypted is not a hint, and not supported by WinRT, but there is precedent for ignoring this (FAT).
  437. // FileOptions.DeleteOnClose should result in an UnauthorizedAccessException when
  438. // opening a file that can only be read, but we cannot safely reproduce that behavior
  439. // in WinRT without actually deleting the file.
  440. // Instead the failure will occur in the finalizer for WinRTFileStream and be ignored.
  441. // open our stream
  442. Stream stream = (await file.OpenAsync(accessMode).TranslateWinRTTask(fullPath)).AsStream(bufferSize);
  443. if (mode == FileMode.Append)
  444. {
  445. // seek to end.
  446. stream.Seek(0, SeekOrigin.End);
  447. }
  448. else if (mode == FileMode.Truncate)
  449. {
  450. // truncate stream to 0
  451. stream.SetLength(0);
  452. }
  453. return new WinRTFileStream(stream, file, access, options, parent);
  454. }
  455. public override void RemoveDirectory(string fullPath, bool recursive)
  456. {
  457. EnsureBackgroundThread();
  458. SynchronousResultOf(RemoveDirectoryAsync(fullPath, recursive));
  459. }
  460. private async Task RemoveDirectoryAsync(string fullPath, bool recursive)
  461. {
  462. StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(fullPath).TranslateWinRTTask(fullPath, isDirectory: true);
  463. // StorageFolder.DeleteAsync will always be recursive. Detect a non-empty folder up front and throw.
  464. if (!recursive && (await folder.GetItemsAsync()).Count != 0)
  465. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_DIR_NOT_EMPTY, fullPath);
  466. // StorageFolder.Delete ignores readonly attribute. Detect and throw.
  467. if ((folder.Attributes & WinRTFileAttributes.ReadOnly) == WinRTFileAttributes.ReadOnly)
  468. throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath));
  469. StorageFolder parentFolder = await folder.GetParentAsync().TranslateWinRTTask(fullPath, isDirectory: true);
  470. // StorageFolder.Delete throws on hidden directories but we cannot prevent it.
  471. await folder.DeleteAsync(StorageDeleteOption.PermanentDelete).TranslateWinRTTask(fullPath, isDirectory: true);
  472. // WinRT will ignore failures to delete in cases where files are in use.
  473. // Throw if the folder still remains after successful DeleteAsync
  474. if (null != await (parentFolder.TryGetItemAsync(folder.Name)))
  475. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_DIR_NOT_EMPTY, fullPath);
  476. }
  477. public override void SetAttributes(string fullPath, FileAttributes attributes)
  478. {
  479. EnsureBackgroundThread();
  480. SynchronousResultOf(SetAttributesAsync(fullPath, attributes));
  481. }
  482. private async Task SetAttributesAsync(string fullPath, FileAttributes attributes)
  483. {
  484. IStorageItem item = await GetStorageItemAsync(fullPath).ConfigureAwait(false);
  485. await SetAttributesAsync(item, attributes).ConfigureAwait(false);
  486. }
  487. private static async Task SetAttributesAsync(IStorageItem item, FileAttributes attributes)
  488. {
  489. BasicProperties basicProperties = await item.GetBasicPropertiesAsync().TranslateWinRTTask(item.Path);
  490. // This works for only a subset of attributes, unsupported attributes are ignored.
  491. // We don't mask the attributes since WinRT just ignores the unsupported ones and flowing
  492. // them enables possible lightup in the future.
  493. var property = new KeyValuePair<string, object>("System.FileAttributes", (UInt32)ConvertFileAttributes(attributes));
  494. try
  495. {
  496. await basicProperties.SavePropertiesAsync(new[] { property }).AsTask().ConfigureAwait(false);
  497. }
  498. catch (Exception exception)
  499. {
  500. if (exception.HResult != HResults.ERROR_INVALID_PARAMETER)
  501. throw new ArgumentException(SR.Arg_InvalidFileAttrs);
  502. throw exception.TranslateWinRTException(item.Path);
  503. }
  504. }
  505. public override void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory)
  506. {
  507. // intentionally noop : not supported
  508. // "System.DateCreated" property is readonly
  509. }
  510. public override void SetCurrentDirectory(string fullPath)
  511. {
  512. throw new PlatformNotSupportedException();
  513. }
  514. public override void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory)
  515. {
  516. // intentionally noop
  517. // "System.DateAccessed" property is readonly
  518. }
  519. public override void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory)
  520. {
  521. // intentionally noop : not supported
  522. // "System.DateModified" property is readonly
  523. }
  524. #region Task Utility
  525. private static void EnsureBackgroundThread()
  526. {
  527. // WinRT async operations on brokered files require posting a message back to the UI thread.
  528. // If we were to run a sync method on the UI thread we'd deadlock. Throw instead.
  529. if (IsUIThread())
  530. throw new InvalidOperationException(SR.IO_SyncOpOnUIThread);
  531. }
  532. private static bool IsUIThread()
  533. {
  534. CoreWindow window = CoreWindow.GetForCurrentThread();
  535. return window != null && window.Dispatcher != null && window.Dispatcher.HasThreadAccess;
  536. }
  537. private static void SynchronousResultOf(Task task)
  538. {
  539. WaitForTask(task);
  540. }
  541. private static TResult SynchronousResultOf<TResult>(Task<TResult> task)
  542. {
  543. WaitForTask(task);
  544. return task.Result;
  545. }
  546. // this needs to be separate from SynchronousResultOf so that SynchronousResultOf<T> can call it.
  547. private static void WaitForTask(Task task)
  548. {
  549. // This should never be called from the UI thread since it can deadlock
  550. // Throwing here, however, is too late since work has already been started
  551. // Instead we assert here so that our tests can catch cases where we forgot to
  552. // call EnsureBackgroundThread before starting the task.
  553. Debug.Assert(!IsUIThread());
  554. task.GetAwaiter().GetResult();
  555. }
  556. #endregion Task Utility
  557. }
  558. }