Wanting to watch a directory for file changes? Or find out the .NET replacement for App. Path ? Or uncover how big that file is in English? If you re looking for the best file and folder techniques for your VB .NET applications, simply read on.
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”Read and Write Files folder.
Reading and writing to simple text files is perhaps one of the most common tasks in the programming world. The old VB6 way of doing this is now defunct , and a new .NET method is here, involving objects within the System.IO namespace.
The following functions help simplify the process of reading and writing to files. The first is called ReadTextFromFile and accepts a filename as a parameter. It returns the text from the specified file:
Public Function ReadTextFromFile(ByVal Filename As String) As String ' Returns text from the specified file On Error Resume Next Dim strFileText As String ' Open the file and launch StreamReader object Dim MyReader As System.IO.StreamReader = _ System.IO.File.OpenText(Filename) ' Read all text through to the end strFileText = MyReader.ReadToEnd ' Close the stream MyReader.Close() ' Return data Return strFileText End Function
The second code snippet is a method called WriteTextToFile , and it accepts a filename and the text to write as parameters:
Public Sub WriteTextToFile(ByVal Filename As String, ByVal Text As String) ' Writes the passed Text into the specified file ' Create file and StreamWriter object Dim MyWriter As System.IO.StreamWriter = _ System.IO.File.CreateText(Filename) ' Write text to the stream MyWriter.Write(Text) ' Close the stream MyWriter.Close() End Sub
Here is an example of each of these code snippets in action:
WriteTextToFile("c:\myfile.txt", TextBox1.Text) MessageBox.Show(ReadTextFromFile("c:\myfile.txt"))
If you re looking to manipulate files using the .NET Framework base classes, you should be heading to the System.IO.File class, where you ll find functions to delete files, copy files, check file attributes, and much more.
Here is a commented example demonstrating the most common uses of the File class:
Dim objFile As System.IO.File ' Check for existence of a file Dim blnExists As Boolean blnExists = objFile.Exists("c:\unlikely.txt") ' Delete a file objFile.Delete("c:\goodbye.txt") ' Copy a file objFile.Copy("c:\source.txt", "e:\destination.txt") ' Move a file objFile.Move("c:\oldlocation.txt", "e:\newlocation.txt") ' Check whether a file is read-only Dim blnReadOnly As Boolean blnReadOnly = CType(objFile.GetAttributes("c:\readonly.txt").ReadOnly, Boolean) ' Check whether a file is hidden Dim blnHidden As Boolean blnHidden = CType(objFile.GetAttributes("c:\hidden.txt").Hidden, Boolean) ' Check a file creation date Dim datCreated As DateTime datCreated = objFile.GetCreationTime("c:\created.txt")
It s worth noting that you don t have to create a new File object to use this functionality. The File class consists of what are known as shared methods , meaning that you can call them directly without having to instantiate a new object. This means you can delete a file with one direct line of code, like this:
System.IO.File.Delete("c:\goodbye.txt")
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”Check Files Are Identical folder.
Checking whether the contents of two files are identical is a surprisingly common request in the programming world, but, beyond simply comparing file sizes, many developers are unsure about how to actually check this.
There s no need to worry. This excellent CompareFiles function does it all for you, initially comparing by size and then byte by byte. If the two file paths you pass in as arguments match, the function passes back a True ; otherwise , it returns False .
Here s the code:
Public Function CompareFiles(ByVal File1 As String, _ ByVal File2 As String) As Boolean ' Compares contents of two files, byte by byte ' and returns true if no differences Dim blnIdentical As Boolean = True Dim objFS1 As System.IO.FileStream = _ New System.IO.FileStream(File1, System.IO.FileMode.Open) Dim objFS2 As System.IO.FileStream = _ New System.IO.FileStream(File2, System.IO.FileMode.Open) ' Begin by checking length If (objFS1.Length <> objFS2.Length) Then blnIdentical = False Else ' Start looping through, comparing bytes Dim intByteF1 As Integer Dim intByteF2 As Integer Do intByteF1 = objFS1.ReadByte() intByteF2 = objFS2.ReadByte() If intByteF1 <> intByteF2Then blnIdentical = False Exit Do End If Loop While (intByteF1 <> -1) End If ' Close files and set return value objFS1.Close() objFS2.Close() Return blnIdentical End Function
Here s how you might call this function in your code:
If CompareFiles("c:.txt", "c:.doc") Then MessageBox.Show("Files are identical!") Else MessageBox.Show("Files do not match!") End If
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”Writing to Temp File folder.
Temporary files are incredibly useful. Most applications use them to store information while running some sort of processing. And you can too. When you re finished, either delete the temporary file or leave it for the next Windows Disk Cleanup operation to thwart.
But how do you go about working with temporary files? Well, firstly you need to get a temporary filename, and the System.IO.Path has a shared function called GetTempFileName to help you here. Then you simply write to the file as normal.
This handy little function wraps all this functionality up for you into one neat function. Simply call WriteToTempFile and pass in your data. It ll return your temporary file path:
Public Function WriteToTempFile(ByVal Data As String) As String ' Writes text to a temporary file and returns path Dim strFilename As String = System.IO.Path.GetTempFileName() Dim objFS As New System.IO.FileStream(strFilename, _ System.IO.FileMode.Append, _ System.IO.FileAccess.Write) ' Opens stream and begins writing Dim Writer As New System.IO.StreamWriter(objFS) Writer.BaseStream.Seek(0, System.IO.SeekOrigin.End) Writer.WriteLine(Data) Writer.Flush() ' Closes and returns temp path Writer.Close() Return strFilename End Function
Here s how you might call this function in your code:
Dim strFilename As String = WriteToTempFile("My data for the temp file") MessageBox.Show(strFilename)
When it came to working with directories in Visual Basic 6, we had MkDir , RmDir , and CurDir . If you wanted anything more complicated, you either had to write your own API routines and sacrifice a few hours of development time, or reference the external FileSystemObject DLL and sacrifice the size of your final project, and, potentially , application speed.
In VB .NET, however, it s plain sailing... introducing the System.IO.Directory class!
Cram packed with shared methods, this class provides you with everything you need to create, move, delete, and check for the existence of directories. It also allows you to retrieve a list of files from a directory, plus obtain a list of the logical drives on your system.
Here s a chunk of sample code showing you how:
Dim objDir As System.IO.Directory ' Creates a directory objDir.CreateDirectory("c:\mydata") ' Delete a directory, recursively objDir.Delete("c:\temp", True) ' Get current directory Dim strCurDir As String = objDir.GetCurrentDirectory ' Check whether a directory exists Dim blnExists As Boolean = objDir.Exists("c:\mydata") ' Get string array of all directories in a path Dim strDirectories() As String = objDir.GetDirectories("c:\Program Files\") ' Get files in a directory Dim strFiles1() As String = objDir.GetFiles("c:\winnt") ' Get all *.DOC files in a directory Dim strFiles2() As String = objDir.GetFiles("c:\my documents", "*.doc") ' Move a directory objDir.Move("c:\backup", "c:\original") ' Retrieve array of drives Dim strDrives() As String = objDir.GetLogicalDrives
As with the System.IO.File class, it s worth noting that you don t have to create a new Directory object to use this functionality. The Directory class consists of shared methods , meaning that you can call them directly without having to instantiate a new object. This means that you can create a directory with one direct line of code, like this:
System.IO.Directory.CreateDirectory("c:\mydata")
Directory watching is one of those really cool techniques that took quite a large lump of skill to implement successfully in Visual Basic 6. With this latest version of VB, however, you can get such functionality by utilizing the brand new FileSystemWatcher class.
The new System.IO.FileSystemWatcher class can be set up either in code or, rather easier, by dragging and dropping the FileSystemWatcher component from the toolbox Component tab onto your application.
Next, you need to start setting properties. First, there s the Path property, which you need to set to the path of the directory that you wish to monitor, such as c:\ or e:\whitecliff\ . Next, there s the Filter property, where you specify which files you want to monitor. You can use *.* to keep an eye on everything in the directory, something like *.doc to check Word documents, or simply use an exact filename, such as datalog.txt .
There s also the NotifyFilter property, which lists exactly what you want your FileSystemWatcher object to inform you about. The default is FileName, DirectoryName, LastWrite , which means that you re informed when a filename or directory name is changed, or a file is written (that is, the LastWrite date and time changes). You can specify your own in code by typing the options from the dropdown list, separated by commas, or in code using the bitwise Or operator. Finally, there s the IncludeSubdirectories property. Change this to True if you want to monitor all subdirectories ”or False otherwise.
And after you ve set up your FileSystemWatcher object? Simply respond to its events (ensure that the EnableRaisingEvents property is set to True ). You have the Changed , Created , Deleted , and Renamed events all at your disposal. Each will fire off whenever a related action occurs. For example, if you re monitoring c:\mydata\ , with a filter of *.txt and the default NotifyFilter property value, and your user or an application edits the contents of c:\mydata\test.txt ”the Changed event will fire.
From within the event, you can use the e argument (the System.IO.FileSystemEventArgs object) to find out more about the altered file. You may use the e.FullPath property to find out the filename, for example ”or analyze the ChangeType or Path .
TOP TIP | There s an Error event associated with the FileSystemWatcher component, too. It only ever comes into play when far too many changes are being made at once (typically a result of badly chosen properties, or mass file alterations by the user) and the system just cannot cope. If it ever occurs, you ll know the events raised may not cover all items. Not always good to experience, but certainly a great event to be aware of. |
And that, quite simply, is how you can easily plug directly into the file system and directly monitor its contents. Doddle!
TOP TIP | Certain users of the FileSystemWatcher component complain they receive multiple (sometimes delayed) events firing in their application, for even the simplest of operations. You may receive two or three notifications for a simple file copy in Windows Explorer, for example. The official explanation is that each operation consists of a number of simpler actions, which each raise their own events (see the note in ˜FileSystemWatcher class, about FileSystemWatcher class in the help index). Unofficially, Microsoft has identified this as an issue and is working to resolve it. If this problem affects you, you need to create your own workaround ”such as maintaining your own unique list of alterations and then running your code a few seconds after the last event has fired . |
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”English File Size folder.
Humans and computers sometimes just don t get along. Take file sizes, for example. What a human being would call one gigabyte, a computer would call 1073741824 bytes. How do you translate one into the other? Pull up a chair .
The following handy function takes a number of bytes and translates it into a readable human string. Here s the code:
Public Function ConvertBytes(ByVal Bytes As Long) As String ' Converts bytes into a readable "1.44 MB", etc. string If Bytes >= 1073741824 Then Return Format(Bytes / 1024 / 1024 / 1024, "#0.00") _ & " GB" ElseIf Bytes >= 1048576 Then Return Format(Bytes / 1024 / 1024, "#0.00") & " MB" ElseIf Bytes >= 1024 Then Return Format(Bytes / 1024, "#0.00") & " KB" ElseIf Bytes > 0 And Bytes < 1024 Then Return Fix(Bytes) & " Bytes "Else Return "0 Bytes" End If End Function
Here s an example of the function in use. Here, the length of my file is 3027676 bytes ”and the ConvertBytes function returns 2.89MB . (See Figure 7-4.) Perfect:
Dim objInfo As New System.IO.FileInfo("c:\myfile.bmp") MessageBox.Show("File is " & ConvertBytes(objInfo.Length))
It s often useful to know the location of a particular folder. For example, you might want to know where the Favorites folder is, so you can add a link to your company Web site. Or you may need to know where the Desktop directory is, so you can save a file directly to it.
For this, the .NET Framework provides the System.Environment.GetFolderPath function. Simply call this, passing in a SpecialFolder enumeration. This will then return a string containing the appropriate path.
For example:
Dim MyFolderPath As String MyFolderPath = System.Environment.GetFolderPath( _ Environment.SpecialFolder.Favorites) MessageBox.Show(MyFolderPath)
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”File Associations folder.
Looking to open a program in its default application? Simply use the Start class and let Windows do the rest of the work for you, like this:
System.Diagnostics.Process.Start("c:\myfile.doc")
But sometimes you want a little more. Sometimes you want to retrieve the exact path to the default program associated with that file type.
With a little rummaging around in the registry, that s exactly what this next code snippet manages to achieve. Simply pass it the file extension, and it ll return the path of the associated application. Passing in the .doc extension on a machine running Office XP, for example, will return the exact path to the Microsoft Word executable.
It s worth noting that this function automatically handles system defined variables , plus removes a number of the excess parameters included in some registry entries. In other words, it works ”and well, too, unlike many samples of this technique currently floating around the Internet.
Here s the function:
Public Function GetAssociatedProgram(ByVal FileExtension As String) As String ' Returns the application associated with the specified FileExtension ' ie, path\denenv.exe for "VB" files Dim objExtReg As Microsoft.Win32.RegistryKey = _ Microsoft.Win32.Registry.ClassesRoot Dim objAppReg As Microsoft.Win32.RegistryKey = _ Microsoft.Win32.Registry.ClassesRoot Dim strExtValue As String Try ' Add trailing period if doesn't exist If FileExtension.Substring(0, 1) <> "." Then _ FileExtension = "." & FileExtension ' Open registry areas containing launching app details objExtReg = objExtReg.OpenSubKey(FileExtension.Trim) strExtValue = objExtReg.GetValue("") objAppReg = objAppReg.OpenSubKey(strExtValue & "\shell\open\command") ' Parse out, tidy up and return result Dim SplitArray() As String SplitArray = Split(objAppReg.GetValue(Nothing), """") If SplitArray(0).Trim.Length > 0 Then Return SplitArray(0).Replace("%1", "") Else Return SplitArray(1).Replace("%1", "") End If Catch Return "" End Try End Function
And here s how you might call it in your application:
Dim strPath As String = GetAssociatedProgram(TextBox1.Text) System.Diagnostics.Process.Start(strPath)
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”Get Drive Serial folder.
The serial number of a drive sounds like a relatively unimportant factor and certainly not worthy of an entry in this book. But it can actually prove highly useful.
Many developers, for example, check which drive Windows is installed on and then send the serial number of the drive ( alongside other unique system information) to their online validation service to activate the product. If they spot a particular user installing their product on a number of machines with different serial numbers , they suspect piracy and refuse to activate the product any further.
So, you see, retrieving a volume serial number can be very handy indeed.
To begin, you ll need to set a reference to the System.Management DLL. Click on Project Add Reference, find and highlight System.Management, click on Select, then hit OK.
Next, add the following function to your project:
Public Function GetDriveSerial(ByVal DriveLetter As String) As String ' Returns the serial number of the specified drive ' ie, GetDriveSerial("c:") Dim strSelectText As String = "Win32_logicaldisk='" & DriveLetter & "'" Dim objMO As New System.Management.ManagementObject(strSelectText) objMO.Get() Return CType(objMO.Properties("VolumeSerialNumber").Value, String) End Function
And this is our GetDriveSerial function. It works by creating an instance of the ManagementObject , then using an SQL-like string to retrieve details about the specified disk. We then pick out and return the VolumeSerialNumber property.
Here s how we might call this function in code:
Label1.Text = GetDriveSerial("C:")
A lot of confusion surrounds how to find out the startup path of your application ”the .NET equivalent of the App.Path property we had in Visual Basic 6. I ve personally written my own elongated routines, when in fact the solution is incredibly simple.
If you want to find out the application path of your Windows application, just reference the StartupPath property of the Application object, as so:
Dim strPath As String = Application.StartupPath
Note that the returned path doesn t include a trailing slash.
If you re developing a class library or similar project, however, you might stumble upon a slight problem. You see, not all projects support the Application object. In these cases, you can use the System.Reflection class to analyze the executing assembly and return its location. A little like this:
Dim strPath As String = System.Reflection.Assembly.GetExecutingAssembly().Location
A bit more in depth, but still pretty darn simple.
Download supporting files at www.apress.com .
The files for this tip are in the Ch7 ”INI Files folder.
Microsoft has been trying to get developers to move away from INI files for quite some time, pithily suggesting using the registry instead despite the fact that it s rarely a suitable replacement. Well, this hint persists with .NET, which proudly boasts absolutely no intrinsic support for INI files.
But, of course, there s always a workaround.
In previous versions of Visual Basic, you d access your INI file through the API. Well, in VB .NET, we can simply do the same. Admittedly, Microsoft would prefer us to run safe, managed code within the .NET Framework ”it can then automatically handle resources for you and ensure a more error-free environment.
However, you can still access unmanaged code, such as functions within the Windows API and COM components , with great ease.
In fact, here I ve developed a class to encapsulate the functionality of some of those older INI file API functions. The fact that they re wrapped up in a class also means that, should you ever implement another method of handling such settings, you can simply edit your code while the interfaces remain the same.
Anyway, enough talk ”here s my class code:
Public Class IniFile ' API functions Private Declare Ansi Function GetPrivateProfileString _ Lib "kernel32.dll" Alias "GetPrivateProfileStringA" _ (ByVal lpApplicationName As String, _ ByVal lpKeyName As String, ByVal lpDefault As String, _ ByVal lpReturnedString As System.Text.StringBuilder, _ ByVal nSize As Integer, ByVal lpFileName As String) _ As Integer Private Declare Ansi Function WritePrivateProfileString _ Lib "kernel32.dll" Alias "WritePrivateProfileStringA" _ (ByVal lpApplicationName As String, _ ByVal lpKeyName As String, ByVal lpString As String, _ ByVal lpFileName As String) As Integer Private Declare Ansi Function GetPrivateProfileInt _ Lib "kernel32.dll" Alias "GetPrivateProfileIntA" _ (ByVal lpApplicationName As String, _ ByVal lpKeyName As String, ByVal nDefault As Integer, _ ByVal lpFileName As String) As Integer Private Declare Ansi Function FlushPrivateProfileString _ Lib "kernel32.dll" Alias "WritePrivateProfileStringA" _ (ByVal lpApplicationName As Integer, _ ByVal lpKeyName As Integer, ByVal lpString As Integer, _ ByVal lpFileName As String) As Integer Dim strFilename As String ' Constructor, accepting a filename Public Sub New(ByVal Filename As String) strFilename = Filename End Sub ' Read-only filename property ReadOnly Property FileName() As String Get Return strFilename End Get End Property Public Function GetString(ByVal Section As String, _ ByVal Key As String, ByVal [Default] As String) As String ' Returns a string from your INI file Dim intCharCount As Integer Dim objResult As New System.Text.StringBuilder(256) intCharCount = GetPrivateProfileString(Section, Key, _ [Default], objResult, objResult.Capacity, strFilename) If intCharCount > 0 Then GetString = _ Left(objResult.ToString, intCharCount) End Function Public Function GetInteger(ByVal Section As String, _ ByVal Key As String, ByVal [Default] As Integer) As Integer ' Returns an integer from your INI file Return GetPrivateProfileInt(Section, Key, _ [Default], strFilename) End Function Public Function GetBoolean(ByVal Section As String, _ ByVal Key As String, ByVal [Default] As Boolean) As Boolean ' Returns a boolean from your INI file Return (GetPrivateProfileInt(Section, Key, _ CInt([Default]), strFilename) = 1) End Function Public Sub WriteString(ByVal Section As String, _ ByVal Key As String, ByVal Value As String) ' Writes a string to your INI file WritePrivateProfileString(Section, Key, Value, strFilename) Flush() End Sub Public Sub WriteInteger(ByVal Section As String, _ ByVal Key As String, ByVal Value As Integer) ' Writes an integer to your INI file WriteString(Section, Key, CStr(Value)) Flush() End Sub Public Sub WriteBoolean(ByVal Section As String, _ ByVal Key As String, ByVal Value As Boolean) ' Writes a boolean to your INI file WriteString(Section, Key, CStr(CInt(Value))) Flush() End Sub Private Sub Flush() ' Stores all the cached changes to your INI file FlushPrivateProfileString(0, 0, 0, strFilename) End Sub End Class
After you ve added this class code to your application, here s how you may want to use it:
Dim objIniFile As New IniFile("c:\data.ini") objIniFile.WriteString("Settings", "ClockTime", "12:59") Dim strData As String = _ objIniFile.GetString("Settings", "ClockTime", "(none)")
TOP TIP | As I mentioned earlier, Microsoft doesn t really like people using INI files. It doesn t fit in with its vision . They would prefer developers use code like this only as a stop-gap measure while upgrading existing systems, then move onto an XML-based method of storing settings. Visit www.gotdotnet.com/userfiles/demeester/XMLINIFile.zip for an INI file replacement, using XML. But, of course, it s completely up to you. |