Posts filed under 'Code Examples'
A common function you’ll need when you’re dealing with a lot of files is a way to compare them and report any differences. In this code example, we’ll look at a way of comparing files and capturing the differences between them as we go.
The Design
In this design we’re going to place our CompareFiles routine in a class called FileCompare. The routine itself, CompareFiles, will be a public shared method of the class since we won’t be saving any stateful information. We will also be adding a nested class called FileDifference that will hold the differences we find. Our function will accept the full file names of the two files to compare as string parameters and return a generic list of FileDifference objects.
The Code
First, we’ll look at the code for our function and then discuss a few points in it.
Public Shared Function CompareFiles(ByVal file1 As String, ByVal file2 As String) As List(Of FileDifference)
Dim ListOfDifferences As New List(Of FileDifference)
If file1 <> file2 Then
If File.Exists(file1) AndAlso File.Exists(file2) Then
Using Stream1 As New FileStream(file1, FileMode.Open), Stream2 As New FileStream(file2, FileMode.Open)
If Stream1.Length <> Stream2.Length Then
ListOfDifferences = Nothing
Else
Dim File1Content As Integer
Dim File2Content As Integer
Do
File1Content = Stream1.ReadByte()
File2Content = Stream2.ReadByte()
If File1Content <> File2Content Then
ListOfDifferences.Add(New FileDifference(File1Content, File2Content, Stream1.Position))
End If
Loop While (File1Content <> -1)
End If
End Using
Else
ListOfDifferences = Nothing
End If
End If
Return ListOfDifferences
End Function
If the same file name is passed in for both parameters we’ll return an empty list. If one of the targeted files doesn’t exists or if they are of different lengths, we’ll return Nothing. Otherwise we’ll return the actual differences, if there are any.
Notice that the Using block can work with multiple objects, in this case our two FileStreams. Using will also take care of closing and disposing of the streams. Some people like to throw in a call to the Close method just for good measure. You can add this in just before the End Using if you feel more comfortable having it there.
When the bytes in the file don’t match, we create a new FileDifference object and add it to our list.
Now, let’s take a look at our FileDifference class
Public Class FileDifference
Private _file1Content As Integer
Private _file2Content As Integer
Private _filePosition As Long
Public Sub New(ByVal content1 As Integer, ByVal content2 As Integer, ByVal position As Long)
_file1Content = content1
_file2Content = content2
_filePosition = position
End Sub
Public ReadOnly Property File1Content() As Integer
Get
Return _file1Content
End Get
End Property
Public ReadOnly Property File2Content() As Integer
Get
Return _file2Content
End Get
End Property
Public ReadOnly Property FilePosition() As Long
Get
Return _filePosition
End Get
End Property
End Class
There’s nothing that special about this class. It’s just a handy way for us to pass back related values.
When you call this function from your code it would look something like this:
‘
‘
Dim Differences As List(Of FileCompare.FileDifference)
Differences = FileCompare.CompareFiles(txtFile1.Text, txtFile2.Text)
‘
‘
Extending It
Of course, this class is just the basics and you will probably want to find ways to extend it for your purposes. For example, you may want to override the ToString function of the FileDifference class to return something you can plug directly into a listbox, such as this:
Public Overrides Function ToString() As String
Return String.Concat(“File 1: 0x”, _file1Content.ToString(“x”), _
” File 2: 0x”, _file2Content.ToString(“x”), _
” at position “, _filePosition)
End Function
You might also want to create a version that deals with text files and compares their contents on a line by line or word by word basis. You could even expand it into a more complex compare and analyze solution if you wanted.
That’s it for this code example. I hope you’ve found it useful. You can click here to download the class file if you wish.. Let me know if you have any questions or observations about this code example by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
October 3rd, 2007
The System.Drawing.Icon class provides several ways for you to work with icons. In this article we’ll look at using its functions to get the icon for a file, creating an icon from a bitmap file, and converting a bitmap to an icon.
Get the Icon for a File
ExtractAssociatedIcon is a shared function that makes it easy to get the icon for a particular file. It will work with any type of file, not just executables. Here’s what the code looks like:
‘
‘
Me.Icon = Icon.ExtractAssociatedIcon(fileName)
‘
‘
In this case we’re setting the form icon to the extracted icon, like you might want to do if you had a MDI child form loading a document.
Another common usage is to load the icon into an ImageList control so that you can use it in a ListView or TreeView control. In the example below we’re assuming that we’re loading the contents of a specified folder into a Listview control.
Dim DirInfo As New DirectoryInfo(txtPath.Text)
For Each LookupFile As FileInfo In DirInfo.GetFiles()
imlFiles.Images.Add(LookupFile.Name, Icon.ExtractAssociatedIcon(LookupFile.FullName))
lvwFiles.Items.Add(LookupFile.Name, LookupFile.Name)
Next
Note that we retrieve the icon and load it into our ImageList, then load the ListView and point to our newly loaded image by key.
Creating an Icon from a Bitmap File
Let’s suppose you wanted to create an icon for your form from a bitmap image. How would you do that? The FromHandle method allows us to convert a bitmap into an icon. Here’s how to do it.
Private Sub ChangeIcon(ByVal bitmapFilename As String)
Using IconBitmap As New Bitmap(bitmapFilename)
Dim HIcon As IntPtr = IconBitmap.GetHicon()
Using newIcon As Icon = System.Drawing.Icon.FromHandle(HIcon)
Me.Icon = newIcon
DestroyIcon(newIcon.Handle)
End Using
End Using
End Sub
<System.Runtime.InteropServices.DllImportAttribute(“user32.dll”)> _
Private Shared Function DestroyIcon(ByVal handle As IntPtr) As Boolean
End Function
First, we load our bitmap file into a bitmap object and get an icon handle. Next we use the FromHandle function to create a new icon and set it to our form’s icon property. The last function, DestroyIcon, is used to free memory used in this process. DestroyIcon is imported in from user32.dll by using InteropServices. Note that we also have Using blocks since we’re working with unmanaged resources.
Converting a Bitmap File to an Icon File
As you can probably guess, the above method could be used to convert a bitmap file into an icon file. To do this you use the Save method of the icon object. Here’s how it works:
Private Sub BitmapToIcon(ByVal bitmapFilename As String, ByVal iconFilename As String)
Using BaseBitmap As New Bitmap(bitmapFilename)
Using IconBitmap As New Bitmap(32, 32)
Using IconGraphics As Graphics = Graphics.FromImage(IconBitmap)
IconGraphics.DrawImage(BaseBitmap, 0, 0, IconBitmap.Width, IconBitmap.Height)
Dim HIcon As IntPtr = IconBitmap.GetHicon()
Using newIcon As Icon = System.Drawing.Icon.FromHandle(HIcon)
Using IconStream As New System.IO.FileStream(iconFilename, FileMode.Create)
newIcon.Save(IconStream)
End Using
DestroyIcon(newIcon.Handle)
End Using
End Using
End Using
End Using
End Sub
The tricky part here is that we need to size the source bitmap to standard icon size, 32×32 pixels. So, to do this, we create a new bitmap and draw the source bitmap to it. We get the icon handle, create a new icon, then create a FileStream with our destination file name, and save the new icon. Once again, we’re using the DestroyIcon function and Using blocks to insure we release resources correctly.
Note that this method isn’t quite complete since it only works with one size of icon (32×32) with 16 colors. More work is needed to have it support more icon formats.
That’s our 3 examples. As I mentioned, they’re just the basics and you will want to work them into your application seamlessly. I don’t recommend just cutting and pasting them in. Take the time to integrate them into your app.
Let me know if you have any questions or suggestions about these routines by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
October 2nd, 2007
Here are three VB.NET functions you can use for some common file operations. These operations are: Getting the File Attributes, Reading an INI File, and Touching the date/time of a file. By providing wrappers for some of these common .NET functions it makes it easier to add checks and exception handling/prevention to the base routines. These routines are certainly open to expansion and improvement so use them as templates that you can build upon.
Getting File Attributes
Often you’ll find the need to determine the attributes of a file before you perform some action on it. Here’s a handy little function to do just that.
Public Function CheckFileAttribute(ByVal filename As String, ByVal attribute As IO.FileAttributes) As Boolean
If IO.File.Exists(filename) Then
If (IO.File.GetAttributes(filename) And attribute) > 0 Then
Return True
Else
Return False
End If
Else
Return False
End If
End Function
This function checks to see if the target file exists and, if it does, it checks the specified attribute to see if it’s set or not. We’re using the IO.FileAttributes Enum as the second parameter so that we can use the same function to check several different attributes like Hidden, Read-Only, Compressed, or Encrypted.
Note that if the file doesn’t exist a False value is returned. You may want to change this to True depending on how you want your program to flow. Another option would be to make it an optional parameter or an overload where you can decide on the file not found return value.
Here are some example calls:
If CheckFileAttribute(CurrentFile, IO.FileAttributes.Hidden) Then
'
..................
'
If CheckFileAttribute(CurrentFile, IO.FileAttributes.ReadOnly) Then
'
'
Read INI File Contents
Here’s a simple function to read the contents of a standard Windows INI file. The target filename is passed in and a generic list of string array is returned. The list does not contain commented out values but does contain section headers with the value for the header in both elements of the array. The VB.NET TextFieldParser class is used to parse the INI file, using the = sign as a field delimiter. Here’s the function:
Public Function ReadIniFile(ByVal filename As String) As List(Of String())
Dim IniContents As New List(Of String())
If Not IO.File.Exists(filename) Then
Return IniContents
End If
Using INIFileParser As FileIO.TextFieldParser = My.Computer.FileSystem.OpenTextFieldParser(filename, "=")
Dim CurrentLine() As String
With INIFileParser
.TrimWhiteSpace = True
Do While Not INIFileParser.EndOfData
CurrentLine = .ReadFields()
If CurrentLine(0).Length > 0 Then
Select Case CurrentLine(0).Substring(0, 1)
Case ";"
'ignore comments
Case "["
'section header
IniContents.Add(New String() {CurrentLine(0), CurrentLine(0)})
Case Else
IniContents.Add(New String() {CurrentLine(0), CurrentLine(1)})
End Select
End If
Loop
End With
End Using
Return IniContents
End Function
Note that this is a very simple function. For more sophisticated uses you would probably want to use a class to represent the section and have a list of key/value pair classes as members of that class. Also, some further error condition and parsing logic could be added to insure smoother operation.
Touch
Our last function is a ‘Touch’ function that sets the file dates and times to a specified date. Here’s the function:
Public Function Touch(ByVal filename As String, ByVal newDateTime As Date) As Boolean
If IO.File.Exists(filename) Then
IO.File.SetCreationTime(filename, newDateTime)
IO.File.SetLastWriteTime(filename, newDateTime)
IO.File.SetLastAccessTime(filename, newDateTime)
Else
Return False
End If
End Function
As you can see, this function sets the creation time, last write time, and last access time to the specified date and returns a True on success and False if the file doesn’t exist. You could expand this function by adding exception handling and more condition checking. You could also do overloaded versions for each date type.
That’s a wrap. Let me know what you think, if you notice any mistakes or if you have any questions by leaving a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 25th, 2007
The .NET Framework 2.0 BackgroundWorker class makes it easy to code simple threading into your applications. However, a common question that gets asked in VB related forums is, “How do I update a control, or controls, from the worker thread?”. Many people seem to be in search of a simple explanation of how to do this. In this article we’ll create a quick and easy example program that will walk you through the steps.
Getting Started
First, we will need to create a new Windows application. On our form we’ll add two buttons, btnStart and btnCancel, a progress bar, prgThread, and a listbox, lstValues. You may also want to import System.ComponentModel into the project namespace or onto the form or you can just spell it out like I’m doing below.
Next we’re going to add code. First, we declare the BackgroundWorker object, TestWorker. Note that we’re declaring it with events so that the event structure is built automatically.
‘
Private WithEvents TestWorker As System.ComponentModel.BackgroundWorker
‘
‘
Now, code is added for the start button, btnStart.
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
btnStart.Enabled = False
btnCancel.Enabled = True
lstValues.Items.Clear()
prgThread.Value = 0
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync()
End Sub
First, we disable the start button and enable the cancel button. Next we reset the values for our listbox and and progressbar. Now we’re ready to create our BackgroundWorker object. Notice that we tell it that we want to have it report progress and support cancellation. Finally, we tell it to run the worker.
The Worker
Here’s the code for our worker thread. This code is contained in the DoWork event. All it’s doing is looping 100 times and reporting the progress and sleeping 100ms between iterations. We also check for a cancellation state.
Private Sub TestWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestWorker.DoWork
Dim ListText As String
For Value As Integer = 0 To 100
If TestWorker.CancellationPending Then
Exit For
End If
ListText = String.Concat(“Item #”, Value)
TestWorker.ReportProgress(Value, ListText)
Threading.Thread.Sleep(100)
Next
End Sub
You can also see that in the ReportProgress function call that we’re passing in the current value and a string.
Updating Form Controls
In the ProgressChanged event, we get to process our values from our DoWork method. Here’s the code:
Private Sub TestWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles TestWorker.ProgressChanged
prgThread.Value = e.ProgressPercentage
lstValues.Items.Add(e.UserState)
End Sub
‘
In this routine, the progress bar and the listbox are updated with values from the worker thread. Remember that the UserState value is an object and will require conversion if you can’t use an object type.
Canceling the Worker
Now, what if you want to cancel the worker? Here’s the code that goes into our cancel button’s Click event:
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
TestWorker.CancelAsync()
End Sub
‘
‘
Here we call the CancelAsync method. However, this only sets a flag and, as shown above, our DoWork method has to check for a CancellationPending state. Otherwise, the work will continue until the DoWork routine is exited.
Finishing Things Up
When the work has been completed or canceled, the RunWorkerCompleted method is called. In our program, this allows us to reset our buttons.
Private Sub TestWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles TestWorker.RunWorkerCompleted
btnStart.Enabled = True
btnCancel.Enabled = False
End Sub
That’s how it’s done in a nutshell. Of course, handling multi-threading in your applications can become very tricky. It is recommended that you further review how to avoid problems with threading before implementing it.
Let me know if you have any questions about BackgroundWorker or if you have any suggestions or thoughts about this example by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 24th, 2007
When you’re depending on human interaction to enter a credit card number often you’ll find the user has accidentally typed in the wrong number. That’s why credit card number validation is a common operation in e-commerce web apps and some point-of-sale or order taking desktop apps. By pre-validating the card number a costly round trip to the credit card processor’s system can be avoided.
In this code example we will build a simple validation class that will tell us if a user entered credit card is valid and what type of card it is. I’ll also being using some techniques taken from a few of my recent tutorial articles.
The Luhn Algorithm
Credit card numbers are validated using the Luhn algorithm. This algorithm performs a simple Mod 10 checksum that’s intended to detect accidental errors in a sequence of numbers. For more on this algorithm see the Wikipedia link.
Credit Card Number Format and Test Values
In addition to validating according to Luhn algorithm, credit cards begin with certain numbers and have a certain length. Here are the formats for the four credit card types we’ll be looking at in this example:
- Visa: 13 or 16 digit numbers starting with 4
- MasterCard: 16 digit numbers starting with 5
- Discover: 16 digit numbers starting with 6011 or 65
- AMEX: 15 digit numbers starting with 34 or 37
For testing purposes you can use these values:
- 13 Digit Visa: 4222222222222
- 16 Digit Visa: 4111-1111-1111-1111
- MasterCard: 5431-1111-1111-1111
- Discover: 6011-6011-6011-6611
- AMEX: 341-1111-1111-1111
Coding the Routine
After defining the class, we define an Enum for the credit cards in our example:
Public Class CreditCard
Public Enum CreditCardTypes
Invalid = 1
Visa
Mastercard
Discover
Amex
End Enum
Notice that we’re going to use the first element as a placeholder for any invalid values. If you wanted to add more cards to the validation routine, just add them at the end of the enum.
Next, we’re going to define a list of regular expression patterns for our supported credit cards. Note that this is a private shared variable.
Private Shared CreditCardPatterns As New List(Of String) _
(New String() { _
"^(4d{12})|(4d{15})$", _
"^5[1-5]d{14}$", _
"(^(6011)d{12}$)|(^(65)d{14}$)", _
"^3[47]d{13}$"})
Private Shared _cardNumber As String
As you can see, we’re using a little trick here to initialize our generic list using a string array. Also note that our list matches up with the enum except of the invalid value. This makes it easy to look up our values in the list. Add any new values to the end of the list and make sure the pattern corresponds to the enum.
The Visa regular expression used here is rather simple and you may need more validation to handle all the variations of this card. More complex validation regular expressions can be found at RegExLib.
We will be storing the credit card number at the module level so that we can have access to it in our Predicate function we’ll be using later.
Now, let’s start our Validation routine. In it you’ll see that we’re passing in the card number as a string and returning the card type. Our first order of business is to strip out everything but the digits from our number and declare and initialize our variables.
Public Shared Function Validate(ByVal cardNumber As String) As CreditCardTypes
Dim CardType As CreditCardTypes = CreditCardTypes.Invalid
_cardNumber = Regex.Replace(cardNumber, "[^d]", String.Empty)
CardType = CType(CreditCardPatterns.FindIndex(AddressOf FindPattern), CreditCardTypes)
If CardType = CreditCardTypes.Invalid Then
_cardNumber = String.Empty
Return CreditCardTypes.Invalid
End If
Here we see the default return value being set and then our module level variable for the card number being set by using regular expressions to remove all but the numeric values from the string. Next we search for a match to our number by search our pattern list with a Predicate function called FindPattern. Here’s what the Predicate looks like
Private Shared Function FindPattern(ByVal value As String) As Boolean
If Regex.IsMatch(_cardNumber, value) Then
Return True
Else
Return False
End If
End Function
As you can see, we use regular expression matching to check our stripped card number value against the pattern. A true or false is returned that the FindIndex function uses to determine if the matching pattern has been found. If the pattern is found, the value is set to the matching enum while it is not, a -1 is returned which matches the invalid enum value.
Now, if we don’t have a match, we return with at this point. If there is a matching pattern, we move on to the Luhn algorithm to test this value further.
Dim Digits As Char() = _cardNumber.ToCharArray
_cardNumber = String.Empty
Dim Digit As Integer
Dim Sum As Integer = 0
Dim Alt As Boolean = False
Array.Reverse(Digits)
For Each Value As Char In Digits
Digit = Integer.Parse(Value)
If Alt Then
Digit *= 2
If Digit > 9 Then
Digit -= 9
End If
End If
Sum += Digit
Alt = Not Alt
Next
_cardNumber = String.Empty
If Sum Mod 10 = 0 Then
Return CardType
Else
Return CreditCardTypes.Invalid
End If
End Function
First, we convert our card number string to a character array and initialize our variables and clear out our working variable. Next, we reverse the array. This allows us to use a cleaner For Each method to loop through the array rather than working through it backwards using a counter variable.
Now, we apply the Luhn algorithm to each digit in the array by convert it from a char to an integer and applying the logic to it. After the loop we check the Mod of the Sum and either pass back the card type on True or that it’s an invalid card if False.
That’s all there is to the class, let’s take a look at how it might be called in a button click:
Private Sub btnProcessOrder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProcessOrder.Click
Dim CardType As CreditCard.CreditCardTypes = CreditCard.Validate(txtCreditCardNumber.Text)
If CardType = CreditCard.CreditCardTypes.Invalid Then
lblStatus.Text = "Not a valid credit card number"
Else
lblStatus.Text = String.Concat("Valid ", [Enum].GetName(GetType(CreditCard.CreditCardTypes), CardType), " Credit Card")
End If
End Sub
Notice that we’re using the [Enum].GetName function to get the text value for our returned enum value.
You can download the code for this example here
Adding to It
There are a number of ways to add to this class. First, it’s implemented as a shared function library. You might want to flesh it out to a full credit card class that would include making calls to a processing service and providing other functionality. Also, you may want to consider some changes to make it thread safe or you might want to add secure string functionality to it. You can also add additional cards or more checking for various card types like I mentioned about the Visa card. Remember this is just a basic example so if you use it you’ll want to adapt it to your situation.
Let me know if you found this example helpful or if I made any mistakes in it or if you have any other observations or questions by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 20th, 2007
Next Posts
Previous Posts