Chapter 35. Configuration and Resources

A very simple application performs a well-defined task that changes minimally over time. You may not need to configure such an application for different circumstances.

Many more complex applications, however, must be configured to meet different conditions. For example, the application might display different data for different kinds of users (such as data-entry clerks, supervisors, managers, and developers). Similarly, you might configure an application for various levels of support. You might have different configurations for trial, basic, professional, and enterprise versions.

The application may also need to save state information between sessions. It mightremember the types of forms that were last running, their positions, and their contents. The next time the program runs, it can restore those forms so the user can get back to work as quickly as possible.

Visual Studio provides many ways to store and use application configuration and resource information. This chapter describes some of these tools. It starts by describing the My namespace that was invented to make these tools easier to find. It then tells how an application can use environment variables, the Registry, configuration files, resource files, and the Application object.

This chapter does not explain how to work with disk files more directly. Databases, XML files, text files, and other disk files are generally intended for storage of larger amounts of data, rather than simple configuration and resource information. Those topics are described more thoroughly in Chapters 20, "Database Controls and Objects," and 38, "File-System Objects."

MY

In older versions of Visual Basic .NET, programmers discovered that many common tasks were difficult to perform. For example, many programs get the name of the user logged on to the computer, read a text file into a string, get the program's version number, or examine all of the application's currently loaded forms. Although you can accomplish all of these tasks in early versions of Visual Basic .NET, doing so is awkward.

To make these common tasks easier, the My namespace was introduced to provide shortcuts for basic chores. For example, to read the text in a file in Visual Basic .NET 2003, you must create some sort of object that can work with a file such as a StreamReader, use the object to read the file (the ReadToEnd method for a StreamReader), and then dispose of the object. The following code shows how you might do this in Visual Basic .NET 2003:

Dim stream_reader As New IO.StreamReader(file_name)
Dim file_contents As String = stream_reader.ReadToEnd()
stream_reader.Close()

This isn't too difficult, but it does seem more complicated than such a simple everyday task should be.

The My namespace provides a simpler method for reading a file's contents. The Computer .FileSystem.ReadAllText method reads a text file in a single statement. The following statement reads the text in the file C:TempTest.txt and displays it in a message box:

Dim file_contents As String =
    My.Computer.FileSystem.ReadAllText("C:TempTest.txt")

There is nothing new in the My namespace. All the tasks it performs you can already handle using existing methods. The My namespace just makes some things easier.

This section describes the My namespace and the shortcuts it provides.

Me and My

Some programmers confuse the Me object and the My namespace. Me is a reference to the object that is currently executing code. If a piece of code is inside a particular class, Me is a reference to the class object that is running.

For example, if the class is a form, then within the form's code, Me returns a reference to the running form. If the form's code must change the form's BackColor property, it can use the Me object to explicitly refer to its own form. It can also omit the keyword to refer to its form implicitly. That means the following two statements are equivalent:

Me.BackColor = SystemColors.Control
BackColor = SystemColors.Control

If you build several instances of a class, the code in each instance gets a different value for Me. Each instance's Me object returns a reference to that instance.

On the other hand, My isn't an object at all. It is a namespace that contains objects, values, routines, and other namespaces that implement common functions. The My namespace is a single unique entity shared by all of the code throughout the application.

It may help if you try not to think of the My namespace as a thing in and of itself. The My namespace doesn't do anything all alone. It needs to be paired with something within the namespace. Think of My.Application, My.User, My.Computer, and so forth. It makes sense to think of My.Computer as representing the computer.

My Sections

The following table briefly outlines the major sections within the My namespace. Other sections of this chapter and Appendix S, "The My Namespace," describe these sections in greater detail.

SECTION

PURPOSE

My.Application

Provides information about the current application: current directory, culture, and assembly information (such as program version number, log, splash screen, and forms)

My.Computer

Controls the computer hardware and system software: audio, clock, keyboard, clipboard, mouse, network, printers, Registry, and file system

My.Forms

Provides access to an instance of each type of Windows Form defined in the application

My.Resources

Provides access to the application's resources: strings, images, audio, and so forth

My.Settings

Provides access to the application's settings

My.User

Provides access to information about the current user

My.WebServices

Provides access to an instance of each XML web service referenced by the application

ENVIRONMENT

Environment variables are values that a program can use to learn information about the system. There are three types of environment variables that apply at the system, user, and process levels. As you may guess from their names, system-level variables apply to all processes started on the system, user-level variables apply to processes started by a particular user, and process-level variables apply to a particular process and any other processes that it starts.

Environment variables may indicate such things as the name of the operating system, the location of temporary directories, the user's name, and the number of processors the system has. You can also store configuration in environment variables for your programs to use.

Environment variables are loaded when a process starts, and they are inherited by any process launched by the initial process. For Visual Basic development, that means the variables are loaded when you start Visual Studio and they are inherited by the program you are working on when you start it. If you make changes to the system's environment variables, you need to close and reopen Visual Studio before your program will see the changes.

A program can also create temporary process-level variables that are inherited by launched processes and that disappear when the original process ends.

Visual Basic provides a couple of tools for working with the application's environment. The following sections describe two: the Environ function and the System.Environment object. Before you can read environment variables, however, you should know how to set their values.

Setting Environment Variables

Environment variables are normally set on a system-wide basis before the program begins. In older operating systems, batch files such as autoexec.bat set these values. More recent systems provide Control Panel tools to set environment variables.

Newer systems also use an autoexec.nt file to set environment variables that apply only to command-line (console) applications so they don't affect GUI applications. Sometimes you can use this fact to your advantage by giving different kinds of applications different environment settings.

To set environment variables in Windows XP, open the Control Panel, run the System applet, and select the Advanced tab. Alternatively, you can right-click My Computer or Computer in newer versions of Windows and select Properties from the context menu. Click the Environment Variables button to display the Environment Variables dialog box. Use the dialog's Add, Edit, and Delete buttons to add, modify, or remove environment variables.

To set environment variables in Windows Vista, open the Control Panel, open the System and Maintenance applet, and then open its System applet. On the left, click the "Advanced system settings" link. That link requires administrator privileges so Vista displays a UAC privilege elevation dialog. Enter an administrator's password and click OK to get to the System Properties dialog. Select the Advanced tab and click the Environment Variables button to display the Environment Variables dialog box. Use the dialog's Add, Edit, and Delete buttons to add, modify, or remove environment variables.

Be careful to use the variables properly. Use system variables when a value should apply to all processes started by all users, user variables when a value should apply to all processes started by a particular user, and process variables when a value should apply to a process and any processes that it starts.

Using Environ

At runtime, a Visual Basic application can use the Environ function to retrieve environment variable values. If you pass this function a number, it returns a string giving the statement that assigns the corresponding environment variable. For example, Environ(1) might return the following string:

ALLUSERSPROFILE=C:ProgramData

You should pass the function a number between 1 and 255. Environ returns a zero-length string if the number does not correspond to an environment variable. The following code uses this fact to list all the application's environment variables. When it finds a variable that has zero length, it knows it has read all of the variables with values.

For i As Integer = 1 To 255
    If Environ(i).Length = 0 Then Exit For
    Debug.WriteLine(Environ(i))
Next i

                                                  
Using Environ

Example program ListEnviron uses similar code to display all of the environment variables' assignment statements. Example program ListEnvironValues, which is also available for download, uses the String class's Split method to separate the environment variables' names and values and displays them in separate columns in a ListView control.

If you pass the Environ function the name of an environment variable, the function returns the variable's value or Nothing if the variable does not exist. The following code displays the value assigned to the USERNAME variable:

MessageBox.Show(Environ("USERNAME"))

Using System.Environment

The Environ function is easy to use, but it's not very flexible; it cannot create or modify variable values.

Of course sometimes that lack of flexibility can be an asset. A malicious program cannot use Environ to mess up the system's environment variables so it gives you one less potential problem.

The System.Environment object provides methods for getting and setting process-level environment variables. It also provides properties and methods for working with many other items in the application's environment. The following table describes the Environment object's most useful properties.

PROPERTY

PURPOSE

CommandLine

Returns the process's command line.

CurrentDirectory

Gets or sets the fully qualified path to the current directory.

ExitCode

Gets or sets the process's exit code. If the program starts from a Main function, that function's return value also sets the exit code.

HasShutdownStarted

Returns True if the common language runtime is shutting down.

MachineName

Returns the computer's NetBIOS name.

NewLine

Returns the environment's defined new line string. For example, this might be a carriage return followed by a line feed.

OSVersion

Returns an OperatingSystem object containing information about the operating system. This object provides the properties ServicePack (name of the most recent service pack installed), Version (includes Major, Minor, Build, and Revision; ToString combines them all), VersionString (combines the operating system name, version, and most recent service pack), and Platform, which can be Unix, Win32NT (Windows NT or later), Win32S (runs on 16-bit Windows to provide access to 32-bit applications), Win32Windows (Windows 95 or later), or WinCE.

ProcessorCount

Returns the number of processors on the computer.

StackTrace

Returns a string describing the current stack trace.

SystemDirectory

Returns the system directory's fully qualified path.

TickCount

Returns the number of milliseconds that have elapsed since the system started.

UserDomainName

Returns the current user's network domain name.

UserInteractive

Returns True if the process is interactive. This only returns False if the application is a service process or web service.

UserName

Returns the name of the user who started the process.

Version

Returns a Version object describing the Common Language Runtime. This object provides the properties Major, Minor, Build, and Revision. Its ToString method combines them all.

WorkingSet

Returns the amount of physical memory mapped to this process in bytes.

Example program SystemEnvironment, which is available for download on the book's web site, displays the values of many of the Environment object's properties.

The following table describes the Environment object's most useful methods.

METHOD

PURPOSE

Exit

Ends the process immediately. Form Closing and Closed event handlers do not execute.

ExpandEnvironment-Variables

Replaces environment variable names in a string with their values. For example, the following code displays the current user's name:

MessageBox.Show(Environment.ExpandEnvironment
Variables("I am %username%."))

GetCommandLineArgs

Returns an array of strings containing the application's command-line arguments. The first entry (with index 0) is the name of the program's executable file.

GetEnvironmentVariable

Returns an environment variable's value.

GetEnvironmentVariables

Returns an IDictionary object containing the names and values of all environment variables.

GetFolderPath

Returns the path to a system folder. This method's parameter is a SpecialFolder enumeration value such as Cookies, Desktop, SendTo, or Recent. See the online help for a complete list of available folders.

GetLogicalDrives

Returns an array of strings containing the names of the logical drives on the current computer.

SetEnvironmentVariable

Creates, modifies, or deletes an environment variable.

The SetEnvironmentVariable method lets you set environment variables at the system, user, and process level. If you set a variable's value to Nothing, this method deletes the variable. For system and user values, it updates the Registry appropriately to set the values. Example program EnvironmentVariableLevels, which is available for download on the book's web site, uses SetEnvironmentVariable to get and set variable values. For more information on the SetEnvironmentVariable method, see msdn2.microsoft.com/library/96xafkes.aspx.

REGISTRY

The System Registry is a hierarchical database that stores values for applications on the system. The hierarchy's root is named MyComputer and is divided into the several subtrees that are also called hives. Which hives are available depends on your operating system. The following table summarizes the most commonly available hives. (The "HKEY" part of each name stands for "hive key.")

REGISTRY BRANCH

CONTAINS

HKEY_CLASSES_ROOT

Definitions of types or classes, and properties associated with those types.

HKEY_CURRENT_CONFIG

Information about the system's current hardware configuration

HKEY_CURRENT_USER

The current user's preferences (such as environment variable settings, program group information, desktop settings, colors, printers, network connections, and preferences specific to applications). Each user has separate HKEY_CURRENT_USER values. This is usually the subtree where a Visual Basic application stores and retrieves its settings.

HKEY_DYN_DATA

Performance data for Windows 95, 98, and Me. (Yes, this is a bit outdated but this hive is still there.)

HKEY_LOCAL_MACHINE

Information about the computer's physical state, including bus type, system memory, installed hardware and software, and network logon and security information.

HKEY_USERS

Default configuration information for new users and the current user's configuration.

Depending on your operating system, the Registry may also contain the unsupported keysHKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_NLSTEXT, and HKEY_ PERFORMANCE_TEXT.

Many applications store information in the Registry. The HKEY_CURRENT_USER subtree is particularly useful for storing individual users' preferences and other configuration information.

Lately, the Registry has gone out of style for saving configuration information. Microsoft now recommends that you store this kind of data locally within a user's data storage area. This makes sense because it makes it easier to copy the settings (they're just files) and helps reduce clutter in the Registry. You can use the configuration files settings (see the section "Configuration Files" later in this chapter) or you can store data in XML files.

Visual Basic provides two main ways to access the Registry. First, you can use the Visual Basic native Registry methods. Second, you can use the tools in the My.Computer.Registry namespace. These two methods are described in the following sectons.

You can also use API functions to manipulate the Registry. These are more complicated and not generally necessary (the My.Computer.Registry namespace contains some very powerful tools), so they are not described here.

Native Visual Basic Registry Methods

Visual Basic provides four methods for saving and reading Registry values for a particular application: SaveSetting, GetSetting, GetAllSettings, and DeleteSetting.

The SaveSetting method saves a value into a Registry key. This routine takes as parameters the name of the application, a section name, the setting's name, and the setting's value. For example, the following code saves the value stored in the m_CurrentDirectory variable in the RegistrySettings application's Config section with the name CurrentDirectory:

SaveSetting("RegistrySettings", "Config", "CurrentDirectory",
    m_CurrentDirectory)

SaveSetting automatically creates the application and section areas in the Registry if they don't already exist.

This value is saved at the following Registry location. This is all one name; it just doesn't fit on one line here:

HKEY_CURRENT_USERSoftwareVB and VBA Program Settings
    RegistrySettingsConfigCurrentDirectory

If you use the Visual Basic SaveSetting, GetSetting, GetAllSettings, and DeleteSetting methods, you don't need to worry about the first part of this Registry path. You need only to remember the application name, section name, and setting name.

The GetSetting function retrieves a Registry value. It takes as parameters the application name, section name, and setting name you used to save the value. It can optionally take a default value to return if the setting doesn't exist in the Registry. The following code displays the value saved by the previous call to SaveSetting. If no value is saved in the Registry, it displays the string <none>.

MessageBox.Show(GetSetting("RegistrySettings", "Config", "CurrentDirectory",
 "<none>"))

The GetAllSettings function returns a two-dimensional array of name and value pairs for a Registry section. The following code uses GetAllSettings to fetch the values stored in the RegistrySettings application's Config section. It loops through the results, displaying the setting names and values.

Dim settings As String(,) = GetAllSettings("RegistrySettings", "Config")
For i As Integer = 0 To settings.GetUpperBound(0)
    Debug.WriteLine(settings(i, 0) & " = " & settings(i, 1))
Next i

If an application needs to use all of the settings in a section, GetAllSettings may be faster than using GetSetting repeatedly.

The DeleteSetting method removes a setting, section, or an entire application's setting area from the Registry. The following code shows how to remove each of those kinds of items:

' Remove the RegistrySettingsRegistrySettings/Config/CurrentDirectory setting.
DeleteSetting("RegistrySettings", "Config", "CurrentDirectory")

' Remove the RegistrySettings/Config section.
DeleteSetting("RegistrySettings", "Config")

' Remove all of the RegistrySettings application's settings.
DeleteSetting("RegistrySettings")

Example program RegistrySettings, which is available for download on the book's web site, demonstrates each of Visual Basic's Registry commands.

My.Computer.Registry

The My.Computer.Registry namespace provides objects that manipulate the Registry. My.Computer.Registry has seven properties that refer to objects of type RegistryKey. The following table lists these objects and the corresponding Registry subtrees.

MY.COMPUTER.REGISTRY PROPERTY

REGISTRY SUBTREE

ClassesRoot

HKEY_CLASSES_ROOT

CurrentConfig

HKEY_CURRENT_CONFIG

CurrentUser

HKEY_CURRENT_USER

DynData

HKEY_DYNAMIC_DATA

LocalMachine

HKEY_LOCAL_MACHINE

PerformanceData

HKEY_PERFORMANCE_DATA

Users

HKEY_USERS

The program can use these RegistryKey objects to work with the corresponding Registry subtree. The following table describes the most useful properties and methods provided by the RegistryKey class.

PROPERTY OR METHOD

PURPOSE

Name

Returns the key's Registry path.

Close

Closes the key and writes it to disk if it has been modified.

CreateSubKey

Creates a new subkey or opens an existing subkey within this key.

DeleteSubKey

Deletes the specified subkey. This method will delete the subkey if it contains values, but not if it contains other subkeys. The subkey to be deleted need not be a direct child of this key. For example, the following code uses the CurrentUser RegistryKey object to delete the descendant key SoftwareVB and VBA Program SettingsMyComputerRegistryConfig:

My.Computer.Registry.CurrentUser.DeleteSubKey("Software
VB and VBA Program SettingsRegistrySettingsConfig")

DeleteSubKeyTree

Recursively deletes a subkey and any child subkeys it contains. The subkey to be deleted need not be a direct child of this key. For example, the following code uses the CurrentUser RegistryKey object to delete all of the settings for the RegistrySettings application:

My.Computer.Registry.CurrentUser.DeleteSubKeyTree
("SoftwareVB and VBA Program SettingsRegistrySettings")

DeleteValue

Deletes a value from the key.

Flush

Writes any changes to the key into the Registry.

GetSubKeyNames

Returns an array of strings giving subkey names.

GetValue

Returns the value of a specified value within this key.

GetValueKind

Returns the type of a specified value within this key. This can be Binary, DWord, ExpandString, MultiString, QWord, String, or Unknown.

GetValueNames

Returns an array of strings giving the names of all of the values contained within the key.

OpenSubKey

Returns a RegistryKey object representing a descendant key. Parameters give the subkey name, and indicate whether the returned RegistryKey should allow you to modify the subkey.

SetValue

Sets a value within the key.

SubKeyCount

Returns the number of subkeys that are this key's direct children.

ToString

Returns the key's name.

ValueCount

Returns the number of values stored in this key.

The following example opens the HKEY_CURRENT_USERSoftwareVB and VBA Program SettingsRegistrySettingsConfig key. It reads the CurrentDirectory value from that key using the default value "C:" and saves the result in the variable current_directory. It closes the key and then uses the DeleteSubKey method to delete the RegistrySettings application's Config section.

' Open the application's Config subkey.
Dim config_section As Microsoft.Win32.RegistryKey =
    My.Computer.Registry.CurrentUser.OpenSubKey(
        "SoftwareVB and VBA Program SettingsRegistrySettingsConfig")

' Get the CurrentDirectory value.
Dim current_directory As String =
    CType(config_section.GetValue("CurrentDirectory", "C:"), String)

' Close the subkey.
config_section.Close()

' Delete the application's whole Config section.
My.Computer.Registry.CurrentUser. DeleteSubKey DeleteSubKey (
    "SoftwareVB and VBA Program SettingsRegistrySettingsConfig")

The following code shows the equivalent operations using the native Registry methods of Visual Basic:

' Get the CurrentDirectory value.
Dim current_directory As String =
GetSetting("RegistrySettings", "Config", "CurrentDirectory", "C:")

' Delete the application's whole Config section.
DeleteSetting("RegistrySettings", "Config")

It is generally easier to use the native Registry methods of Visual Basic. Those methods work only with values in the HKEY_CURRENT_USERSoftwareVB and VBA Program Settings Registry subtree, however. If you need to access keys and values outside of this subtree, you must use the My.Computer.Registry objects.

Example program MyComputerRegistry, which is available for download on the book's web site, demonstrates many useful My.Computer.Registry operations. It does the same things as program RegistrySettings mentioned in the previous section except it uses My.Computer.Registry instead of Visual Basic's native Registry methods.

CONFIGURATION FILES

Configuration files let you store information for a program to use at runtime in a standardized external file. You can change the values in the configuration file, and the program will use the new value the next time it starts. That enables you to change some of the application's behavior without needing to recompile the executable program.

One way to use configuration files is through dynamic properties. Dynamic properties are automatically loaded from the configuration file at runtime by Visual Basic.

Start by defining the settings you will bind to the dynamic properties. In Solution Explorer, double-click My Project and select the Settings tab to see the property page shown in Figure 35-1. Use this page to define the settings that you will load at runtime.

Use this page to define application settings.

Figure 35.1. Use this page to define application settings.

A setting's scope can be Application or User. A setting with Application scope is shared by all of the program's users. Settings with User scope are stored separately for each user so different users can use and modify their own values.

Next, add a control to a form and select it. In the Properties window, open the ApplicationSettings entry, click the PropertyBinding subitem, and click the ellipsis to the right to display a list of the control's properties.

Select the property that you want to load dynamically and click the drop-down arrow on the right to see a list of defined settings that you might assign to the property. Figure 35-2 shows the Application Setting dialog box with this drop-down list displayed for a control's Text property. From the list, select the setting that you want to assign to the property.

Use the drop-down list to assign a setting to the dynamic property.

Figure 35.2. Use the drop-down list to assign a setting to the dynamic property.

Visual Studio adds the setting to the program's configurationfile. If you open Solution Explorer and double-click the app.config entry, you'll see the new dynamic property.

The following text shows the configuration setting sections of an App.config file. The applicationSettings section defines the settings shown in Figure 35-1.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      ...
      <userSettings>
            <ConfigFile.My.MySettings>
                  <setting name="txtBackColor" serializeAs="String">
                        <value>Yellow</value>
                  </setting>
                  <setting name="txtForeColor" serializeAs="String">
                        <value>Blue</value>
                  </setting>
                  <setting name="txtFontName" serializeAs="String">
                        <value>Comic Sans MS</value>
                  </setting>
                  <setting name="txtFontSize" serializeAs="String">
                        <value>50</value>
                  </setting>
                  <setting name="clrForeColor" serializeAs="String">
                         <value>Blue</value>
                  </setting>
                  <setting name="clrBackColor" serializeAs="String">
                         <value>Yellow</value>
                  </setting>
                  <setting name="fntFont" serializeAs="String">
                         <value>Comic Sans MS, 48pt</value>
                  </setting>
            </ConfigFile.My.MySettings>
      </userSettings>
</configuration>
                                                   
Use the drop-down list to assign a setting to the dynamic property.

When the application starts, Visual Basic loads the app.config file, reads the settings, and assigns their values to any properties bound to them.

So far, this is just a very roundabout way to set the control's property value. The real benefit of this method comes later when you want to change this setting. If you look in the compiled application's directory (normally the binDebug directory when you're developing the program), you'll find a file with the same name as the application but with a .config extension. If the application is called ConfigFile.exe, then this file is called ConfigFile.exe.config.

If you open this file with any text editor and change the value of a setting, the program uses the new value the next time it runs. For example, if you change the value of clrBackColor from Yellow to Orange, then the next time the program runs, any controls that use that color for their backgrounds will now be orange. Instead of recompiling the whole application, you only need to change this simple text file. If you have distributed the application to a large number of users, you need only to give them the revised configuration file and not a whole new executable.

When you make a new setting, Visual Basic automatically generates code that adds the setting to the My.Settings namespace, so the program can easily get their values. The following code displays the values of the txtFontSize and txtFontName settings:

MessageBox.Show(My.Settings.txtFontSize & "pt " & My.Settings.txtFontName)

The My.Settings namespace provides several other properties and methods that make working with settings easy. The following table summarizes the most useful My.Settings properties and methods.

PROPERTY OR METHOD

PURPOSE

Item

A name-indexed collection of the values for the settings.

Properties

A name-indexed collection of SettingsProperty objects that contain information about the settings, including their names and default values.

Reload

Reloads the settings from the configuration file.

Save

Saves any modified settings into the configuration file. The program can modify settings with user scope. Settings with application scope are read-only.

Example program ShowSettings uses the following code to display the settings listed in the My.Settings.Properties collection:

Imports System.Configuration

Public Class Form1
      Private Sub Form1_Load() Handles MyBase.Load
            For Each settings_property As SettingsProperty In
             My.Settings.Properties
                  Dim new_item As New ListViewItem(settings_property.Name)
                  new_item.SubItems.Add(settings_property.DefaultValue.ToString)
                  lvSettings.Items.Add(new_item)
            Next settings_property
lvSettings.Columns(0).Width = −2
            lvSettings.Columns(1).Width = −2
      End Sub
End Class

                                                  
Use the drop-down list to assign a setting to the dynamic property.

When a program closes, it automatically saves any changes to User scope settings. However, if the program crashes, it does not have a chance to save any changes. If you want to be sure changes are saved, call My.Settings.Save after the user changes settings.

Example program SaveSettings, which is also available for download, uses two settings: a User scope color named clrForm that determines the form's background color and an Application scope color named clrButton that determines the background colors of the program's two buttons. The program provides Form Color and Button Color buttons to let you change the two color settings. If you change the colors, close the program, and restart it, you'll see that the User scope form color is saved, but the Application scope button color is not.

RESOURCE FILES

Resource files contain text, images, and other data for the application to load at runtime. The intent of resource files is to let you easily replace one set of resources with another.

One of the most common reasons for using resource files is to provide different resources for different languages. To create installation packages for different languages, you simply ship the executable and a resource file that uses the right language. Alternatively, you can ship resource files for all of the languages you support and then let the application pick the appropriate file at runtime based on the user's computer settings.

Resource files are not intended to store application configuration information and settings. They are intended to hold values that you might want to change, but only infrequently. Configurations and settings, on the other hand, may change relatively often. You should store frequently changing data in configuration files or the System Registry rather than in resource files.

The distinction is small and frankly somewhat artificial. Both configuration files and resource files store data that you can swap without recompiling the application. Rebuilding resource files can be a little more complex, however, so perhaps the distinction that configuration and setting data changes more frequently makes some sense.

Resource files can also be embedded within a compiled application. In that case, you cannot swap the resource file without recompiling the application. Although this makes embedded resource files less useful for storing frequently changing information, they still give you a convenient place to group resource data within the application. This is particularly useful if several parts of the application must use the same pieces of data. For example, if every form should display the same background image, it makes sense to store the image in a common resource file that they can all use.

The following sections describe the four most common types of resources: application, embedded, satellite, and localization.

Application Resources

To create application resources in Visual Basic, open Solution Explorer, double-click the My Project entry, and select the Resources tab. Use the dropdown on the left to select one of the resource categories: Strings, Images, Icons, Audio, Files, or Other. Figure 35-3 shows the application's Resources tab displaying the application's images.

The Resources tab contains image and other resources used by the application.

Figure 35.3. The Resources tab contains image and other resources used by the application.

If you double-click an item, Visual Studio opens an appropriate editor. For example, if you double-click a bitmap resource, Visual Studio opens the image in an integrated bitmap editor.

Click the Add Resource dropdown list and select Add Existing File to add a file from the disk to the program's resources. Use the dropdown's Add New String, Add New Icon, or Add New Text File commands to add new items to the resource file. The dropdown's New Image item opens a cascading submenu that lets you create new PNG, bitmap, GIF, JPEG, and TIFF images.

Using Application Resources

When you create application resources, Visual Studio automatically generates code that adds strongly typed resource properties to the My.Resources namespace. If you open Solution Explorer and click the Show All Files button, you can see the Resources.Designer.vb file that contains this code. The Solution Explorer path to this file is My Project/Resources.resx/Resources.Designer.vb.

For example, the following code shows the property that Resources.Designer.vb contains to retrieve the Octahedron image resource:

Friend ReadOnly Property Octahedron() As System.Drawing.Bitmap
    Get
        Dim obj As Object =
            ResourceManager.GetObject("Octahedron", resourceCulture)
        Return CType(obj,System.Drawing.Bitmap)
    End Get
End Property

                                                  
Using Application Resources

The following code shows how a program can use these My.Resources properties. It sets the lblGreeting control's Text property to the string returned by the My.Resources.Greeting property. Then it sets the form's BackgroundImage property to the image resource named Dog.

Private Sub Form1_Load() Handles MyBase.Load
    lblGreeting.Text = My.Resources.Greeting
    Me.BackgroundImage = My.Resources.Dog
End Sub

                                                  
Using Application Resources

Because these property procedures are strongly typed, IntelliSense can offer support for them. If you type My.Resources, IntelliSense lists the values defined in the application's resource file.

The strongly typed resource properties use a ResourceManager object to fetch the application's resources. The My.Resources namespace exposes that object through its ResourceManager property, so you can use that object directly to retrieve the application's resources. The following code does the same things as the previous version, except it uses the ResourceManager directly. Note that the ResourceManager property's GetObject method returns a generic Object, so the code uses CType to convert the result into an Image before assigning it to the form's BackgroundImage property.

Private Sub Form1_Load() Handles MyBase.Load
    lblGreeting.Text = My.Resources.ResourceManager.GetString("Greeting")
    Me.BackgroundImage = CType(
        My.Resources.ResourceManager.GetObject("Dog"), Image)
End Sub

                                                  
Using Application Resources

Example program UseResources uses similar code to set a label's text and to display an image. Commented-out code does the same thing by using the strongly typed resource properties.

Generally, you should use the automatically generated resource properties in the My.Resources namespace because they are easier to use and they are strongly typed. The ResourceManager class is much more useful when you use other embedded resource files as described in the following section.

Embedded Resources

In addition to storing resources in the application's resource file Resources.resx, you can add other resources files to the application. Open the Project menu and select the Add New Item command. Pick the Resources File template, give the file a meaningful name, and click OK.

After you add a resource file to the project, you can double-click it in Solution Explorer to open it in the resource editor. Then you can add resources to the file exactly as you do for the application's resource file.

At runtime, you can use a ResourceManager object to fetch the resources from the embedded file. The following code loads the Dog image from the file Images.resx and the string Greeting from the file Strings.resx.

It starts by declaring a ResourceManager variable. It then initializes the variable, passing its constructor the resource file's path and the assembly containing the file. The file's path consists of the application's root namespace EmbeddedResources followed by the file's name Images.

The program then uses the ResourceManager object's GetObject method to fetch the Dog image, converts the generic Object returned into an Image, and assigns the result to the form's BackgroundImage property.

Next, the code reinitializes the ResourceManager object so it represents the Strings resource file. It calls the manager's GetString method to get the value of the Greeting resource and saves the result in the lblGreeting control's Text property.

Private Sub Form1_Load() Handles MyBase.Load
      Dim resource_manager As ResourceManager

      ' Get the Dog image from Images.resx.
      resource_manager = New ResourceManager(
            "EmbeddedResources.Images",
            Me.GetType.Assembly)
      Me.BackgroundImage = CType(
            resource_manager.GetObject("Dog"), Image)

      ' Get the Greeting from StringResources.resx.
      resource_manager = New ResourceManager(
            "EmbeddedResources.Strings", Me.GetType.Assembly)
      lblGreeting.Text = resource_manager.GetString("Greeting")
End Sub

                                                  
Embedded Resources

Just as it generates strongly typed properties for application resources, Visual Studio generates similar code for other embedded resource files. You can access these properties by adding the resource file's name after My.Settings and before the resource name. For example, to get the image resource named Dog from the Images resource file, the program would use My.Settings.Images.Dog.

Example program EmbeddedResources, which is available for download on the book's web site, contains code to load embedded resources both with a resource manager and by using the strongly typed resource properties.

Satellite Resources

Embedded resource files enable you to organize data in central locations. For example, you can keep all the images used by the application in a single resource file, and all of the program's forms can fetch the images from that file.

Rather than embedding a resource file in the application, you can load it at runtime. Then if you must make changes to the resources, you can replace the resource file and the program will load the new resources the next time it runs.

To make switching resources easy, you can place them in a resource-only assembly. This satellite assembly contains data for the application but doesn't contain any program logic.

To make a resource-only satellite assembly, start a new Visual Basic project, selecting the Class Library template. Delete the project's initial class. Then, add resources to the project as you would add them to any other project. You can add resources to the project's application resources or to embedded resource files. When you are finished adding resources, compile the project to build a .dll file.

You can then build an executable Windows application that loads resources from the assembly as shown in the following code:

Private Sub Form1_Load() Handles MyBase.Load
      ' Get the resource Assembly.
      Dim satellite_assembly As Assembly
      satellite_assembly = Assembly.LoadFrom("SatelliteResourcesDll.dll")

      ' Create a ResourceManager for the satellite's
      ' main resource file.
      Dim resource_manager As ResourceManager
      resource_manager = New ResourceManager(
            "SatelliteResourcesDll.Resources", satellite_assembly)
      ' Get the string resource from the satellite's
      ' main resource file.
      lblGreeting.Text = resource_manager.GetString("Greeting")

      ' Create a ResourceManager for the satellite's
      ' Images resource file.
      resource_manager = New ResourceManager(
            "SatelliteResourcesDll.Images", satellite_assembly)

      ' Get the form's background image from the satellite's
      ' Images resource file.
      Me.BackgroundImage = CType(resource_manager.GetObject("Dog"), Image)
End Sub

                                                  
Satellite Resources

The program declares an Assembly object and uses the shared Assembly.LoadForm method to load information about the resource-only assembly SatelliteResourcesDll.dll. In this example, the file was copied into the executable program's startup directory so the program does not need to pass a complete path to the LoadFrom method.

Next, the program uses the Assembly object to create a ResourceManager. For the resource file's name, it passes the ResourceManager object's constructor to the assembly's root namespace SatelliteResourcesDll, followed by the name of the resource file within the satellite project. In this case, the first resource is stored in the project's resources, so the file is MyResources.resx. Now the program uses the ResourceManager object's GetString method to fetch the Greeting resource.

The program then creates a new ResourceManager using the same Assembly object, this time using the resource file named ImageResources.resx. It uses the GetObject method to fetch the image named Dog, converts the result into an Image, and saves the result in the form's BackgroundImage property.

Later, if you must change the value of the Greeting string or the Dog image, you can update the satellite project, build a new DLL file, and copy the DLL file into the executable program's startup directory. The next time you run the program, it will load the new resources.

Example projects SatelliteResourcesDll and SatelliteMain, which are both available for download on the book's web site, contain the satellite resource DLL and an executable program that uses it, respectively.

Localization Resources

One of the most important reasons for inventing resource files was to allow localization: supporting different text, images, and other items for different languages and cultures. In Visual Studio .NET, localization is easy.

First, create a form using whatever language you typically use from day to day. For me, that's English as spoken in the United States. Open the form in the form designer and give it whatever controls you need. Set the form's and controls' properties as usual.

Next, set the form's Localizable property to True. Then set the form's Language property to the first language you want to support other than the default language that you have been working with so far. Modify the controls' properties for the new language.

As you modify a form, Visual Studio saves the changes you make to a new resource file attached to the form. If you open Solution Explorer and click the Show All Files button, you can see these resource files below the form's file.

Example program Localized, which is available for download on the book's web site, uses default settings for United States English. It also includes localizations for generic German (as opposed to German as spoken in Switzerland, Germany, Liechtenstein, or some other country). If you expand the form's entry in Solution Explorer, you'll find the files Form1.resx holding the default settings and Form1.de.resx holding the German settings.

At runtime, the application automatically checks the user's computer and selects the best resource file based on the system's regional settings.

Normally, you should let the application pick the appropriate resource file automatically, but you can explicitly select a resource file for testing purposes. To do that, open the Solution Explorer and click the Show All Files button. Find the form's design file (for example, Form1.Designer.vb) and open it.

Give the form an empty constructor that sets the current thread's CurrentCulture and CurrentUICulture properties to the culture that you want to use. See the online help for the CultureInfo class to get a list of possible cultures such as en-US (United States English) or de-DE (German in Germany, as opposed to in Austria, Switzerland, or some other locale). The result should look something like the following code:

Imports System.Threading
Imports System.Globalization

Public Class Form1
      Public Sub New()
            MyBase.New()

            ' Set the culture and UI culture to German.
            Thread.CurrentThread.CurrentCulture = New CultureInfo("de-DE")
            Thread.CurrentThread.CurrentUICulture = New CultureInfo("de-DE")

            'This call is required by the Windows Forms Designer.
            InitializeComponent()
      End Sub
End Class

                                                  
Localization Resources

The rest is automatic. When the form's InitializeComponent method executes, it loads the resources it needs for the culture you selected.

Example program LocalizedUseGerman, which is available for download on the book's web site, uses this code to open the form localized for German even if your system would not normally select that version.

ComponentResourceManager

Like the ResourceManager class, the ComponentResourceManager class provides GetString and GetObject methods for loading resources from a resource file one at a time.

ComponentResourceManager also provides the ApplyResources method, which makes applying resources to an object easier. ApplyResources searches a resource file for items with a particular object name. It then applies any resources it finds to an object's properties.

For example, you could use ApplyResources to search for resources with the object name ExitButton and apply them to the btnExit control. If the resource file contained an item named ExitButton.Text, ApplyResources would apply it to the btnExit control's Text property. If the method found other resources (such as ExitButton.Location and ExitButton.Size), it would apply those properties to the control, too.

When you localize a form, Visual Studio automatically creates entries in the appropriate resource files for the form's controls. If you set the form's Language to German and set the btnYes button's Text property to Ja, Visual Studio adds a string entry named btnYes.Text with value Ja to the form's German resource file.

Visual Studio uses the special name $this to represent the form's properties. For example, if you set the form's Text property, the IDE adds a string resource named $this.Text to the appropriate resource file.

Example program LocalizedPickLanguage uses ComponentResourceManager objects in the following code to load different localized resources while it is running:

' Select English.
Private Sub radEnglish_CheckedChanged() Handles radEnglish.CheckedChanged
      If Not radEnglish.Checked Then Exit Sub
      LoadAllResources(New CultureInfo("en-US"))
End Sub

' Select German.
Private Sub radGerman_CheckedChanged() Handles radGerman.CheckedChanged
      If Not radGerman.Checked Then Exit Sub
      LoadAllResources(New CultureInfo("de-DE"))
End Sub

' Load resources for the form and all controls.
Private Sub LoadAllResources(ByVal culture_info As CultureInfo)
      ' Make a ComponentResourceManager.
      Dim component_resource_manager As New ComponentResourceManager(Me.GetType)

      ' Load the form's resources.
      LoadResources(Me, "$this", component_resource_manager, culture_info)
End Sub

 ' Load appropriate resources for an object and its contained controls.
Private Sub LoadResources(ByVal parent As Control,
 ByVal parent_name As String,
 ByVal component_resource_manager As ComponentResourceManager,
 ByVal culture_info As CultureInfo)
      ' Load the parent's resources.
      component_resource_manager.ApplyResources(
            parent, parent_name, culture_info)
ToolTip1.SetToolTip(parent, component_resource_manager.GetString(
            parent_name & ".ToolTip", culture_info))

      ' Load each contained control's resources.
      For Each ctl As Control In parent.Controls
            LoadResources(ctl, ctl.Name, component_resource_manager, culture_info)
      Next ctl
End Sub

                                                  
ComponentResourceManager

When the user clicks the radEnglish radio button, the code calls subroutine LoadAllResources, passing it a new CultureInfo object representing United States English. Similarly, when the user clicks the radGerman radio button, the code calls LoadAllResources passing it a CultureInfo object representing German.

Subroutine LoadAllResources creates a ComponentResourceManager for the form's type and calls subroutine LoadResources, passing it the form, the special form resource named $this, the component resource manager, and the CultureInfo object.

Subroutine LoadResources loads the resources for an object and all of the controls it contains. First, it uses the component resource manager's ApplyResources to load the object's resources.

Next, it sets the object's tooltip if it has one. Recall that a control's tooltip is actually not a property of the control. Instead it is an extender provider property provided by a ToolTip control. To load the form's tooltip, the code explicitly calls the Tooltip1 control's SetToolTip method, passing to it the object being loaded and the object's name with ".ToolTip" appended.

Finally, the code loops through the object's contained controls and performs calls LoadResources for each. This makes the code recursively call subroutine LoadResources for every control in the application.

Example program LocalizedPickLanguage2 uses the following slightly modified code to accomplish the same tasks. Instead of passing a CultureInfo object into the ApplyResources method, the program sets the current thread's CurrentCulture and CurrentUICulture properties. It then lets the calls to ApplyResources figure out which resource file to use.

' Load resources for the form and all controls.
Private Sub LoadAllResources(ByVal culture_info As CultureInfo)
      ' Set the current culture and UI culture.
      Thread.CurrentThread.CurrentCulture = culture_info
      Thread.CurrentThread.CurrentUICulture = culture_info

      ' Make a ComponentResourceManager.
      Dim component_resource_manager As New ComponentResourceManager(Me.GetType)

      ' Load the form's resources.
      LoadResources(Me, "$this", component_resource_manager)
End Sub

' Load appropriate resources for an object and its contained controls.
Private Sub LoadResources(ByVal parent As Control,
 ByVal parent_name As String,
 ByVal component_resource_manager As ComponentResourceManager)
      ' Load the parent's resources.
      component_resource_manager.ApplyResources(parent, parent_name)
      ToolTip1.SetToolTip(parent,
            component_resource_manager.GetString(parent_name & ".ToolTip"))

      ' Load each contained control's resources.
      For Each ctl As Control In parent.Controls
            LoadResources(ctl, ctl.Name, component_resource_manager)
      Next ctl
End Sub

                                                  
ComponentResourceManager

The program can later check the CurrentCulture and CurrentUICulture properties if it needs to remember which culture it is currently using.

APPLICATION

The Application object represents the running application at a very high level. It provides properties and methods for starting an event loop to process Windows messages, possibly for a form. It also provides methods for controlling and stopping the event loop.

Don't confuse the Application object with the My.Application namespace. The two have somewhat similar purposes but very different features.

The following sections describe the Application object's most useful properties, methods, and events.

Application Properties

The following table describes the Application object's most useful properties.

PROPERTY

PURPOSE

CommonAppDataPath

Returns the path where the program should store application data shared by all users. By default, this path has the form base_pathcompany_nameproduct_nameproduct_version. The base_path is typically C:Documents and SettingsAll UsersApplication Data.

CommonAppDataRegistry

Returns the Registry key where the program should store application data shared by all users. By default, this path has the form HKEY_LOCAL_MACHINESoftwarecompany_nameproduct_nameproduct_version.

CompanyName

Returns the application's company name.

CurrentCulture

Gets or sets the CultureInfo object for this thread.

CurrentInputLanguage

Gets or sets the InputLanguage for this thread.

ExecutablePath

Returns the fully qualified path to the file that started the execution, including the file name.

LocalUserAppDataPath

Returns the path where the program should store data for this local, nonroaming user. By default, this path has the form base_pathcompany_nameproduct_nameproduct_version. The base_path is typically C:Documents and Settingsuser_nameLocal SettingsApplication Data.

MessageLoop

Returns True if the thread has a message loop. If the program begins with a startup form, this loop is created automatically. If it starts with a custom Sub Main, the loop doesn't initially exist and the program must start it by calling Application.Run.

OpenForms

Returns a collection holding references to all of the application's open forms.

ProductName

Returns the application's product name.

ProductVersion

Gets the product version associated with this application.

StartupPath

Returns the fully qualified path to the directory where the program starts.

UserAppDataPath

Returns the path where the program should store data for this user. By default, this path has the form base_pathcompany_nameproduct_nameproduct_version. The base_path is typically C:Documents and Settingsuser_nameApplication Data.

UserAppDataRegistry

Returns the Registry key where the program should store application data for this user. By default, this path has the form HKEY_CURRENT_USERSoftwarecompany_nameproduct_nameproduct_version.

UseWaitCursor

Determines whether this thread's forms display a wait cursor. Set this to True before performing a long operation, and set it to False when the operation is finished.

Example program ListForms, which is available for download on the book's web site, uses the Application.OpenForms collection to list its running forms.

To set the CompanyName, ProductName, and ProductVersion, open Solution Explorer, double-click the My Project entry, and select the Application tab. Then click the Assembly Information button and enter the values on the Assembly Information dialog box. Example program ShowProductInfo displays these three values.

Application Methods

The following table describes the Application object's most useful methods.

METHOD

PURPOSE

AddMessageFilter

Adds a message filter to monitor the event loop's Windows messages. See the following text for an example.

DoEvents

Processes Windows messages that are currently in the message queue. If the thread is performing a long calculation, it would normally prevent the rest of the thread from taking action such as processing these messages. Calling DoEvents lets the user interface catch up with the user's actions. Note that you can often avoid the need for DoEvents if you perform the long task on a separate thread.

Exit

Ends the whole application. This is a rather abrupt halt and any forms that are loaded do not execute their FormClosing or FormClosed event handlers, so be certain that the application has executed any necessary clean-up code before calling Application.Exit.

ExitThread

Ends the current thread. This is a rather abrupt halt, and any forms running on the thread do not execute their FormClosing or FormClosed event handlers.

OnThreadException

Raises the Application object's ThreadException event, passing it an exception. If your application throws an uncaught exception in the IDE, the IDE halts. That makes it hard to test Application.ThreadException event handlers. You can call OnThreadException to invoke the event handler.

RemoveMessageFilter

Removes a message filter.

Run

Runs a message loop for the current thread. If you pass this method a form object, it displays the form and processes its messages until the form closes.

SetSuspendState

Makes the system suspend operation or hibernate. When the system hibernates, it writes its memory contents to disk. When you restart the system, it resumes with its previous desktop and applications running. When the system suspends operation, it enters low-power mode. It can resume more quickly than a hibernated system, but memory contents are not saved, so they will be lost if the computer loses power.

Example program FilterMessages uses the following code to filter messages and ignore left mouse button down messages:

Public Class Form1
      ' Filter out left mouse button down messages.
      Private Class NoLeftDownMessageFilter
            Implements IMessageFilter

            Public Function PreFilterMessage(
             ByRef m As System.Windows.Forms.Message) _
             As Boolean Implements IMessageFilter.PreFilterMessage
                  ' If the message is left mouse down, return True
                  ' to indicate that the message should be ignored.
                  Const WM_LBUTTONDOWN As Long = & H201
                  Return (m.Msg = WM_LBUTTONDOWN)
            End Function
      End Class

      ' Install the message filter.
      Private Sub Form1_Load() Handles MyBase.Load
            Dim no_left_down_message_filter As New NoLeftDownMessageFilter
            Application.AddMessageFilter(no_left_down_message_filter)
      End Sub

      ' Toggle the wait cursor.
      Private Sub Form1_Click() Handles Me.Click
            Application.UseWaitCursor = Not Application.UseWaitCursor
      End Sub
End Class

                                                  
Application Methods

The NoLeftDownMessageFilter class implements the IMessageFilter interface, which specifies the PreFilterMessage function. That function examines a message and returns True if it wants to filter the message out of the form's message queue. In this example, the function returns True if the message is WM_LBUTTONDOWN, indicating that the left button has been pressed.

The form's Load event handler creates a new instance of the filter class and uses the Application object's AddMessageFilter method to install the filter.

The form's Click event handler toggles the state of the Application object's UseWaitCursor property. This displays or hides the wait cursor.

When you left-click the form, the message filter intercepts the left button down message, so the Click event doesn't occur. If you right-click the form, the filter allows all messages through so the Click event occurs and the form displays or hides the wait cursor.

Application Events

The Application object provides a few events that give you information about the application's state. The following table describes these events.

EVENT

PURPOSE

ApplicationExit

Occurs when the application is about to shut down.

Idle

Occurs when the application finishes executing some code and is about to enter an idle state to wait for events.

ThreadException

Occurs when the application throws an unhandled exception. See the following code for an example.

ThreadExit

Occurs when a thread is about to exit.

When you end an application by unloading its form, the program receives the events FormClosing, FormClosed, ThreadExit, and ApplicationExit, in that order.

If you end the application by calling the Application object's Exit method, the program only receives the ThreadExit and ApplicationExit events. If more than one thread is running, they each receive ThreadExit events, and then they each receive ApplicationExit events.

Example program CatchThreadException uses the following code to catch all exceptions thrown by the application:

Imports System.IO

Public Class Form1
      ' Install the ThreadException event handler.
      Private Sub Form1_Load() Handles Me.Load
            AddHandler Application.ThreadException,AddressOf Me.app_ThreadException
      End Sub

      ' Catch a ThreadException event.
      Private Sub app_ThreadException(ByVal sender As Object,
       ByVal e As System.Threading.ThreadExceptionEventArgs)
            MessageBox.Show("Caught unhandled exception:" &
                  vbCrLf & vbCrLf & e.Exception.Message)
      End Sub

      ' Throw an InvalidDataException.
      Private Sub btnThrow_Click() Handles btnThrow.Click
            Throw New InvalidDataException("Bad data! Bad!")
      End Sub

      ' Call the OnThreadException method.
      Private Sub btnOnThreadException_Click() Handles btnOnThreadException.Click
            Application.OnThreadException(New InvalidDataException("Bad data! Bad!"))
      End Sub
End Class

                                                  
Application Events

When it starts, the form's Load event handler uses the Application object's AddHandler method to add the app_ThreadException subroutine as a handler for the Application's ThreadException event.

The app_ThreadException subroutine simply displays an error message describing the exception that it caught. A real application would take different actions such as logging the error and a stack trace, restarting the application, and so forth.

When the user clicks the Throw button, the program throws an exception. If you are running the program in the development environment, Visual Studio halts at the Throw statement and tells you that it encountered an unhandled exception. If you run the compiled executable outside of the development environment, the application's ThreadException event occurs.The app_ThreadException routine catches the event and displays its message.

When the user clicks the OnThreadException button, the program calls the Application object's OnThreadException method, passing it an exception object. Whether you are running in the development environment or running the compiled executable, the application's ThreadException event occurs and the app_ThreadException routine catches it. You can use OnThreadException to throw the exception without the IDE's interference, so you can catch it and debug the exception handling code.

SUMMARY

Visual Studio provides many ways to store and use application configuration and resource information. Some of the most useful of these include environment variables, the Registry, configuration files, and resource files. The My namespace and the Application object make working with some of these easier.

Store configuration information that changes relatively often in configuration files. Store resources that determine the application's appearance in resource files. If you will distribute the application in multiple languages, use localized resource files to manage the different languages. If necessary, you can change the data stored in configuration and resource files and redistribute them to your users without rebuilding the entire application.

You can store small pieces of information between program runs in the System Registry. Use databases, XML files, and other files to store larger amounts of data.

Using all of these techniques, you can make your application easily configurable. You can satisfy the needs of different kinds of users and customize the application without recompiling it.

This chapter explained ways that a program can save configuration and resource information using tools such as the Registry, environment variables, and resource files. Generally, these kinds of data are of relatively limited size. If an application needs to store larger amounts of data, it generally uses a database or file.

Chapter 37, "Streams," explains classes that a Visual Basic application can use to work with stream data in general, and files in particular. Using streams attached to files, a program can read and write large amounts of data without cluttering up the Registry, environment variables, or resource files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.117.231