using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Runtime.InteropServices; using System.IO; using System.Globalization; using System.Text; namespace FoxTricks { /// /// The ExtendedProcedures partial class wraps all the extended stored procedures from SQL 2000 we are implementing in SQL 2005. /// We suppress the "Microsoft.Design : Remove the public constructors from 'ExtendedProcedures'." Code analysis warning because /// the public constructor sits in the other part of the partial class.... /// public partial class ExtendedProcedures { /// /// Class containing all constants in use. /// private struct Constant { internal const string DATEFORMAT = "yyyyMMdd"; internal const string TIMEFORMAT = "Hmmss"; internal const string VERSION_TOKEN = "version"; } /// /// Return a resultset containing the properties of the file passed as parameter /// /// When the filePath parameter has the value 'version' we will display the build number of this component. /// Instead of path information. This option will greatly improve deployment problems and the chance that /// somebody enters that value for a path name is marginal. /// /// The path to the file we want to analyze [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes"), Microsoft.SqlServer.Server.SqlProcedure(Name="xp_getfiledetails")] public static void GetFileDetails(string filePath) { // Validate the parameter. if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException("filePath"); } #region "List of columns in the recordset." SqlMetaData colAlternateName = new SqlMetaData("Alternate Name", SqlDbType.NVarChar, 4000); SqlMetaData colSize = new SqlMetaData("Size", SqlDbType.BigInt); SqlMetaData colCreationDate = new SqlMetaData("Creation Date", SqlDbType.NChar, 8); SqlMetaData colCreationTime = new SqlMetaData("Creation Time", SqlDbType.NChar, 6); SqlMetaData colLastWrittenDate = new SqlMetaData("Last Written Date", SqlDbType.NChar, 8); SqlMetaData colLastWrittenTime = new SqlMetaData("Last Written Time", SqlDbType.NChar, 6); SqlMetaData colLastAccessedDate = new SqlMetaData("Last Accessed Date", SqlDbType.NChar, 8); SqlMetaData colLastAccessedTime = new SqlMetaData("Last Accessed Time", SqlDbType.NChar, 6); SqlMetaData colAttributes = new SqlMetaData("Attributes", SqlDbType.Int); //Define a recordset based on the column metadata SqlDataRecord record = new SqlDataRecord(new SqlMetaData[] { colAlternateName, colSize, colCreationDate, colCreationTime, colLastWrittenDate, colLastWrittenTime, colLastAccessedDate, colLastAccessedTime, colAttributes}); #endregion //Check if the filePath is pointing to a file or a folder. if (Directory.Exists(filePath)) { #region "Directory treatment" // Get a FileInfo object which will tell us everything we want to know DirectoryInfo myDirectory = new DirectoryInfo(filePath); record.SetInt64(1, 0); record.SetString(2, myDirectory.CreationTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(3, (myDirectory.CreationTime.Add(ExplorerBias(myDirectory.CreationTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetString(4, myDirectory.LastWriteTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(5, (myDirectory.LastWriteTime.Add(ExplorerBias(myDirectory.LastWriteTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetString(6, myDirectory.LastAccessTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(7, (myDirectory.LastAccessTime.Add(ExplorerBias(myDirectory.LastAccessTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetInt32(8, (int)myDirectory.Attributes); #endregion } else { #region "File treatment" if (File.Exists(filePath)) { // Get a FileInfo object which will tell us everything we want to know about the file FileInfo myFile = new FileInfo(filePath); //Only return a value for "Alternate Name" when the filename is longer then 8 if ((myFile.Name.Length - myFile.Extension.Length) > 8 || myFile.Extension.Length > 4) { StringBuilder shortPath = new StringBuilder(255); NativeMethods.GetShortPathName(filePath, shortPath, shortPath.Capacity); record.SetString(0, Path.GetFileName(shortPath.ToString())); } record.SetInt64(1, myFile.Length); record.SetString(2, myFile.CreationTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(3, (myFile.CreationTime.Add(ExplorerBias(myFile.CreationTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetString(4, myFile.LastWriteTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(5, (myFile.LastWriteTime.Add(ExplorerBias(myFile.LastWriteTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetString(6, myFile.LastAccessTime.ToString(Constant.DATEFORMAT, CultureInfo.InvariantCulture)); record.SetString(7, (myFile.LastAccessTime.Add(ExplorerBias(myFile.LastAccessTime))).ToString(Constant.TIMEFORMAT, CultureInfo.InvariantCulture)); record.SetInt32(8, (int)myFile.Attributes); } #endregion } //Send the resulting record to SQL Server SqlContext.Pipe.SendResultsStart(record); SqlContext.Pipe.SendResultsRow(record); SqlContext.Pipe.SendResultsEnd(); } #region "Helper Functions" /// /// This function gives the offset caused by daylight savings time. Apparently Windows explorer and the original xp_getfiledetails /// are smart enough to account for daylight saving. But .Net just adjusts UTC for this based on the current daylight saving situation. /// Of course for file properties this causes all kinds of funky offsets. /// When you call this function it will return the Daylight savings offset to be added to the timestamp to reach the original timestamp of the file. /// /// The timestamp to calculate the offset for. /// private static TimeSpan ExplorerBias(DateTime timeStamp) { TimeSpan bias = new TimeSpan(0); if (TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now) != TimeZone.CurrentTimeZone.IsDaylightSavingTime(timeStamp)) { bias = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now) - TimeZone.CurrentTimeZone.GetUtcOffset(timeStamp); } return bias; } #endregion }; /// /// internal class containing all Marshalled Native methods. /// internal static class NativeMethods { /// /// Import the GetShortPathName API through interop. This api is used to determine the 8.3 filename of a file /// that has a funky name. /// /// the full path with the long filename /// the full path including the short filename /// the length of the returned short path. /// status code if the call was successfull. [DllImport("kernel32.dll", CharSet = CharSet.Auto)] internal static extern int GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string path, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath, int shortPathLength); }; }