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);
};
}