PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Trunk-Updater/src/Updater/Utilities/FileUtility.cs

#
C# | 378 lines | 200 code | 30 blank | 148 comment | 22 complexity | 6702e96e998557b08a8b52626fea1c75 MD5 | raw file
Possible License(s): MIT
  1. //============================================================================================================
  2. // Microsoft Updater Application Block for .NET
  3. // http://msdn.microsoft.com/library/en-us/dnbda/html/updater.asp
  4. //
  5. // FileUtility.cs
  6. //
  7. // Contains the implementation of the FileUtility helper class.
  8. //
  9. // For more information see the Updater Application Block Implementation Overview.
  10. //
  11. //============================================================================================================
  12. // Copyright Š Microsoft Corporation. All rights reserved.
  13. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  14. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  15. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  16. // FITNESS FOR A PARTICULAR PURPOSE.
  17. //============================================================================================================
  18. using System;
  19. using System.IO;
  20. using System.Runtime.InteropServices;
  21. namespace Microsoft.ApplicationBlocks.Updater.Utilities
  22. {
  23. /// <summary>
  24. /// Indicates how to proceed with the move file operation.
  25. /// </summary>
  26. [Flags]
  27. public enum MoveFileFlag : int
  28. {
  29. /// <summary>
  30. /// Perform a default move funtion.
  31. /// </summary>
  32. None = 0x00000000,
  33. /// <summary>
  34. /// If the target file exists, the move function will replace it.
  35. /// </summary>
  36. ReplaceExisting = 0x00000001,
  37. /// <summary>
  38. /// If the file is to be moved to a different volume,
  39. /// the function simulates the move by using the CopyFile and DeleteFile functions.
  40. /// </summary>
  41. CopyAllowed = 0x00000002,
  42. /// <summary>
  43. /// The system does not move the file until the operating system is restarted.
  44. /// The system moves the file immediately after AUTOCHK is executed, but before
  45. /// creating any paging files. Consequently, this parameter enables the function
  46. /// to delete paging files from previous startups.
  47. /// </summary>
  48. DelayUntilReboot = 0x00000004,
  49. /// <summary>
  50. /// The function does not return until the file has actually been moved on the disk.
  51. /// </summary>
  52. WriteThrough = 0x00000008,
  53. /// <summary>
  54. /// Reserved for future use.
  55. /// </summary>
  56. CreateHardLink = 0x00000010,
  57. /// <summary>
  58. /// The function fails if the source file is a link source, but the file cannot be tracked after the move. This situation can occur if the destination is a volume formatted with the FAT file system.
  59. /// </summary>
  60. FailIfNotTrackable = 0x00000020,
  61. }
  62. /// <summary>
  63. /// Provides certain utilities used by configuration processors, such as correcting file paths.
  64. /// </summary>
  65. public sealed class FileUtility
  66. {
  67. #region Constructor
  68. /// <summary>
  69. /// Default constructor.
  70. /// </summary>
  71. private FileUtility()
  72. {
  73. }
  74. #endregion
  75. #region Public members
  76. /// <summary>
  77. /// Returns whether the path is a UNC path.
  78. /// </summary>
  79. /// <param name="path">The path string.</param>
  80. /// <returns><c>true</c> if the path is a UNC path.</returns>
  81. public static bool IsUncPath( string path )
  82. {
  83. // FIRST, check if this is a URL or a UNC path; do this by attempting to construct uri object from it
  84. Uri url = new Uri( path );
  85. if( url.IsUnc )
  86. {
  87. // it is a unc path, return true
  88. return true;
  89. }
  90. else
  91. {
  92. return false;
  93. }
  94. }
  95. /// <summary>
  96. /// Takes a UNC or URL path, determines which it is (NOT hardened against bad strings, assumes one or the other is present)
  97. /// and returns the path with correct trailing slash: backslash for UNC or
  98. /// slash mark for URL.
  99. /// </summary>
  100. /// <param name="path">The URL or UNC string.</param>
  101. /// <returns>Path with correct terminal slash.</returns>
  102. public static string AppendSlashUrlOrUnc( string path )
  103. {
  104. if( IsUncPath( path ) )
  105. {
  106. // it is a unc path, so decorate the end with a back-slash (to correct misconfigurations, defend against trivial errors)
  107. return AppendTerminalBackslash( path );
  108. }
  109. else
  110. {
  111. // assume URL here
  112. return AppendTerminalForwardSlash( path );
  113. }
  114. }
  115. /// <summary>
  116. /// If not present appends terminal backslash to paths.
  117. /// </summary>
  118. /// <param name="path">A path string; for example, "C:\AppUpdaterClient".</param>
  119. /// <returns>A path string with trailing backslash; for example, "C:\AppUpdaterClient\".</returns>
  120. public static string AppendTerminalBackslash( string path )
  121. {
  122. if( path.IndexOf( Path.DirectorySeparatorChar, path.Length - 1 ) == -1 )
  123. {
  124. return path + Path.DirectorySeparatorChar;
  125. }
  126. else
  127. {
  128. return path;
  129. }
  130. }
  131. /// <summary>
  132. /// Appends a terminal slash mark if there is not already one; returns corrected path.
  133. /// </summary>
  134. /// <param name="path">The path that may be missing a terminal slash mark.</param>
  135. /// <returns>The corrected path with terminal slash mark.</returns>
  136. public static string AppendTerminalForwardSlash( string path )
  137. {
  138. if( path.IndexOf( Path.AltDirectorySeparatorChar, path.Length - 1 ) == -1 )
  139. {
  140. return path + Path.AltDirectorySeparatorChar;
  141. }
  142. else
  143. {
  144. return path;
  145. }
  146. }
  147. /// <summary>
  148. /// Creates a new temporary folder under the system temp folder
  149. /// and returns its full pathname.
  150. /// </summary>
  151. /// <returns>The full temp path string.</returns>
  152. public static string CreateTemporaryFolder()
  153. {
  154. return Path.Combine( Path.GetTempPath(), Path.GetFileNameWithoutExtension( Path.GetTempFileName() ) );
  155. }
  156. /// <summary>
  157. /// Copies files from the source to destination directories. Directory.Move is not
  158. /// suitable here because the downloader may still have the temporary
  159. /// directory locked.
  160. /// </summary>
  161. /// <param name="sourcePath">The source path.</param>
  162. /// <param name="destinationPath">The destination path.</param>
  163. public static void CopyDirectory( string sourcePath, string destinationPath )
  164. {
  165. CopyDirectory( sourcePath, destinationPath, true );
  166. }
  167. /// <summary>
  168. /// Copies files from the source to destination directories. Directory.Move is not
  169. /// suitable here because the downloader may still have the temporary
  170. /// directory locked.
  171. /// </summary>
  172. /// <param name="sourcePath">The source path.</param>
  173. /// <param name="destinationPath">The destination path.</param>
  174. /// <param name="overwrite">Indicates whether the destination files should be overwritten.</param>
  175. public static void CopyDirectory( string sourcePath, string destinationPath, bool overwrite )
  176. {
  177. CopyDirRecurse( sourcePath, destinationPath, destinationPath, overwrite );
  178. }
  179. /// <summary>
  180. /// Move a file from a folder to a new one.
  181. /// </summary>
  182. /// <param name="existingFileName">The original file name.</param>
  183. /// <param name="newFileName">The new file name.</param>
  184. /// <param name="flags">Flags about how to move the files.</param>
  185. /// <returns>indicates whether the file was moved.</returns>
  186. public static bool MoveFile( string existingFileName, string newFileName, MoveFileFlag flags)
  187. {
  188. return MoveFileEx( existingFileName, newFileName, (int)flags );
  189. }
  190. /// <summary>
  191. /// Deletes a folder. If the folder cannot be deleted at the time this method is called,
  192. /// the deletion operation is delayed until the next system boot.
  193. /// </summary>
  194. /// <param name="folderPath">The directory to be removed</param>
  195. public static void DestroyFolder( string folderPath )
  196. {
  197. try
  198. {
  199. if ( Directory.Exists( folderPath) )
  200. {
  201. Directory.Delete( folderPath, true );
  202. }
  203. }
  204. catch( Exception )
  205. {
  206. // If we couldn't remove the files, postpone it to the next system reboot
  207. if ( Directory.Exists( folderPath) )
  208. {
  209. FileUtility.MoveFile(
  210. folderPath,
  211. null,
  212. MoveFileFlag.DelayUntilReboot );
  213. }
  214. }
  215. }
  216. /// <summary>
  217. /// Deletes a file. If the file cannot be deleted at the time this method is called,
  218. /// the deletion operation is delayed until the next system boot.
  219. /// </summary>
  220. /// <param name="filePath">The file to be removed</param>
  221. public static void DestroyFile( string filePath )
  222. {
  223. try
  224. {
  225. if ( File.Exists( filePath ) )
  226. {
  227. File.Delete( filePath );
  228. }
  229. }
  230. catch
  231. {
  232. if ( File.Exists( filePath ) )
  233. {
  234. FileUtility.MoveFile(
  235. filePath,
  236. null,
  237. MoveFileFlag.DelayUntilReboot );
  238. }
  239. }
  240. }
  241. /// <summary>
  242. /// Returns the path to the newer version of the .NET Framework installed on the system.
  243. /// </summary>
  244. /// <returns>A string containig the full path to the newer .Net Framework location</returns>
  245. public static string GetLatestDotNetFrameworkPath()
  246. {
  247. Version latestVersion = null;
  248. string fwkPath = Path.GetFullPath(Path.Combine(Environment.SystemDirectory, @"..\Microsoft.NET\Framework"));
  249. foreach (string path in Directory.GetDirectories(fwkPath, "v*"))
  250. {
  251. string candidateVersion = Path.GetFileName(path).TrimStart('v');
  252. try
  253. {
  254. Version curVersion = new Version(candidateVersion);
  255. if (latestVersion == null || (latestVersion != null && latestVersion < curVersion))
  256. {
  257. latestVersion = curVersion;
  258. }
  259. }
  260. catch { }
  261. }
  262. return Path.Combine(fwkPath, "v" + latestVersion.ToString());
  263. }
  264. /// <summary>
  265. /// Returns the path to a version of the .NET Framework installed on the system.
  266. /// </summary>
  267. /// <param name="version">The version to search for.</param>
  268. /// <returns>
  269. /// A string containig the full path to the specified .Net Framework location
  270. /// </returns>
  271. public static string GetDotNetFrameworkPath(Version version)
  272. {
  273. string fwkPath = Path.GetFullPath(Path.Combine(Environment.SystemDirectory, @"..\Microsoft.NET\Framework"));
  274. string versionPath = Path.Combine(fwkPath, "v" + version.ToString());
  275. if (Directory.Exists(versionPath))
  276. {
  277. return versionPath;
  278. }
  279. return string.Empty;
  280. }
  281. #endregion
  282. #region Private members
  283. /// <summary>
  284. /// API declaration of the Win32 function.
  285. /// </summary>
  286. /// <param name="lpExistingFileName">Existing file path.</param>
  287. /// <param name="lpNewFileName">The file path.</param>
  288. /// <param name="dwFlags">Move file flags.</param>
  289. /// <returns>Whether the file was moved or not.</returns>
  290. [DllImport("KERNEL32.DLL")]
  291. private static extern bool MoveFileEx(
  292. string lpExistingFileName,
  293. string lpNewFileName,
  294. long dwFlags );
  295. /// <summary>
  296. /// Utility function that recursively copies directories and files.
  297. /// Again, we could use Directory.Move but we need to preserve the original.
  298. /// </summary>
  299. /// <param name="sourcePath">The source path to copy.</param>
  300. /// <param name="destinationPath">The destination path to copy to.</param>
  301. /// <param name="originalDestination">The original dstination path.</param>
  302. /// <param name="overwrite">Whether the folders should be copied recursively.</param>
  303. private static void CopyDirRecurse( string sourcePath, string destinationPath, string originalDestination, bool overwrite )
  304. {
  305. // ensure terminal backslash
  306. sourcePath = FileUtility.AppendTerminalBackslash( sourcePath );
  307. destinationPath = FileUtility.AppendTerminalBackslash( destinationPath );
  308. if ( !Directory.Exists( destinationPath ) )
  309. {
  310. Directory.CreateDirectory( destinationPath );
  311. }
  312. // get dir info which may be file or dir info object
  313. DirectoryInfo dirInfo = new DirectoryInfo( sourcePath );
  314. string destFileName = null;
  315. foreach( FileSystemInfo fsi in dirInfo.GetFileSystemInfos() )
  316. {
  317. if ( fsi is FileInfo )
  318. {
  319. destFileName = Path.Combine( destinationPath, fsi.Name );
  320. // if file object just copy when overwrite is allowed
  321. if ( File.Exists( destFileName ) )
  322. {
  323. if ( overwrite )
  324. {
  325. File.Copy( fsi.FullName, destFileName, true );
  326. }
  327. }
  328. else
  329. {
  330. File.Copy( fsi.FullName, destFileName );
  331. }
  332. }
  333. else
  334. {
  335. // avoid this recursion path, otherwise copying directories as child directories
  336. // would be an endless recursion (up to an stack-overflow exception).
  337. if ( fsi.FullName != originalDestination )
  338. {
  339. // must be a directory, create destination sub-folder and recurse to copy files
  340. //Directory.CreateDirectory( destinationPath + fsi.Name );
  341. CopyDirRecurse( fsi.FullName, destinationPath + fsi.Name, originalDestination, overwrite );
  342. }
  343. }
  344. }
  345. }
  346. #endregion
  347. }
  348. }