The System.IO.FileAttributes class gives us access to file/directory attributes. In this article, we’ll see how to use this class to first read the current attributes and then change them.
The System.IO.FileAttributes class
The class has numerous members that can be accessed. These members, which are really file or directory properties, fall under the Mscorlib Assembly (in Mscorlib.dll), and should be the same for Windows 98, Windows NT 4.0, Windows Millennium Edition, Windows 2000, Windows XP Home Edition, Windows XP Professional, and the Windows .NET Server family.
Member Name | Description |
Archive | The file’s archive status. Applications use this attribute to mark files for backup or removal. |
Compressed | The file is compressed. |
Device | Reserved for future use. |
Directory | The file is a directory. |
Encrypted | The file or directory is encrypted. For a file, this means that all data in the file is encrypted. For a directory, this means that encryption is the default for newly created files and directories. |
Hidden | The file is hidden, and thus is not included in an ordinary directory listing. |
Normal | The file is normal and has no other attributes set. This attribute is valid only if used alone. |
NotContentIndexed | The file will not be indexed by the operating system’s content indexing service. |
Offline | The file is offline. The data of the file is not immediately available. |
ReadOnly | The file is read-only. |
ReparsePoint | The file contains a reparse point, which is a block of user-defined data associated with a file or a directory. |
SparseFile | The file is a sparse file. Sparse files are typically large files whose data are mostly zeros. |
System | The file is a system file. The file is part of the operating system or is used exclusively by the operating system. |
Temporary | The file is temporary. File systems attempt to keep all of the data in memory for quicker access rather than flushing the data back to mass storage. A temporary file should be deleted by the application as soon as it is no longer needed. |
This enumeration allows a bitwise combination of its member values. Bit fields are generally used for lists of elements that might occur in combination. Therefore, bit fields are designed to be combined with a bitwise OR operation to generate unnamed values. Each member in this class carries a bitwise value with it.
The web form: editAttribs.aspx
Our aspx page will accept a directory or a file through the URL (by HTTP GET). I leave this up to you to perform, as it should be pretty easy to do. Make sure it is a virtual path though, otherwise you will have to modify my code. If it’s a file, preceed the path by an F, otherwise if it’s a directory, preceed the path by a D.
In the example above, we will display the attributes for a file, located at /images/xefteri.gif. If we wanted to see the attributes of the parent directory of this file (i.e. images), then the URL which we would have had to pass would be http://localhost/editAttribs.aspx?location=D/images.
The final template will look like this:
The HTML code that produces this is pretty much straight forward. All the attributes, images and checkboxes in the template are declared as a Web Controls, so that we can programatically set them. The StatusMessage label control will output any exceptions on updating the attributes. Our code-behind source code is simply editAttribs.aspx.vb and the class which we will inherit from is called EditAttribs.
<%@ Page Language="vb" src="editAttribs.aspx.vb" inherits="EditAttribs" %> <html> <head> <title>Xefteri - Edit Attributes</title> <style type="text/css"> #tblContents { font-family: Verdana; font-size: 70%; } </style> </head> <body bgcolor="#ffffff"> <form method="post" ID="setAttribs" runat="server"> <asp:Label runat="server" ID="StatusMessage" Visible="False" /> <table border="0" cellpadding="2" cellspacing="0"> <tr> <td bgcolor="#696969"> <table cellspacing="0" cellpadding="4" border="0" bgcolor="#ffffff" id="tblContents"> <tr> <td valign="top" align="right"><asp:Image runat="server" ID="imgType" /></td> <td> </td> </tr> <tr> <td valign="top" align="right"><b>Name:</b></td> <td valign="top"><asp:Label runat="server" ID="lblName" /></td> </tr> <tr> <td colspan="2"><hr size="1" width="100%" noshade></td> </tr> <tr> <td valign="top" align="right"><b>Virtual Location:</b></td> <td valign="top"><asp:Label runat="server" ID="lblLocation" /></td> </tr> <tr> <td valign="top" align="right"><b>Physical Location:</b></td> <td valign="top"><asp:Label runat="server" ID="lblAbsLocation" /></td> </tr> <tr> <td valign="top" align="right"><b>Size:</b></td> <td valign="top"><asp:Label runat="server" ID="lblSize" /></td> </tr> <tr> <td colspan="2"><hr size="1" width="100%" noshade></td> </tr> <tr> <td valign="top" align="right"><b>Created:</b></td> <td valign="top"><asp:Label runat="server" ID="lblCreated" /></td> </tr> <tr> <td valign="top" align="right"><b>Last Modified:</b></td> <td valign="top"><asp:Label runat="server" ID="lblModified" /></td> </tr> <tr> <td valign="top" align="right"><b>Last Accessed:</b></td> <td valign="top"><asp:Label runat="server" ID="lblLastAccessed" /></td> </tr> <tr> <td colspan="2"><hr size="1" width="100%" noshade></td> </tr> <tr> <td valign="top"><asp:CheckBox runat="server" ID="cbxReadOnly" Text="Read-Only" /></td> <td valign="top"><asp:CheckBox runat="server" ID="cbxHidden" Text="Hidden" /></td> </tr> <tr> <td colspan="2" valign="top"><asp:CheckBox runat="server" ID="cbxArchive" Text="Ready For Archiving" /></td> </tr> <tr> <td colspan="2" valign="top"><asp:CheckBox runat="server" ID="cbxIndexing" Text="For fast searching, allow Indexing Service to index this" /></td> </tr> <tr> <td colspan="2"><hr size="1" width="100%" noshade></td> </tr> <tr> <td colspan="2" valign="top"><asp:Image runat="server" ID="imgCompressed" />Compressed contents to save disk space</td> </tr> <tr> <td colspan="2" valign="top"><asp:Image runat="server" ID="imgEncrypted" /> Encrypted contents to secure data</td> </tr> <tr> <td colspan="2"> </td> </tr> <tr> <td colspan="2" align="center" valign="top"><asp:Button runat="server" ID="btnSetAttribs" Text="Update Attributes" onClick="UpdateAttribs_Click" /></td> </tr> <tr> <td colspan="2"> </td> </tr> </table> </td> </tr> </table> </form> </body> </html>
The code-behind: editAttribs.aspx.vb
Let’s take a look, piece by piece, at the code needed to get this done.
Option Explicit on Option Strict on Imports System Imports System.IO Imports Microsoft.VisualBasic Public Class EditAttribs Inherits System.Web.UI.Page 'declare variables used throughout the class Dim Private strLocation As String Dim Private strAbsLocation As String Dim Private originalLocation As String Dim Private isFile As Boolean Dim Private intTotalFolders As Integer Dim Private intTotalFiles As Integer Dim Private imgYes As String = "yes.gif" Dim Private imgNo As String = "no.gif" 'declare the various Web Controls Protected lblLocation As System.Web.UI.WebControls.Label Protected lblAbsLocation As System.Web.UI.WebControls.Label Protected imgType As System.Web.UI.WebControls.Image Protected lblName As System.Web.UI.WebControls.Label Protected lblSize As System.Web.UI.WebControls.Label Protected lblCreated As System.Web.UI.WebControls.Label Protected lblModified As System.Web.UI.WebControls.Label Protected lblLastAccessed As System.Web.UI.WebControls.Label Protected cbxReadOnly As System.Web.UI.WebControls.CheckBox Protected cbxHidden As System.Web.UI.WebControls.CheckBox Protected cbxIndexing As System.Web.UI.WebControls.CheckBox Protected cbxArchive As System.Web.UI.WebControls.CheckBox Protected imgCompressed As System.Web.UI.WebControls.Image Protected imgEncrypted As System.Web.UI.WebControls.Image Protected StatusMessage As System.Web.UI.WebControls.Label ...
Our custom class is called EditAttribs, and it inherits from the System.Web.UI.Page parent class. The first thing we do is dimension our global variables, and then declare our Web Controls which are used in the default.aspx page.
... Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim objFile As FileInfo Dim objFolder As DirectoryInfo 'read the location variable passed in the URL strLocation = Request.Params("location") originalLocation = strLocation If strLocation = "" Then strLocation = "/" End If isFile = strLocation.StartsWith("F") strLocation = Mid(strLocation, 2, Len(strLocation)) strAbsLocation = Server.MapPath(strLocation) lblLocation.Text = strLocation lblAbsLocation.Text = strAbsLocation 'if we are viewing a file If isFile Then imgType.ImageUrl = "file.gif" imgType.AlternateText = "Type: File" objFile = New FileInfo(strAbsLocation) lblName.Text = objFile.Name lblSize.Text = FormatSize(objFile.Length) lblCreated.Text = FormatDate(objFile.CreationTime) lblModified.Text = FormatDate(objFile.LastWriteTime) lblLastAccessed.Text = FormatDate(objFile.LastWriteTime) If ((CDbl(objFile.Attributes And FileAttributes.ReadOnly)) = FileAttributes.ReadOnly) Then cbxReadOnly.Checked = True End If If ((CDbl(objFile.Attributes And FileAttributes.Hidden)) = FileAttributes.Hidden) Then cbxHidden.Checked = True End If If ((CDbl(objFile.Attributes And FileAttributes.NotContentIndexed)) = _ FileAttributes.NotContentIndexed) Then cbxIndexing.Checked = False Else cbxIndexing.Checked = True End If If ((CDbl(objFile.Attributes And FileAttributes.Archive)) = FileAttributes.Archive) Then cbxArchive.Checked = True End If If ((CDbl(objFile.Attributes And FileAttributes.Compressed)) = FileAttributes.Compressed) Then imgCompressed.ImageUrl = imgYes imgCompressed.AlternateText = "Compressed" Else imgCompressed.ImageUrl = imgNo imgCompressed.AlternateText = "Not Compressed" End If If ((CDbl(objFile.Attributes And FileAttributes.Encrypted)) = FileAttributes.Encrypted) Then imgEncrypted.ImageUrl = imgYes imgEncrypted.AlternateText = "Encrypted" Else imgEncrypted.ImageUrl = imgNo imgEncrypted.AlternateText = "Not Encrypted" End If 'if we are viewing a directory Else imgType.ImageUrl = "directory.gif" imgType.AlternateText = "Type: Folder" objFolder = New DirectoryInfo(strAbsLocation) lblName.Text = objFolder.Name 'run recursive method to calculate directory size 'and include total number of subdirectories and files lblSize.Text = FormatSize(GetDirectorySize(objFolder.FullName)) " (" + intTotalFolders.ToString() + " folders, " + intTotalFiles.Tostring() + " files)" lblCreated.Text = FormatDate(objFolder.CreationTime) lblModified.Text = FormatDate(objFolder.LastWriteTime) lblLastAccessed.text = FormatDate(objFolder.LastWriteTime) If ((CDbl(objFolder.Attributes And FileAttributes.ReadOnly)) = FileAttributes.ReadOnly) Then cbxReadOnly.Checked = True End If If ((CDbl(objFolder.Attributes And FileAttributes.Hidden)) = FileAttributes.Hidden) Then cbxHidden.Checked = True End If If ((CDbl(objFolder.Attributes And FileAttributes.NotContentIndexed)) = _ FileAttributes.NotContentIndexed) Then cbxIndexing.Checked = False Else cbxIndexing.Checked = True End If If ((CDbl(objFolder.Attributes And FileAttributes.Archive)) = FileAttributes.Archive) Then cbxArchive.Checked = True End If If ((CDbl(objFolder.Attributes And FileAttributes.Compressed)) = FileAttributes.Compressed) Then imgCompressed.ImageUrl = imgYes imgCompressed.AlternateText = "Compressed" Else imgCompressed.ImageUrl = imgNo imgCompressed.AlternateText = "Not Compressed" End If If ((CDbl(objFolder.Attributes And FileAttributes.Encrypted)) = FileAttributes.Encrypted) Then imgEncrypted.ImageUrl = imgYes imgEncrypted.AlternateText = "Encrypted" Else imgEncrypted.ImageUrl = imgNo imgEncrypted.AlternateText = "Not Encrypted" End If End If End Sub ...
The Page_Load() method performs most of the actions. It first reads the location variable passed through the URL, to determine if we are looking at a file or a directory, and what its path is. We use the System.IO.FileInfo class to access the attributes if we are looking at a file, and the System.IO.DirectoryInfo class if it’s a directory. The Web Controls’ output is the same in both cases, except for the lblSize, which includes the number of subdirectories and files in parenthesis when we are looking at a directory. This closely resembles the Windows OS behavior when viewing a directory’s properties.
To determine if an attribute is set, we must convert the bit sum of the attribute into a Double value and compare it with the corresponding FileAttribute value. Let’s say for example, that we are trying to establish if the attribute Compressed has been set for a directory. We must convert the value of System.IO.DirectoryInfo.Attributes and System.IO.FileAttributes.Compressed into a Double value, and compare it with System.IO.FileAttributes.Compressed. If they are equal, then the attribute has been set.
... Protected Sub UpdateAttribs_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim fileAttribs As FileAttributes = FileAttributes.Normal If Request.Form("cbxReadOnly") = "on" Then fileAttribs = fileAttribs Or FileAttributes.ReadOnly End If If Request.Form("cbxHidden") = "on" Then fileAttribs = fileAttribs Or FileAttributes.Hidden End If If Request.Form("cbxArchive") = "on" Then fileAttribs = fileAttribs Or FileAttributes.Archive End If If Request.Form("cbxIndexing") <> "on" Then fileAttribs = fileAttribs Or FileAttributes.NotContentIndexed End If Try File.SetAttributes(strAbsLocation, fileAttribs) Server.Transfer("editAttribs.aspx?location=" + originalLocation) Catch exc As Exception StatusMessage.Text = exc.Message StatusMessage.Visible = True End Try End Sub ...
The UpdateAttribs_Click() method gets called when we click on the button “Update Attributes” in our aspx page. It checks every checkbox control to see if it was checked or not, and sets the file/directory attributes accordingly. To add the attribute ReadOnly for example, we simply create an instance of the FileAttributes class for the file or directory, and add the attribute to the Normal state using OR. This works the same way for both files and directories.
... Private Function FormatDate(ByVal datDate as Date) As String Return String.Format("{0:dddd, MMMM dd yyyy - hh:mm tt}", datDate) End Function Private Function FormatSize(ByVal dblFileSize As Double) As String If dblFileSize < 1024 Then Return String.Format("{0:N0} B", dblFileSize) ElseIf dblFileSize < 1024 * 1024 Then Return String.Format("{0:N2} KB", dblFileSize/1024) ElseIf dblFileSize < 1024 * 1024 * 1024 Then Return String.Format("{0:N2} MB", dblFileSize/(1024*1024)) End If End Function Private Function GetDirectorySize(ByVal path As String) As Long Dim lngDirSize As Long = 0 Dim objDirInfo As DirectoryInfo = New DirectoryInfo(path) Dim arrChildFiles As Array = objDirInfo.GetFiles() Dim arrSubFolders As Array = objDirInfo.GetDirectories() Dim objChildFile As FileInfo Dim objChildFolder As DirectoryInfo 'for each file found under the directory 'increment by 1 the number of files found 'and add its size to the total size For Each objChildFile in arrChildFiles intTotalFiles = intTotalFiles + 1 lngDirSize += objChildFile.Length Next 'for each subfolder found 'increment by 1 the number of sub-directories found 'and call this function recursively For Each objChildFolder in arrSubFolders intTotalFolders = intTotalFolders + 1 lngDirSize += GetDirectorySize(objChildFolder.FullName) Next Return lngDirSize End Function End Class
These last 3 functions perform some general tasks for us. The FormatDate() and FormatSize() give us a custom output for dates and sizes respectively. The GetDirectorySize() returns the total size of a directory – something that is not provided by the current release of the .NET framework. If you wish, you can take a look at a previous article, which goes into more detail on how to accomplish this. The GetDirectorySize() function also calculates the total number of sub-directories and files found under the directory that we are viewing.
Editing other document properties
There are other properties that are available in a file. These include, among others, the Title, Subject, Author, Category, Keywords & Comments. Microsoft makes available a DLL that can read and change these properties. Since this is outside the scope of this article, you can read more about how to do this from Microsoft or from others who have done this.
We have seen how to view and edit file and directory attributes by using the System.IO.FileAttributes class. We are not limited to these attributes though. We can expand on this if we want to, and also edit the CreationTime, LastAccessTime and LastWriteTime. These last 3 attributes are supported under both the System.IO.FileInfo and System.IO.DirectoryInfo classes, and and they can be set and retrieved.