Chapter 5. .NET Assemblies

The mere mention of the word assembly takes me back to my days as a high school freshman. The assembly was actually held in the school gym, with 2,000 screaming adolescents filling the bleachers around the basketball court. Since this was a school function, I naturally thought of an experience packed with fresh educational opportunities. School, education—the words just seem to go together. But then came the marching band, and the football players, and the cheerleaders, and the school mascot (a horse). For the next 30 minutes, the principal whipped the students into a controlled frenzy, attempting to prove the institution’s place as the number-one school in the city. I still don’t know what area we were supposed to be number one in, but it was all very exciting.

.NET assemblies are not that exciting. In fact, they’re just files, EXE and DLL files, and without you to activate them, they just sit there taking up disk space. And as they are not doing anything else, let’s take a moment to examine what they are and what they contain.

What Is an Assembly?

As I already mentioned in Chapter 1, an assembly is a “unit of deployment,” which in most cases is just a file. An assembly is a repository for compiled .NET application code; any code you write will eventually be stored in some EXE file (if it is an application) or DLL file (for code libraries or extensions to an application). Everything that .NET needs to know to load and run your application is stored in the assembly.

Assemblies are either private or public. Private assemblies are designed for use in a single application only. If there aren’t any DLLs, an EXE assembly is the application. Private assemblies appear in their own directory, the installation directory of the application or library. You can run two different private assemblies at the same time, and they won’t bother each other. This is true even if each assembly uses the same combination of namespace and class names for its coded elements. If two application assemblies each implement a class named WindowsApplication1.Class1, they will not interfere with each other when running; they are private, and private means private.

Public assemblies are designed for shared use among multiple .NET applications. Public assemblies differ from private assemblies in two major ways:

  • Public assemblies always have a strong name, an encrypted digital signature that is attached to an assembly to guarantee that it came from its named vendor or source. (Private assemblies can also include a strong name, but they don’t have to.) The strong name is built from the assembly’s name, version number, culture information, a “public key,” and a digital signature generated from the assembly file that contains the manifest (described later). The .NET Framework includes a Strong Name generation tool (sn.exe) that assists in this process, and Visual Studio includes options that let you add a digital signature during the compilation process. (It’s on the Signing tab of the project’s properties.)

    The strong name of an assembly should be (and better be) unique; if two assemblies share a common strong name, they are copies of the same assembly.

  • Public assemblies are stored in the Global Assembly Cache (GAC). Although you can put a copy of your shared component in your application’s install directory, it will only truly be shared once it reaches the GAC directory. The GAC lives in a directory named assembly within the computer’s Windows directory. (On my system, it’s in c:windowsassembly.) Once a .NET assembly has a strong name applied, you can add it to the GAC by either dragging the file into the assembly directory or using the Global Assembly Cache Tool (gacutil.exe). Don’t worry about your file being lonely if it’s not communing with your other installed files. On my freshly installed copy of .NET, I found nearly 400 files already in the GAC directory, including all the DLLs for the Framework Class Libraries (FCLs).

.NET lets you install multiple versions of an assembly on a system and use them at the same time (a process called versioning). This applies both to applications (EXE) and libraries (DLL), and to private assemblies and shared assemblies in the GAC. Don’t believe me? Open up the GAC’s assembly folder, set the Explorer folder to a Details view, and then sort by Assembly Name. If you scroll down, you’ll see the same file show up multiple times. Figure 5-1 shows a part of the cache. Two copies of “Microsoft.VisualStudio.Windows.Forms” are listed (from the Microsoft.VisualStudio.Windows.Forms.dll file), one with a version number of 2.0 and one listing version 9.0.

The GAC has this duplication under control

Figure 5-1. The GAC has this duplication under control

Although there is usually a one-to-one relationship between files and assemblies, there may be cases when an assembly is made up of multiple files. For instance, an application might include an external graphics file in its assembly view. .NET keeps a close watch on these files. If any of the files are modified, deleted, or otherwise maimed, you will hear about it. For the purposes of discussion, the rest of this chapter considers only single-file assemblies.

What’s Inside an Assembly?

An assembly’s EXE or DLL file is a standard “Portable Execution” (PE) file, the same file format used for non-.NET executables and code libraries (pretty much any Windows EXE or DLL file). What makes .NET PE files different is all the extra stuff found inside. As a general word, assembly indicates a gathering together of various parts into a single unit. In a .NET assembly, these “various parts” are specifically designed for use with .NET.

A .NET PE file contains three main parts:

A PE header

Required of all PE files, this section identifies the locations of the other sections of the file.

The MSIL code section

The actual code associated with the assembly is stored as semicompiled Microsoft Intermediate Language (MSIL) code. Unfortunately, the Intel or AMD chip in your computer is apparently too brainless to process MSIL code directly (what were they thinking?), so the .NET Framework includes a just-in-time (JIT) compiler that can convert MSIL to native x86 code at a moment’s notice.

The Metadata section

All of the extra detail that .NET needs to rummage through to know about your assembly appears in this essential section. Some of these items, when taken together, make up the assembly’s manifest, a type of document that completely describes the assembly to the world. In the following list of metadata elements, I’ve noted which items appear in the manifest:

The name of the assembly

(Part of the manifest.) This is defined on the Application tab of the project’s properties.

The version number of the assembly

(Part of the manifest.) That’s the four-part version number, as in 1.2.3.4. You’ve probably been wondering all day how you could set this number in your own projects. Your patience will be rewarded in this chapter’s "Project" section, where I will demonstrate not just one, but two ways to set the assembly version number.

Strong name content

(Part of the manifest.) This includes the publisher’s public key.

Culture and language settings

(Part of the manifest.) This is especially useful when you need to create language-specific resource files.

Assembly file listing

(Part of the manifest.) Single-file assemblies will show only the EXE or DLL filename, but some assemblies may include several files in this section. All files in an assembly must appear within the same directory, or in a directory subordinate to the assembly file that contains the manifest.

Exported type information

(Part of the manifest.) Some assemblies “export” some of their types for use outside the application. The details of those types appear here.

References

(Part of the manifest, but in multifile assemblies, each file will contain its own list of references.) The metadata includes a listing of all external assemblies referenced by your application, whether they are private or appear in the GAC. This list indicates which specific version, culture, and platform-target of the external assembly your assembly expects.

Internal type information

(Not part of the manifest.) All types crafted in your assembly are fully described within the metadata. Also, any additional metadata you added to your types through Visual Basic’s attribute feature appear here.

In multifile assemblies, the manifest-specific elements appear only in the “main” file of the assembly.

The manifest is a subset of the metadata within your assembly. I hate to say that it’s the most important part of the metadata—but it is. The manifest is the public expression of your assembly, and the only way that .NET knows whether it is legit. It’s sort of like the “Nutrition Facts” label put on American food packaging (see Figure 5-2).

Is that really good for me?

Figure 5-2. Is that really good for me?

When you look at the food label, you know what the food package contains—although no one really knows what riboflavin is. When you look at the manifest for an assembly, you know at a glance what the assembly contains, and what requirements it has before it can be loaded and run.

Even before .NET burst onto the scene, executables and libraries already contained some “metadata,” such as the version number of the file. But this data wasn’t used to manage access between software components, nor was it organized in a generic and extensible way. The metadata in .NET embodies all of these attributes.

The presence of both the MSIL and metadata in each assembly makes these files very readable and understandable. With the right tools, even I seem to understand them. And if I can, anyone can, which leads to a big problem. Companies invest a lot of time and money in their software development efforts, and they don’t want any rinky-dink two-bit startup reverse-engineering their code and getting all their algorithmic secrets. To prevent this casual reading of any .NET application, Microsoft and other third parties include obfuscators, software programs that scramble the contents of an assembly just enough so that it’s hard for humans to understand, but not for the .NET Framework. I’ll talk more about obfuscation in Chapter 22.

Reflection

It may be a bad thing for people to access the content of an assembly, but it’s great when the code in an assembly can access itself. .NET includes a feature called reflection that lets you examine the contents of an assembly. You generally use this feature to access metadata in your own assembly, but it also works with any available assembly. Most reflection-related features appear in the System.Reflection namespace.

Through reflection, you can extract pretty much anything stored in the metadata of an assembly, including details on all types, their members, and even the parameters included with function members. This is why obfuscation is so important to vendors; between the compiled MSIL and the metadata, you can virtually regenerate the entire source code for an application from just its executable. The source code would be in MSIL, but it wouldn’t be that tough for someone to massage much of it back into Visual Basic or C#.

Assemblies and Applications

.NET applications (EXE files) are an instance of an assembly. But a single application can include multiple assemblies; it fact, it almost always does. I wrote a little program that uses reflection to list all assemblies actively being used by the program itself. I gave the program the default name of WindowsApplication1. When I ran the program against itself, it generated the following list:

mscorlib
Microsoft.VisualStudio.HostingProcess.Utilities
System.Windows.Forms
System
System.Drawing
Microsoft.VisualStudio.HostingProcess.Utilities.Sync
Microsoft.VisualStudio.Debugger.Runtime
vshost
System.Data
System.Deployment
System.Xml
System.Core
System.Xml.Linq
System.Data.DataSetExtensions
Microsoft.VisualBasic
WindowsApplication1
System.Runtime.Remoting

Wow! Seventeen assemblies, including WindowsApplication1, the main program. Most of the assemblies are framework-supplied DLLs. For Microsoft.VisualBasic, it’s the Microsoft.VisualBasic.dll assembly; for System, it’s the System.dll assembly. All of the assemblies (except the main program assembly) are shared libraries from the GAC. The application can also support private assemblies loaded from local DLL files.

The .NET Framework automatically loaded these assemblies for me when WindowsApplication1 started up; it figured out which ones needed to be loaded by looking in the manifest for WindowsApplication1. When the framework loaded each assembly, it checked to see whether those assemblies in turn needed additional assemblies loaded, and so on. Pretty soon, your once-simple application becomes a dumping ground for assemblies all over the GAC. But that’s OK, since the purpose of .NET is to manage it all.

The My Namespace and Assemblies

The .NET Framework, with its thousands of classes, contains a lot of packaged logic that I can use in my own programs. But I don’t have all of the many assemblies and their classes memorized (yet), and it takes time to wander around the FCL documentation. With so many classes available, I sometimes shudder when I think of the effort it will take me to find just the right class or feature I need to accomplish some development task.

Fortunately, I’m not the only one who thinks this way; Microsoft agrees with me. Historically, Visual Basic programmers were sheltered from the complexities of Windows application development. Not that they needed to be; we all know that Visual Basic developers are generally a cut above the rest. But there was “the Visual Basic motto” to contend with: Make Windows Development Fast and Easy. And calling some esoteric method deep within the bowels of the System namespace just to get a minor piece of data is neither easy nor fast.

To bring back some semblance of the pleasant experience previously available in Visual Basic development, Microsoft introduced the My pretend namespace in its 2005 release of the language. The My pretend namespace collects a lot of useful features from all around the FCL, and organizes them in a much smaller hierarchy for simple and direct access. I briefly mentioned My in Chapter 1, but now is a good time to take a closer look at what it does.

The My pretend namespace looks a lot like other namespaces, such as System, System.Reflection, and System.Windows.Forms. But it’s not really a namespace—it’s pretend! For one thing, you can’t use the Imports keyword to create a shortcut to branches within its hierarchy. Also, some sections of the hierarchy are dynamic; they change as your project changes. Table 5-1 lists the major nodes of the hierarchy.

Table 5-1. Major nodes in the My namespace hierarchy

Branch

Available features

My.Application

Provides information about the current application, including culture settings and the deployment method.

My.Application.Info

Gives further details about the application and its assembly, including the name and version.

My.Application.Log

Allows you to generate trace and logging output to registered logging destinations. Used only with client applications.

My.Computer

Provides access to general resources located on the local computer.

My.Computer.Audio

Plays named and system sounds through the computer’s speakers.

My.Computer.Clipboard

Retrieves data from the system clipboard, and lets you add your own data to the clipboard in a variety of predefined and custom formats.

My.Computer.Clock

Gets the current system date and time dished up in a variety of ways.

My.Computer.FileSystem

Provides tools to examine and manipulate files and directories on local or networked filesystems.

My.Computer.FileSystem. SpecialDirectories

References special Windows folders such as Documents, Desktop, and Temp.

My.Computer.Info

Provides information about the installed operating system and other local system resources.

My.Computer.Keyboard

Exposes the current state of the keyboard and its keys.

My.Computer.Mouse

Makes available a few properties of the local computer’s mouse.

My.Computer.Network

Reports on network availability, and provides features to interact with that network.

My.Computer.Ports

Lets you interact with the system’s serial ports.

My.Computer.Registry

Reads and writes keys and values in the registry.

My.Forms

Presents a dynamic collection of all forms defined in the application. This node is available only in Windows Forms applications.

My.Log

Allows you to generate trace and logging output to registered logging destinations. Used only with ASP.NET applications.

My.Request

This object is similar to the older Active Server Pages Request object. It is available only in ASP.NET applications.

My.Resources

Provides dynamic access to application-specific or locale-specific resources included with the application.

My.Response

This object is similar to the older Active Server Pages Response object. It is available only in ASP.NET applications.

My.Settings

Provides dynamic access to the application settings system.

My.User

Identifies the current Windows user, including authentication information.

My.WebServices

Presents a collection of available web services for use in the application. This node is not available in ASP.NET applications.

The My namespace includes a lot of features you will use regularly, including access to the version number of the application. Instead of typing System.Reflection.whatever to get to the version number’s “major” component, you can now just type:

My.Application.Info.Version.Major

Need a list of assemblies, but you’re too lazy to type the word Reflection? Try:

My.Application.Info.LoadedAssemblies

Need to know the time right now in England?

My.Computer.Clock.GmtTime

Can you communicate over the local area network?

My.Computer.Network.IsAvailable

Who is running this computer anyway?

My.User.Name

There isn’t much in the My namespace that you can’t already do with standard FCLs. There are even a few parts of My that are repeats of features already included in the Visual Basic language, although with some enhancements. For instance, Visual Basic includes a Kill command that lets you delete files. The My.Computer.FileSystem.DeleteFile method also removes files, but it offers additional options, including one that lets you send the file to the Recycle Bin instead of just losing it forever.

Directives and Assemblies

Directives are Visual Basic statements—but then again, they’re not. The two key directives—#Const and #If—provide instructions to the compiler on how to handle a block of Visual Basic source code. (A third directive, #Region, helps to visually present source code within Visual Studio, but it has no impact on the compiler or the final compiled application.) By using directives, you can tell the compiler to include or exclude specific chunks of source code from the final project. So, they aren’t really Visual Basic source code statements, but they are available only in Visual Basic.

Why would you want to include or exclude code in an application? Well, I can think of several good reasons, some of which involve the CIA and former Federal Reserve chairman Alan Greenspan. But the most common use is when you want to produce two different versions of your application, based on some condition. For example, you may sell an “express” version and a “professional” version of a product. Much of the code is identical for the two versions, but the professional version would include features not available in the express version. Also, the express version may include a simplified presentation for a feature that has a more complex usage in the professional edition.

Some software products fulfill this need by using standard Visual Basic conditions.

If (professionalVersion = True) Then
   ShowWhizBangFeatures(  )
Else
   ShowLaughableFeatures(  )
End If

This, of course, works just fine. But the express application still contains all the enhanced features. Since it can’t access any of that code, why even include it on the installation CD? If you use directives, you can mark down that problem as solved. Directives use conditional expressions, much like the professionalVersion = True condition in the preceding block of code. But they are defined with the #Const statement, and are called compiler constants.

#Const fullVersion = True

This statement defines a Boolean compiler constant. The constant can be used only with directives; if you try to use fullVersion in a standard Visual Basic statement, the compiler will complain. But it will work just fine in the #If directive.

#If (fullVersion = True) Then
   ShowWhizBangFeatures(  )
#Else
   ShowLaughableFeatures(  )
#End If

This code looks a lot like the previous code block, but with the added # signs. It looks the same but it’s not. With the plain If statement, the following code gets compiled into the final application:

If (professionalVersion = True) Then
   ShowWhizBangFeatures(  )
Else
   ShowLaughableFeatures(  )
End If

Yeah, the whole block of code. But with the directives, what gets included in the compiled application depends on the value of fullVersion. If fullVersion is True, this gets compiled into the compiled application:

ShowWhizBangFeatures(  )

The other four lines are gone; they’ve vanished . . . into thin air, as though they never existed. But in this case, it’s a good thing. The goal was to have a version of the assembly completely devoid of the undesired code, and that’s what happened.

To set the fullVersion compiler constant to generate the full version, you include this line at the top of each source code file that includes conditional #If code blocks:

#Const fullVersion = True

When you’re ready to generate the “express” version, just change each of these lines to their False counterpart:

#Const fullVersion = False

Somehow, changing this line in every source code file that needs it seems like a lot of work, and it is. And what happens if I forget to set one of them to the right version? No good, I can tell you.

To keep Visual Basic developers from running down the halls screaming more than they normally would, Visual Studio provides a few different ways to set compiler constants once, and have them apply to every part of the application. The most common way to do this is through the project properties’ Compile panel (see Figure 5-3). Click on the Advanced Compile Options button, and then add your global compiler constants to the “Custom constants” field.

This is a whole lot easier than all that typing

Figure 5-3. This is a whole lot easier than all that typing

Now, by adding either fullVersion = True or fullVersion = False to this field, you can build different versions of the application. The Visual Basic compiler also provides features that let you set up different compile scripts for your project. I won’t talk about it in this book, but you can read up on the MSBuild tool in the Visual Studio documentation if you need this level of control.

Besides Booleans, compiler constants can be numbers and strings. The Visual Studio environment also defines some compiler constants for you. The DEBUG and TRACE constants are True or False based on the “Define DEBUG constant” and “Define TRACE constant” checkboxes that appear in Figure 5-3. The VBC_VER constant identifies the version of the Visual Basic compiler being used; it is set to 9.0 in Visual Basic 2008.

Summary

Assemblies aren’t just souped-up EXE or DLL files; they contain gobs of metadata, including the manifest, that make .NET applications self-describing. The compiler uses this information to correctly configure and process the managed MSIL code in each assembly.

Although not actually parts of an assembly, this chapter also discussed the My namespace and directives, two Visual Basic features that impact what gets included in your assembly.

Project

This chapter’s project officially kicks off the coding of the Library Project (muted applause). We’ll start off with something simple: building the About form that provides basic information about the application, including its version number.

PROJECT ACCESS

Load the Chapter 5 (Before) Code project, either through the New Project templates or by accessing the project directly from the installation directory. To see the code in its final form, load Chapter 5 (After) Code instead.

Our goal is a pleasant form that conveys basic information about the program, a form that looks something like Figure 5-4.

Everything you wanted to know about the program

Figure 5-4. Everything you wanted to know about the program

Like any Visual Basic application for Windows, the creation of this form involves two steps: (1) adding controls to the form; and (2) writing the related code.

Adding Controls

If there is one area where Visual Basic excels, it is form creation. Programs can be created by the simple dragging and dropping of prebuilt controls onto the surface of a prebuilt form. It’s all done from within the comfort and convenience of the Visual Studio Integrated Development Environment (IDE), as shown in Figure 5-5.

The Visual Studio environment

Figure 5-5. The Visual Studio environment

The displayed environment includes four key areas, which I’ve labeled with letters in Figure 5-5:

A. The toolbox

This listing of controls includes not only display controls, but also controls that expose no specific user interface, such as the Timer. (If you don’t see the toolbox, select the View → Toolbox menu command.) To add a control to a form, double-click the control in the toolbox, drag it from the toolbox to the form, or draw the control on the form after first selecting it from the toolbox.

B. The form surface

Place any control that exposes a user interface here. The form is WYSIWYG, so you can see the final result as you design the form.

C. The Solution Explorer

All files related to your project appear here. For the current project, you will see only the My Project entry and an entry for the form, Form1.vb. There are actually more files. If you click the second button from the left at the top of the Solution Explorer, it will show you additional files, most of which are managed by Visual Studio on your behalf.

D. The Properties panel

When you select a control on your form surface, or the form surface itself, or an item in the Solution Explorer, the properties of the selected item appear in this area. You can alter the settings of many properties by typing in the new setting. Some properties include special tools to assist you in setting the property value.

If you haven’t done so already, open the form Form1.vb in design view by double-clicking it in the Solution Explorer. We’ll add eight text labels, three shape and line elements, two web-style hyperlinks, a command button, and a picture to the form’s surface. I’ve already added the picture to the form for you, with an image of some books, naming it SideImage.

Set up the form by adjusting the following properties from their defaults. Click on the form surface, and then modify these property values using the Properties panel.

Property

Setting

(Name)

AboutProgram

ControlBox

False

FormBorderStyle

FixedDialog

Size

440, 311

StartPosition

CenterScreen

Text

About the Library Project

Next, add the eight basic text labels to the form’s surface using the Label control. You’ll find this control in the toolbox. As you add each Label control, use the following list of settings to set the properties for each label. The included text matches my situation, but feel free to modify the content as needed.

Label name

Property settings

ProgramName

(Name): ProgramName

AutoSize: True

Font/Bold: True

Location: 136, 16

Text: The Library Project

ProgramVersion

(Name): ProgramVersion

AutoSize: True

Location: 136, 32

Text: Version X.Y Revision Z

LicenseInfo

(Name): LicenseInfo

AutoSize: False

Location: 136, 48

Size: 280, 32

Text: Unlicensed

DevelopedBy

(Name): DevelopedBy

AutoSize: True

Location: 136, 88

Text: Developed By

DeveloperName

(Name): DeveloperName

AutoSize: True

Location: 160, 112

Text: Tim Patrick

DeveloperBook

(Name): DeveloperBook

AutoSize: True

Location: 160, 128

Text: Programming Visual Basic 2008

DeveloperProject

(Name): DeveloperProject

AutoSize: True

Location: 160, 144

Text: In-book Project

CompanyCopyright

(Name): CompanyCopyright

AutoSize: True

Location: 136, 208

Text: Copyright (c) 2008 by Tim Patrick.

Let’s add some lines and colored sections to the form. Visual Basic 6.0 included distinct shape controls for lines, rectangles, and ellipses that you could apply directly to the form surface. .NET no longer includes these items; you have to add them by hand using source-code-specified drawing commands.[3] But we can simulate lines and rectangles using the standard Label control, sans the text.

Label name

Property settings

VersionDivider

(Name): VersionDivider

AutoSize: False

BackColor: Black

Location: 136, 80

Size: 280,1

Text: [Don't add any text]

BackgroundSide

(Name): BackgroundSide

AutoSize: False

BackColor: White

Location: 0, 0

Size: 120, 296

Text: [Don't add any text]

BackgroundDivider

(Name): BackgroundDivider

AutoSize: False

BackColor: Black

Location: 120, 0

Size: 1, 296

Text: [Don't add any text]

If the BackgroundSide label obscures the graphic, right-click on the label and select Send To Back from the shortcut menu that appears.

The LinkLabel control is similar to the more basic Label control, but you can include “links” in the text, clickable sections that are similar to the links on a web page. We’ll use these to display the web site and email address. Add two LinkLabel controls to the form and use the following settings to configure each control’s properties.

LinkLabel name

Property settings

CompanyWeb

(Name): CompanyWeb

AutoSize: True

LinkBehavior: HoverUnderline

Location: 160, 160

Text: http://www.timaki.com

CompanyEmail

(Name): CompanyEmail

AutoSize: True

LinkBehavior: HoverUnderline

Location: 160, 176

Text:

The final control to add is a button that lets the user close the form. Add a Button control to the form with the following properties.

Button name

Property settings

ActClose

(Name): ActClose

DialogResult: Cancel

Location: 344, 240

Size: 80, 24

Text: Close

Forms can be configured so that a press of the Esc key triggers a Button control on the form, as though the user was clicking on the button instead of pressing the Esc key. To do this, click on the form surface, and then set its CancelButton property to ActClose. We had to delay this step until the button was actually added to the form; the CancelButton property would not have allowed a setting for a nonexistent button.

Well, the form should look pretty good by now. The last thing I like to do is to set up the tab order, the order in which the user accesses each field on the form when pressing the Tab key on the keyboard. To edit the tab order, select the form surface and then select the View → Tab Order menu command. Each control on the form that can be given a tab order value will suddenly have a tab order number next to it. Click on each number or control in order until you get the arrangement you want. (See Figure 5-6 to view how I ordered the controls.) Finally, select the View → Tab Order menu command again, or press the Esc key, to leave the tab ordering process.

Nice and orderly

Figure 5-6. Nice and orderly

You can also set the tab order for each control by modifying its TabIndex property using a zero-based numbering system. However, it’s usually faster to set these values by clicking on each control in order.

Adding the Code to the Form

Now it’s time to add some real Visual Basic code. Not that we haven’t added any until now. Everything we did on the form, although we didn’t see it happen, was converted into Visual Basic source code. Let’s take a quick look. In the Solution Explorer, click on the Show All Files button, the second button from the left. When all the files appear, click on the “plus sign” next to Form1.vb, and finally, double-click Form1.Designer.vb (see Figure 5-7).

Accessing the hidden, secret, forbidden code—yeah, it’s out there

Figure 5-7. Accessing the hidden, secret, forbidden code—yeah, it’s out there

Since it’s more than 200 lines of source code bliss, I won’t be printing it here. But look it over; it’s all pretty interesting. As you dragged-and-dropped controls on the form and modified its properties, Visual Studio edited this file on your behalf. It’s part of your form’s class (all forms are classes that derive from System.Windows.Forms.Form). You can tell by the Partial keyword at the top.

Partial Public Class AboutProgram
   Inherits System.Windows.Forms.Form

Most of the action happens in the InitializeComponent procedure. When you are finished looking it all over, close up the designer code and return to the form surface. To make our form a real and interesting form, we need it to do three things:

  • Show the actual version number of the application. This should be determined and displayed right when the form first appears.

  • Jump to the appropriate web site or email recipient when clicking on the link labels. These events get processed in response to a user action.

  • Close the form when the user clicks the Close button. This is also a user-driven event.

Let’s start with the easy one, closing the form. I’m sure you remember about events from Chapter 1. Events are blocks of code that are processed in response to something happening, most often a user action such as a mouse click. All of the actions we want to perform on this form will be in response to a triggered event (lucky us). The easiest way to get to the “default” event for a control is to double-click the control. Try it now; double-click the Close button. When you do, the IDE opens the source code view associated with the form, and adds an empty event handler (the ActClose_Click subroutine).

Public Class AboutProgram
   Private Sub ActClose_Click(ByVal sender As System.Object, _
         ByVal e As System.EventArgs) Handles ActClose.Click

   End Sub
End Class

Every forms-based event (and in fact, most other types of events) in .NET has pretty much the same arguments: (1) a sender argument that indicates which object triggered this event; and (2) the e argument, which allows sender to supply any additional information that may be useful in the event. In this case, the sender argument will be a reference to the ActClose button, since that’s the object that will generate the Click event. A button’s Click event doesn’t have any more useful information available, so e is the default object type, System.EventArgs, which is pretty much just a placeholder, and the object from which all of the more interesting e argument types derive.

The name of this event handler is ActClose_Click, but if you want to change it to FredAndWilma, that’s fine; it won’t mess up anything. But you must keep the Handles ActClose.Click clause intact. This is the part that links the event handler to the actual event.

The code to close the form is extremely simple. Enter it now, either by using the first code snippet for this chapter or by typing it directly.

INSERT SNIPPET

Insert Chapter 5, Snippet Item 1.

' ----- Close the form.
Me.Close(  )

This statement says, “I’m the AboutProgram form/object, and I command myself to close.” If you run the program right now (press the F5 key), you close the form by clicking on the Close button. Since the AboutProgram form was the only form in the application, closing it automatically ended the entire application, no questions asked.

OK, back up to the second item, the web-style links. You could go back to the form surface and double-click on each link label to create an event handler for each label’s default event (in this case, the LinkClicked event). But you can also add the event handler subroutines right in the editor, either by typing the code yourself (which is no fun) or by using the two drop-down lists just above the editor window (see Figure 5-8).

The Class Name and Method Name fields

Figure 5-8. The Class Name and Method Name fields

The Class Name list appears on the left side. Selecting an entry from this list updates the righthand list, the Method Name list. To add an event handler template for the CompanyWeb’s LinkClicked event, first select CompanyWeb from the Class Name list, and then select LinkClicked from the Method Name list. The following code block appears in the code window:

Private Sub CompanyWeb_LinkClicked(ByVal sender As Object, _
      ByVal e As System.Windows.Forms. _
      LinkLabelLinkClickedEventArgs) _
      Handles CompanyWeb.LinkClicked

End Sub

This template’s argument list is a little more interesting, since its e argument is an object of type System.Windows.Forms.LinkLabelLinkClickedEventArgs. The LinkLabel control allows you to have multiple web-style links in a single control, interspersed among regular text. The e argument has a Link property that tells you which of the links in the control was clicked by the user. Since our labels have only a single link, we won’t bother to check it. We’ll just show the web page immediately anytime the link is clicked.

INSERT SNIPPET

Insert Chapter 5, Snippet Item 2.

' ----- Show the company web page.
Process.Start("http://www.timaki.com")

The Process object is part of the System.Diagnostics namespace, and Start is one of its shared members that lets you start up external applications and resources. You pass it any valid URL and it will run using the user’s default browser or application for that URL. Let’s try it again with the CompanyEmail’s LinkClicked event. Add in the template any way you choose and then type or insert the code that starts a new message to an email address.

INSERT SNIPPET

Insert Chapter 5, Snippet Item 3.

' ----- Send email to the company.
Process.Start("mailto:[email protected]")

The last event to design is one of the first events called in the lifetime of the form: the Load event. It’s called just before the form appears on the screen. Double-clicking on the surface of the form creates an event handler template for the Load event. If you prefer to use the Class Name and Method Name drop-down lists instead, select (AboutProgram Events) from the Class Name list before using the Method Name list.

Private Sub AboutProgram_Load(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles Me.Load

End Sub

Let’s add code to this event handler that displays the correct version number, using the version information found in My.Application.Info.Version, an instance of the System.Version class.

INSERT SNIPPET

Insert Chapter 5, Snippet Item 4.

' ----- Update the version number.
With My.Application.Info.Version
   ProgramVersion.Text = "Version " & .Major & "." & _
      .Minor & " Revision " & .Revision
End With

This code uses a With statement to reduce the amount of typing needed in the main assignment statement. Inside the With...End With statement, you aren’t required to retype the object name that appears just after the With keyword—in this case, My.Application.Info.Version. You can just refer to that object’s members by typing a dot (.) followed by the name of the member. You could forgo the With statement and type the full object name each time you wanted to use one of the version values, but this way keeps the code cleaner and less overwhelming.

Setting the Version Number

If you run the program, it will display the currently defined version number, “1.0 Revision 0,” as shown in Figure 5-9.

The version number from the AboutProgram form

Figure 5-9. The version number from the AboutProgram form

My question—and I hope I can answer it before the paragraph is finished—is, “Where is that version number defined, and how can it be changed?” It turns out that I do know the answer: the version values are stored as metadata within the assembly. Visual Studio includes a form that lets you modify the basic informational metadata stored in the assembly. To access the form, display the project’s properties (double-click on My Project in the Solution Explorer), select the Application tab, and then click on the Assembly Information button (see Figure 5-10).

The Assembly Information form, filled out with some relevant values

Figure 5-10. The Assembly Information form, filled out with some relevant values

Our AboutProgram form displays the assembly’s version number, which is set using the four text fields next to the Assembly Version label. Those four fields represent the Major, Minor, Build, and Revision numbers of the assembly. Go ahead, set them to some other values, click OK, and run the program again.

Although this form is convenient, it’s just another example of Visual Studio writing some of your project’s code on your behalf. Every field on this form gets saved in a source code file included with your project. To view it, make sure you have the Show All Files button still selected in the Solution Explorer. Expand the My Project item using its “plus sign,” and then double-click on the AssemblyInfo.vb item. This file defines several assembly-specific attributes (which we’ll explore in Chapter 18), including the following informational entries:

<Assembly: AssemblyTitle("The Library Project")>
<Assembly: AssemblyDescription( _
   "ACME Library Database System")>
<Assembly: AssemblyCompany("ACME")>
<Assembly: AssemblyProduct("Library")>
<Assembly: AssemblyCopyright( _
   "Copyright © 2008 by Tim Patrick")>
<Assembly: AssemblyTrademark("")>
<Assembly: AssemblyVersion("1.0.0.0")>

As you can see, this file has been updated with the values I typed into the Assembly Information form. Thank you Visual Studio! You see the AssemblyVersion attribute defined here. If you modify these values, the changes will be reflected in the Assembly Information form, and also in your running application and final compiled assembly.

The last thing we will do for now to the AboutProgram form is to give it a meaningful filename. Currently, it is named Form1.vb, but AboutProgram.vb would be much more descriptive. To change the name, select Form1.vb in the Solution Explorer, and modify the File Name property to “AboutProgram.vb” in the Properties panel. If you still have all the files showing, you will see Visual Studio also update the names of the file’s two subordinate files, the designer file (AboutProgram.Designer.vb) and the resource file (AboutProgram.resx).

Now would be a great time to save your work (File → Save All).

Adding the Main Form

As useful and full featured as the AboutProgram form is, such forms are seldom the core focus of an application. In the Library Project, this form will be displayed only when triggered from the “Main” form, so let’s add a simple main form now. In Visual Studio, select the Project → Add Windows Form menu command. When the Add New Item form appears, select Windows Form from the list of available items, and give it a name of “MainForm.vb” before clicking the Add button.

When the new form appears, adjust the following properties as indicated.

Property

Setting

(Name)

MainForm

FormBorderStyle

FixedSingle

MaximizeBox

False

Size

576, 459

Text

The Library Project

From the toolbox, add a Button control to the form with the following properties.

Property

Setting

(Name)

ActHelpAbout

Size

80, 24

Text

&About...

If you’re familiar with Visual Basic development from its pre-.NET days, you will recognize the “&” character in the button’s text. This special character sets the “shortcut” for the button. When you press the Alt key and the letter that follows “&” (in this case, A), the program acts as though you clicked on the button with the mouse.

Double-click the button and add the following code to the click event procedure.

INSERT SNIPPET

Insert Chapter 5, Snippet Item 5.

' ----- Show the About form.
AboutProgram.ShowDialog(  )

Here we specify a direct reference to the AboutProgram form. Before the 2005 version of Visual Basic, showing a new form required that you create an instance of the form class before showing it.

(New AboutProgram).ShowDialog(  )

That syntax still works, and is the way to go if you need to display multiple copies of the same form on-screen at the same time. However, the AboutProgram.ShowDialog( ) syntax is much cleaner for single-use forms, and more closely reflects how form presentation was done in Visual Basic since its initial release. Actually, this statement is using the My namespace. The full statement looks like this:

My.Forms.AboutProgram.ShowDialog(  )

The My.Forms collection allows you to reference any form within it without having to say “My.Forms” first. The members of the My.Forms collection represent default instances of each form in the project.

That’s all the code we need for now, but if you run the program, it will still show only the AboutProgram form. That’s because the AboutProgram form is set as the “startup” form. To alter this, open the project’s properties window, select the Application tab, and set the “Startup form” field to “MainForm.”

Since the AboutProgram form is now being shown as a “dialog” form (through a call to its ShowDialog method), its behavior is somewhat different. Each form includes a DialogResult property whose value is returned by the ShowDialog method when the form closes. Each button on your form can be configured to automatically set this property and close the form. The Close button on the AboutProgram form does just that; its own DialogResult property is set to Cancel, which is assigned to the form’s DialogResult property when the user clicks the Close button. As a side effect, anytime a value (other than None) gets assigned to the form’s DialogResult property, the form closes.

The upshot of that drawn-out paragraph is that you can now delete the event handler for the Close button’s Click event, and the button will still close the form. Delete the ActClose_Click procedure from the AboutProgram’s source code, run the program, and see what happens. The Close button still closes the form, even without the event handler.

You could also have left the procedure there, cleared the Close button’s DialogResult property, and added the following statement to that button’s event handler:

Me.DialogResult = Windows.Forms.DialogResult.Cancel

That brings to three the number of different ways we can close the AboutProgram form. It’s the flexibility of .NET at work; there are many different ways to accomplish the same task. So, be creative!

Extra Credit: Adding an Icon

If you’ve still got a little energy left, we can make one more change before this chapter runs out of paper: adding a custom icon to the main form. Just follow these step-by-step instructions:

  1. Display the main form by double-clicking on the MainForm.vb item in the Solution Explorer.

  2. Select the form’s surface.

  3. Select the form’s Icon property in the Properties panel.

  4. Click the " ... " button in this property, and search for the Book.ico file in the Chapter 5 Before subdirectory of the book’s installation directory. You can also use any other .ico file.

Save Your Work

Make sure you always save changes. By default, Visual Studio is configured to save your changes every time you run your program, but I like to save often just in case.

This chapter included a lot of manual instruction because there were so many cool Visual Studio features to play with; I just couldn’t help myself. We’ll probably keep up this pace somewhat for a few chapters, but eventually there will be so much code that a lot of it will come from the code snippets.



[3] Microsoft does offer line and shape controls as part of its “Power Packs” for Visual Basic 2005. You’ll find them in the download area of Microsoft’s Visual Basic Development Center, located at http://msdn.microsoft.com/vbasic. As of this writing, 2008 editions of the Power Packs are not yet available, but the 2005 versions will probably work just fine with Visual Basic 2008.

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

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