Register and Manipulate Files in Whidbey
Take advantage of the new My.Computer functionality in Whidbey to implement a utility that manipulates the Registry to take care of bad e-mails.
- By Josef Finsel
Technology Toolbox: VB.NET
Exchange Server 2003 features a BadMail folder where Exchange places undeliverable messages. This directory comes complete with a small warning flagged as important: "Items in this folder must be manually deleted. You should include regular maintenance of this folder in your administration policy."
You could run a script to delete the e-mails in this folder, but if your system is small enough, you can scan and delete the e-mails without much fuss by opening the directory and looking at the files. The problem is that each e-mail creates three files, all with the same name but different extensions: BAD for the e-mail; BDR, which contains information about why the file was not deliverable; and BDP, which contains binary information related to the file. It isn't a big deal to open all the BAD files and take a quick look at them, but there is a better, simpler way.
Visual Studio 2005 (code-named Whidbey) lets you create a listbox that displays all the pending bad e-mail files and a textbox that displays the contents of the files. You can even add the ability to store the path to the BadMail directory so you don't need to enter it every time the program runs. This adds a little complexity to the program, but it's worth it because you end up with a module that simplifies working with the Registry. Download a working version of the app, including source code.
The Registry has long been a convenient place to store information for programs, but Visual Basic programmers have never really had easy ways to access it. For a long time, using the Registry effectively required using API callsbeasts that could be unforgiving if you made a mistake. This led many programmers to develop a BAS file with functions using those API calls that could be included in a project to make Registry calls easier. The good news is that Registry manipulation is much easier in Visual Studio 2005. The bad news is that it still isn't as easy as it could be. However, I'll show you how to build a module that includes all the tools you'll need to make Registry access easy.
The Registry is a database of information used to operate your computer. It consists of five hives, which correspond to tables in a database (see Table 1). Each hive can contain a large number of keys or subkeys. They behave similarly to the way folders and subfolders behave on the hard drive, an image reinforced by the way RegEdit displays the information.
Every key can contain either entries or more keys, with entries available as one of five types of information. For example, if you open up RegEdit (Start | Run | RegEdit), you'll find five folders that correspond to the five hives listed above. If you open HKEY_CURRENT_USER, then Software, then Microsoft, and finally Notepad, you'll find a list of values (see Figure 1). Notepad uses each of these values to display a text file.
As you can see in Figure 1, I've set the font for Notepad to Lucida Console. Every time Notepad starts up, it opens up \\HKEY_CURRENT_USER\Software\Microsoft\Notepad to read how all of the settings were when it was last closed. This keeps users from having to change these settings to their preferences manually every time they open up the applicationexactly what you want to avoid with this utility.
RegistryKey Holds the Key
The Microsoft.Win32 namespace contains all the tools you need to access the Registry. The one that you care about for the sample is Microsoft.Win32.RegistryKey. You can do anything you want with the Registry once you have a RegistryKey. The act of getting to any key in the Registry begins with the hive that contains the key you want to access. Fortunately, you can access these keys from My.Computer, a new feature in VS 2005. Getting the hive for HKEY_CURRENT_USER is simple:
Dim curUserKey as RegistryKey = _
After acquiring a RegistryKey, you can add new keys, add a new value, change a value, delete values or keys, and even get a reference to one of the subkeys. In short, you can use My.Computer.Registry to do anything you can do in RegEdit, once you have a RegistryKey. The biggest problem with using the RegistryKey is the nature of subkeys.
Assume you want to store Registry information in \\HKEY_CURRENT_USER\Software\Finsel\ProgramName, as I typically do. You must verify the existence of each key before you can create a subkey. In this case, the key is three levels deep. Coding each level would be far from fun every time you want to work with the Registry, so it would be simpler to create a module to handle all of this for youan easy task using recursion (see Listing 1).
Begin by defining the five hives as string variables that you can access outside the module. This makes it easier to define the whole key starting with the hive, without worrying that you've typed something wrong. The first function, GetKey, is the simplest. It requires only the path of the key, and then it goes about returning a RegistryKey. It begins by verifying that the hive is correct, then it removes the hive from the key path and tries to open the key from the hive. The nice part: You don't have to specify each individual key to get to the end result. However, you end up with nothing if the key you're looking for doesn't exist. It's at this point that you want to improve on the tools in the Win32 namespace.
The BaseKey variable won't contain a "\" if the key you want to create is a subkey directly under the hive. This means you can use the CreateSubKey method of the RegistryKey class to create the key and return it. If your Registry path isn't directly under the hive, then you need to work your way up the path until you find a part of the path that works. You can accomplish this by taking the last subkey in the Registry path and calling OpenKey. OpenKey takes a RegistryKey and a Registry path and tries to get that key. If the subkey doesn't exist, then OpenKey moves one level up the Registry path and calls itself until it finally creates a subkey and can pass that back to create all of the subkeys required.
Access Values in a Key
So far, you can access and create subkeys throughout the directory. Next, you need to focus on the values within a key. This simple process requires only that you get a RegistryKey and use the functions just described to get or set a value. The goal of this module is to create functions that are as easy to use as possible. To that end, you need to leave all of the Registry manipulation in the module and create GetRegistryValue and SetRegistryValue, which take a Registry path to the key you want, the name of the value, and the value itself. You can create five entry types, from string to binary. Fortunately, RegistryKey has one method, SetValue, that creates the correct entry based on the type of the parameter that is being passed. This is much simpler than the code I wrote for a similar module in VB6, which required separate functions for each type.
You need to add a default value to the GetRegistryValue method that is returned if the entry doesn't exist. At this point, you've created a module that allows you to access and manipulate the Registry in your program without having to refer to the Registry or deal with keys. This makes it much easier to write a program that utilizes the Registry.
You're now ready to deal with the issue of undeliverable e-mails. The Registry module shows how easy it is to interface with the Registry using My.Computer, but there is much more to it. Consider this text from Microsoft's Beta 1 documentation: "The My feature provides easy and intuitive access to a number of .NET Framework classes, enabling the Visual Basic user to interact with the computer, application, settings, resources, and so on."
There are six levels under My: Application, Computer, Forms, Resources, User, and WebServices. Use My.Computer.Registry to access the Registry. Use My.Computer.FileSystem to access and read all the files with a BAD extension in the directory specified by the public variable badEmailDirectory:
For Each foundFile As String In _
badEmailDirectory, True, "*.bad")
foundFile.LastIndexOf("\") + 1))
Now that you have a list of files, you want to display the text of the selected file in the textbox. In Visual Studio 2003, this requires getting a file stream and reading it. You can do this in Whidbey with a single line that uses the ReadAllText method to return a string containing the file:
emailBody.Text = _
badEmailDirectory & "\" & _
One step remains before you can delete the selected e-mail. Remember that you need to delete the BAD, BDR, and BDP files. Unfortunately, My.Computer.FileSystem doesn't let you use wildcard characters when deleting files, so you need to scan the directory for the matching files. This is just a variation on the For Each loop you use to load the listbox. The DeleteFile method has three parameters: the filename, a parameter that indicates whether to show a dialog box that informs the user you're about to delete a file, and a parameter that indicates whether to send it to the Recycle Bin. The sample deletes the files without transferring them first to the Recycle Bin or displaying a dialog box.
Add Some Finishing Touches
This simple sample utility is intended primarily to illustrate the power of Registry manipulation with Visual Studio 2005. However, you can give it a professional touch by including a splash screen and an About dialog. I've used a couple templates for these items in Visual Studio 2003, dragging and dropping them into a program where I add code to the stub module to display the splash screen while the program is loading and doing housekeeping chores. I've also added a line to the menu to display the About box. Both of these templates get information such as the program name and description from the App object.
Whidbey supplies templates for both a splash screen and an About box that fill in the program name and description by defaulting to the information found in My.Application. As you fill out this information, you can also take care of the last couple of options that you need to set to wrap up this program.
Start by opening up the project properties (Project | BadMail Properties). Instead of the dialog box that you get in VS 2003, Whidbey displays a page within the design environment with tabs running down the side. Begin by clicking on the first tab, Application. Note that the Windows Application Properties section includes a couple dropdown boxes. The first determines when the program shuts down, either when the main form closes or the last form closes. If you're writing an application that loads a lot of child forms, you no longer need to worry about writing the code to end the program when the forms close. Instead, you can make a selection from the dropdown box. Similarly, selecting a splash screen adds code to the MyApplication.vb file that loads the splash screen automatically. Sure, it's simple touch, but it's a nice one.
Now open up the Assembly information and add some text to explain what the project is. Then complete your project by adding a line of code to display the About box when selected from the menu.
Now you have both a simple utility for cleaning out your BadMail directory and a reusable module for extremely easy Registry manipulation. Rather than simply loading the BAD file into the textbox, you might want to add the ability to scroll the text so that it displays the information starting with the body of the e-mail. You also might want to add the capability to automatically delete e-mails or forward those that are obvious spam to email@example.com. You could base the criteria on keywords stored in an XML file, and you could create an easy way to add words based on new e-mails.