Advanced Visual Basic .NET - Project 7

Creating a Code Library (OLE Server Application)

Project 7

What’s so important about Code Libraries and OLE Server applications

Code Libraries, in the form of DLL (Dynamic Link Libraries) files, form the foundation of the Windows Operating System--dynamic link library files have a .dll extension.  The functions that a Code Library contains are exposed so that other applications can access them.   For example, the code that generates the Windows GUI (Graphic User Interface) exists in DLL files which are part of the operating system.  These files are shared and can be used by almost every application.  By putting commonly used functions into Code Libraries, programs don't need to contain the code of those functions, so they are smaller and easier to maintain. A Code Library is usually in the form of a DLL file, which cannot run as a stand-alone application.   An OLE Server application on the other hand, is a stand-alone program (the program file has an .exe extension), which also has exposed functionality.  For example, Microsoft Word and Excel are Ole Server applications.  We discovered that in projects 5 and 6, where we were able to run instances of Word and Excel directly from within our own applications.

Creating a custom CompInfo code library

In this project, we will create a Code Library that contains four custom classes (objects). Each class will have several Properties or Methods that provide useful information about the computer.  Here is a list of the classes we will create:

Code Library
Class Property/Method  
pCPU Count Number of CPU's in the computer
  Architecture CPU architecture, i.e. x86
  Version Version number of the CPU
  Level Level number of the version
  Identifier A summary of the above information
pOS Platform The name of the operating system
  Version The version of the operating system
pDisk Size Size of the specified disk drive
  FreeSpace Free space on the specified disk drive
  UsedSpace Used space on the specified disk drive
pMemory FreePhys Free physical memory
  FreeVirt Free Virtual memory

All of the above classes will be combined into a single Code Library file named CompInfo.dll.  We will then be able to create instances of these classes from within other applications by setting a reference to the CompInfo.dll code library.

Removing your old project and creating a new one

Run Visual Basis .NET and open your Solution (<your name>.sln).

Right-click on the LoanCalc project in the Solution Explorer window and select Remove from the context menu.  Click the OK button when warned that LoanCalc will be removed from the Solution.  Drop down the File menu and select New Project under the Add Project menu item.  When the Add New Project dialog appears, be sure that the Visual Basic Projects folder is open in the Project Types pane, and that the Windows Application template is selected in the Templates pane.  Type CompInfo in the Name textbox.  Then click the OK button.  This creates a new folder inside the \Visual Studio Projects\<Your Name> folder named CompInfo:

        ...My Documents\Visual Studio Projects\<Your Name>\CompInfo.

Note: When class is over, be sure to follow the instructions at the end of this project that tell you how to copy your project to your floppy diskette so you can take it home with you.

Deleting the Form file from the new project

Right-click on the Form1.vb in the Solution Explorer window and select Delete from the context menu (do not save the form if prompted to do so).

Adding four Class modules to the project

Right-click on the CompInfo project in the Solution Explorer window (as shown below) and select Add New Item under the Add menu item on the context menu:

On the Add New Item dialog, be sure the Class template is selected in the Templates pane (see illustration below).  Type pCPU.vb in the Name textbox and click the Open button:

This adds the pCPU class module to the project.  Now use the Add New Item option three more times to add three more class modules to the project and name them pDisk.vb, pMemory.vb, and pOS.vb. Be sure to name each class module as you add it by typing it's name in the Name textbox of the Add New Item dialog.  Your solution explorer window should look like this when you are finished adding all four Class modules:

Setting the Startup Object to Sub Main

Right-click on the CompInfo project in your Solution Explorer window, click on the Properties item at the bottom of the context-menu.  In the CompInfo Property Pages dialog drop down the Startup object list and choose Sub Main and click the OK button.  We will be adding a Module file to our project later, where we will create a Sub Main procedure to test our new classes as we create them.

Adding five Properties to the pCPU class

The basic format of a Property declaration looks like this (do not type this code):

'Internal storage for the property's value.
Private MonthNum As Integer = 1
'The Month property declaration
Property Month() As Integer
     'The Get section returns the value of the property
    Get
        Return MonthNum
    End Get
     'The Set section applies a value to the property
    Set(ByVal Value As Integer)
        If
Value < 1 Or Value > 12 Then
              'Error processing for invalid value.
        Else
       
    MonthNum = Value
        End If
    End Set
End Property  

This Month Property--of some unknown class, hereafter called <ClassName>--is an example of the basic layout of a Property declaration.  Notice the Get and Set parts of the declaration.   The Get part is executed when the value of the property is examined, with code like this for example:

If  <ClassName>.Month = 6  Then

The Set part of the property declaration is executed when the programmer wants to assign a new value to the Property, like this:

<ClassName>.Month = 11

This kind of Property has both Get and Set parts.  But Properties can be created that have only a Get section.  Properties with only a Get section are called ReadOnly Properties, since their values cannot be set by the programmer.   The properties that we will be creating for the pCPU class will be ReadOnly.   A ReadOnly property declaration looks like this:

'Returns the number of CPU's in the computer
 
ReadOnly Property Count() As String
    Get
        Return GetEnvironmentVariable("NUMBER_OF_PROCESSORS").ToString
    End Get
End Property

Notice how we use the ReadOnly keyword when we declare the property.  This prevents us from even adding a Set section in the declaration.  This means that a programmer can see the value of this property, but they cannot assign a value to it. Note: A common example of a ReadOnly property is the Count property of a collection.  When examined, the Count property returns the number of items in the collection, but you cannot assign a value to Count because it is ReadOnly

Accessing information about the Environment with System.Environment

Be sure that the code window of the pCPU class is displayed.  Add the following Imports statement at the top of the  pCPU class code window (above the Public Class pCPU line):

    Imports  System.Environment  

This gives us access to the GetEnvironmentVariable method of the System.Environment class.  By passing an environment variable name as a string to the GetEnvironmentVariable method, it returns the value of that environment variable.  Note: Environment variables are stored in the registry, the GetEnvironmentVariable method looks in the registry to find their values.  Now add the following code below the Public Class pCPU line to create a Count property for the pCPU class:

'Returns the number of CPU's in the computer
ReadOnly Property Count() As String
    Get
        Return GetEnvironmentVariable("NUMBER_OF_PROCESSORS").ToString
    End Get
End Property

Your pCPU code window should look like this now:

Now continue adding the following code to create four more properties for pCPU (below the End Property line of the Count property):

'Returns the Processor Architecture
ReadOnly Property Architecture() As String
    Get
        Return GetEnvironmentVariable("PROCESSOR_ARCHITECTURE").ToString
    End Get
End Property

'Returns the Processor Level
ReadOnly Property Level() As String
    Get
        Return GetEnvironmentVariable("PROCESSOR_LEVEL").ToString
    End Get
End Property

'Returns the Processor Version
ReadOnly Property Version() As String
    Get
        Return GetEnvironmentVariable("PROCESSOR_REVISION").ToString
    End Get
End Property

'Returns the Processor description
ReadOnly Property Identifier() As String
    Get
        Return GetEnvironmentVariable("PROCESSOR_IDENTIFIER").ToString
    End Get
End Property

Adding a Module so that we can Test the new Properties of the pCPU class

In order to test the five new properties we've added to the pCPU class, we will need to add a Module to the project.  Think of a Module as the code window part of a Form without the actual Form.  Right-click on the CompInfo project in the Solution Explorer window and select Add New Item under the Add menu item on the context menu.  On the Add New Item dialog, be sure the Module item in the Templates pane is selected (as shown below), and type modCompInfo.vb in the Name text box and click the Open button:

Your solution explorer window should look like this when you are finished adding the modCompInfo.vb Module:

Adding a Sub Main procedure to the Module

Open the code window for the modCompInfo.vb module file.  Below the Module modCompInfo line, type the following line of code and press enter:

    Sub Main  

This creates a Sub Main procedure.  Recall that above we set the Startup Object to Sub Main.   That means that when we run this project, any code that we add to the Sub Main procedure will be executed (Note: This project has no Form!).  Add the following line of code to the Sub Main procedure to construct a pCPU object:

Dim objCPU As New CompInfo.pCPU()

Now that we have created a pCPU object, we can access it's properties to see if the values they contain are valid.  Add the following lines of code to the Sub Main procedure. 

Debug.WriteLine(">>>CPU<<<")
Debug.
WriteLine("CPU.Count: " & objCPU.Count)
Debug.
WriteLine("CPU.Arch: " & objCPU.Architecture)
Debug.
WriteLine("CPU.Version: " & objCPU.Version)
Debug.
WriteLine("CPU.Level: " & objCPU.Level)
Debug.
WriteLine("CPU.Ident: " & objCPU.Identifier)

Notice that when you typed objCPU. a pop up list of the properties we added to the pCPU class are displayed:

Important: Because this project has no form, we should add a message box to let us know when the program finishes.  Add the following line of code immediately above the End Sub line at the bottom of Sub Main to display a message box when the program finishes:

'Because there is no form, display a message box when the program finishes.
MessageBox.Show("Program Finished! Check the OutPut Window.", _
        "CompInfo Test"
)

Testing the program so far

Save the project now.  Run the program.  When the message box tells you the program is finished, close it and take a look at the Output window.  It should look something like this:  

Notice the five lines under the >>>CPU<<< header line.  These are the Debug.WriteLine statements for the five properties of the pCPU class.  Your values will differ from the above values, depending upon the type of computer you are using.

Adding Two Properties to the pOS class

Be sure that the code window of the pOS class is displayed.  We will use the OSVersion class of System.Environment to provide the values for the Platform and Version properties we will now add to the pOS class.  So add the following Imports statement at the top of the pOS class code window (above the Public Class pOS line):

    Imports  System.Environment  

This gives us access to the of the OSVersion class, which conveniently has Platform and Version properties.  Add the following code below the Public Class pOS line to create the Platform and Version properties for the pOS class:

'Returns the Operating System Name
ReadOnly Property Platform() As String
   
Get
        Return
OSVersion.Platform.ToString
    End Get
End Property

'Returns the Operating System Version
ReadOnly Property Version() As String
    Get
        Return OSVersion.Version.ToString
    End Get
End Property

Using Sub Main to test the new pOS properties

Save the project now.  Add the following line of code at the beginning of the Sub Main procedure in the modCompInfo.vb module code window to construct a pOS object:

Dim objOS As New CompInfo.pOS()

Now add the following three lines of code to the Sub Main procedure (above the Message Box line of code) in the modCompInfo.vb module code window:

Debug.WriteLine(">>>OS<<<")
Debug.
WriteLine("OS.Platform: " & objOS.Platform)
Debug.
WriteLine("OS.Version: " & objOS.Version)

Testing the program so far

Save the project now.  Run the program.  When the message box tells you the program it's finished, close it and take a look at the Output window.  Do you see the pOS Platform and Version information under the >>>OS<<< header line?

Adding three Methods to the pDisk class

Instead of Properties, we will add three Methods (Size, FreeSpace, and UsedSpace) to the pDisk class.  Unlike Properties, Methods are functions that can be passed parameters.  In this case, Size, FreeSpace, and UsedSpace need to be passed the drive letter they are supposed to provide information for, i.e. objDisk.Size("c:")

Adding a Reference to the System.Management component library

To access information about the disk drives on the computer, we will need to use a ManagementObject ManagementObject's are available through the System.Management component library.  The System.Management component library is not available by default, so we will need to set a Reference to it.  Right-click on the CompInfo project in the solution explorer window and select the Add Reference menu item, as shown below:

With the .NET tab selected, scroll down and select the System.Management component library and click the Select button once to add it to the Selected Components listbox (as shown below).  Then click the OK button:

Now with the pDisk class code window open, add the following Imports statement at the top (above the Public Class pDisk line):

    Imports  System.Management  

This gives us access to the of the ManagementObject class, which can be passed a win32_logicaldisk.deviceid=<drive> parameter to get information about specific disk drives on the computer.  Adding methods to a Class is as simple as adding custom functions to the code window of a form.  Add the following code below the Public Class pDisk line:

'Returns the physical size of Drive
Function Size(ByVal Drive As String) As String
    Try
        Dim Disk As New _
           
ManagementObject("win32_logicaldisk.deviceid=" &  _
                   
"""" & Drive & """")
       
Disk.Get()
        'Convert the number to Gigabytes
        Return Disk("Size").ToString / 1024 / 1024 / 1024
    Catch
        Return "Drive " & Drive & " not found!"
    End Try
End Function

In the above code we construct a ManagementObject called Disk.  By using it's Get method we populate a collection it contains with information about the disk drive (Drive) that we passed along with the win32_logicaldisk.deviceid= parameter to the ManagementObject constructor call.  The different elements of the Disk collection can be accessed by providing an index string, i.e. Disk("Size").  Also notice the Try-Catch error trapping in the above code.  This is always a good idea when creating Methods for your classes which rely upon some, potentially erroneous, values being passed to them by a programmer (we know how stupid they can be).  Now add the following code to add two more methods to the pDisk class:

'Returns the amount of Free Space on Drive
Function FreeSpace(ByVal Drive As String) As String
    Try
        Dim Disk As New _
           
ManagementObject("win32_logicaldisk.deviceid=" &  _
                   
"""" & Drive & """")
       
Disk.Get()
        'Convert the number to Gigabytes
        Return Disk("FreeSpace").ToString / 1024 / 1024 / 1024
    Catch
        Return "Drive " & Drive & " not found!"
    End Try
End Function

'Returns the amount of Used Space on Drive
Function UsedSpace(ByVal Drive As String) As String
    Try
       
Dim Space As Long
        Dim Disk As New _
           
ManagementObject("win32_logicaldisk.deviceid=" &  _
                     """"
& Drive & """")
       
Disk.Get()
        'Calculate the Used space
        Space = Disk("Size").ToString - Disk("FreeSpace").ToString
        'Convert the number to Gigabytes
        Return Space.ToString / 1024 / 1024 / 1024
    Catch
        Return "Drive " & Drive & " not found!"
    End Try
End Function

Using Sub Main to test the new pDisk methods

Add the following line of code at the beginning of the Sub Main procedure in the modCompInfo.vb module code window to construct a pDisk object:

Dim objDisk As New CompInfo.pDisk()

Now add the following four lines of code to the Sub Main procedure (above the Message Box line of code) in the modCompInfo.vb module code window:

Debug.WriteLine(">>>Disk<<<")
Debug.
WriteLine("Disk.Size: " & objDisk.Size("c:"))
Debug.
WriteLine("Disk.FreeSpace: " & objDisk.FreeSpace("c:"))
Debug.WriteLine("Disk.UsedSpace: " & objDisk.UsedSpace("c:"))

Testing the program so far

Save the project now.  Run the program.  When the message box tells you the program is finished, close it and take a look at the Output window.  Do you see the result of pDisk's Size, FreeSpace, and UsedSpace method calls under the >>>Disk<<< header line?

Adding two Properties to the pMemory class

To access information about the computer's memory, we will use the ManagementObjectSearcher class of the System.Management component library.  The ManagementObjectSearcher class returns a collection of WMI objects based upon a specified query. Here is Microsoft's description of WMI: Windows Management Instrumentation (WMI) is a scalable system management infrastructure that uses a single consistent, standards-based, extensible, object-oriented interface. WMI provides us with a standard way to interact with system management information and the underlying WMI APIs. WMI is used primarily by system management application developers and administrators to access and manipulate system management information.

Basically, what this means is, we can access information about the computer if we construct a ManagementObjectSearcher object.  Here is the code we will use to construct a ManagementObjectSearcher object (don't type this code yet):

Dim sQuery As String
sQuery =
"SELECT * FROM Win32_OperatingSystem"
Dim WMIData As New ManagementObjectSearcher(sQuery)

Win32_OperatingSystem is a WMI class that represents an operating system installed on a Win32 computer system. Any operating system that can be installed on a Win32 system is a descendent (or member) of this class. If the computer has multiple operating systems installed, this class returns only an instance for the currently active operating system.  By using Win32_OperatingSystem in the query string that we pass to the ManagementObjectSearcher object constructor we create a WMI object that has access to more than fifty different properties that the Win32_OperatingSystem class contains.  We can then extract specific information about system memory with code like this (don't type this code yet):

Dim mo As ManagementObject
For Each
mo In WMIData.Get()
     'Return the amount of free Physical Memory
    Return mo("FreePhysicalMemory").ToString
Next
mo

Or this:

Dim mo As ManagementObject
For Each
mo In WMIData.Get()
     'Return the amount of free Virtual Memory
    Return mo("FreeVirtualMemory").ToString
Next
mo

By creating a ManagementObject reference variable (mo) in the above code, we can look through the WMIData collection with a For-Each loop and return only the information we are interested in to provide the values for our properties. 

With the pMemory class code window open, type the following Imports statement at the top (above the Public Class pMemory line):

    Imports  System.Management  

This gives us access to the ManagementObjectSearcher and ManagementObject classes.  Now add that following code below the Public Class pMemory line to create the FreePhys and FreeVirt properties of the pMemory class:

'Return the amount of free Physical Memory
ReadOnly Property FreePhys() As String
   
Get
       
Try
            Dim
sQuery As String
           
sQuery = "SELECT * FROM Win32_OperatingSystem"
            Dim
WMIData As New ManagementObjectSearcher(sQuery)
            Dim
mo As ManagementObject
            For Each
mo In WMIData.Get()
                Return
mo("FreePhysicalMemory").ToString
            Next
mo
        Catch
            Return
"(Unknown)"
       
End Try
    End Get
End Property

'Return the amount of free Virtual Memory
ReadOnly Property FreeVirt() As String
   
Get
       
Try
            Dim
sQuery As String
           
sQuery = "SELECT * FROM Win32_OperatingSystem"
            Dim
WMIData As New ManagementObjectSearcher(sQuery)
            Dim
mo As ManagementObject
            For Each
mo In WMIData.Get()
                Return
mo("FreeVirtualMemory").ToString
            Next
mo
       
Catch
            Return
"(Unknown)"
       
End Try
    End Get
End Property

A little Try-Catch error trapping doesn't hurt here either.

Using Sub Main to test the new pMemory properties

Add the following line of code at the beginning of the Sub Main the procedure in the modCompInfo.vb module code window to construct a pMemory object:

Dim objMemory As New CompInfo.pMemory()

Now add the following three lines of code to the Sub Main procedure (above the Message Box line of code) in the modCompInfo.vb module code window:

Debug.WriteLine(">>>Memory<<<")
Debug.
WriteLine("Memory.FreePhys: " & objMemory.FreePhys)
Debug.
WriteLine("Memory.FreeVirt: " & objMemory.FreeVirt)

Testing the program so far

Save the project now.  Run the program.  When the message box tells you the program is finished, close it and take a look at the Output window.  Do you see pMemory's FreePhys and FreeVirt property information under the >>>Memory<<< header line?

Making the changes necessary to compile CompInfo as a Code Library file

In order to test the classes we've added to the CompInfo project, we started off by creating a normal windows application with the Windows Application template.  This results in a program that compiles to a stand-alone executable file that has an EXE extension.  This allowed us to use the Sub Main of a module to build and run the program each step of the way, so that we could test the properties and methods of our four classes as we created them.  In it's final form though, CompInfo must be compiled as a dynamic link library, or DLL file.  It will have a .dll extension, and not be a stand-alone application.  It is surprisingly easy to make the modifications required so that VB.NET will compile what began as a stand-alone EXE application into a DLL Code Library.

We must begin by commenting out all the code in the Sub Main of our modCompInfo.vb file.  This code is no longer required for an application that cannot run stand-alone.  When you're finished adding a single quote to the beginning of each line the code in your Sub Main should should like this:

Now save the project and completely exit from VB.NET before doing the next step.

Modifying the Project file with Notepad

Important: Be sure that you have completely exited from VB.NET before doing this step!  Open the My Documents folder, then open the Visual Studio Projects folder.  Locate your solution folder (your name) and open it.  Inside you should find your CompInfo project folder.  Open it to see your CompInfo project files, as shown below:

Important: You must make sure that you can see the file extensions of your files.  By default, the file extensions of known file types are hidden.  To turn this option off--so that you can see all file extensions--do the follow:

  1. Drop down the Tools menu (shown above) and select Folder Options.
  2. Click on the View tab of of the Folder Options dialog.
  3. Remove the checkmark from the Hide extensions of known file types item and click the OK button.

Your main project file should be named, CompInfo.vbproj.   Right-click on the CompInfo.vbproj file and select the Open With... item on the context menu (as shown above).  The Open With dialog should appear:

Important: Make sure that the Always use the selected program to open this kind of file item below the listbox is NOT check marked.  Uncheck it if it is checked.  Select the Notepad item on the list.  Now click the OK button.

Once it is loaded into Notepad, you can see that the CompInfo.vbproj project file is just a plain text file.  In order to change the project so that it produces a Code Library file (a DLL file) instead of a stand-alone EXE file, modify the circled line in the illustration below from this:

    OutputType = "WinExe"

to this:

    OutputType = "Library"

Check your spelling!  The default value "WinExe" must be changed to "Library".  Once your OutputType line matches the circled line in the illustration above, choose Save on Notepad's File drop down menu to save the modified project file.  Now exit from Notepad.

Compiling your Project to create a Code Library file (DLL)

Drop down the Solution Configurations listbox and change the default Debug setting to Release to exclude debugging information from the finished CompInfo.dll file: 

Note: The inclusion of debugging information in the compiled version of your project allows you to step though the program so you can debug it during the program development process.  To significantly speed up, and reduce in size, the final version of your program, change the Solution Configurations option from Debug to Release when compiling your applications prior to release.

Right-click on the CompInfo project in the solution explorer window and select the Build menu item, as shown:

Once the compilation is complete (after just a few seconds), you should see this message in the Output window at the bottom of the screen:

Taking a look at the finished CompInfo.dll file

Exit Visual Basis .NET and open the My Documents folder, then open the Visual Studio Projects folder.  Locate your solution folder (your name) and open it.  Inside you should find your CompInfo project folder.  Open it to see your CompInfo project files.  Open the bin folder:

You should see a CompInfo.exe file, which was created when we were compiling and running the project to test it as we added properties to the different classes.  You should also see a CompInfo.dll file, which is the Code Library version of the project that we just created by changing the the OutputType item in the project file to Library.

In project 8 we will write an application that uses the CompInfo Code Library file (CompInfo.dll).  We will also learn how to convert a Code Library into a Component.


To copy a Project folder from your Solution on the Hard Drive to a floppy diskette, follow these steps:

  1. Exit Visual Basic .NET and insert the floppy diskette, that you want to copy the Project folder to, into drive A:
  2. Select the My Documents item on the Start Menu to open the My Documents folder.
  3. In the My Documents folder, double-click the Visual Studio Projects folder to open it.
  4. Double-click on your Solution folder to open it (it should have your name).
  5. Open the Project folder that you want to copy, by double-clicking on it.

Deleting the Obj and Bin folders from inside the Project folder before copying it.

  1. Inside the Project folder, delete the Obj and Bin folders--these folders are created automatically when you open a project.  You do not need to copy them, or their contents, to your floppy diskette.
  2. Hit the Backspace key once--or click the Back button on the toolbar.  This moves you from inside the Project folder to back inside your Solution folder.
  3. Right-click on the Project folder and selected: 31/2" Floppy A: on the Send To fly-out menu.  This copies the Project folder to your floppy diskette.