My.Internals: Examining the Visual Basic My Feature

 

Tyler Whitney
Visual Basic Team
Microsoft Corporation

Updated November 2005

Summary: See the technical details on how the My feature accesses the .NET Framework functionality and items within a project. This article is not a primer on how to use the My feature, but shows how it works. (8 printed pages)

Download theMyInternals.msi sample file.

Contents

Introduction
.NET Framework Classes Available from My
Dynamically Generated Classes
Threading
Conclusion

Introduction

My is a new feature in Visual Basic 2005 that puts commonly used functionality at your fingertips and reduces the number of lines of code that you have to write. It does this in a way that is efficient, robust, and thread-safe. In an environment where programmer productivity matters more than ever, My can help you get things done faster, which is what Visual Basic is all about.

The purpose of this article is not to introduce the My feature or demonstrate how to use it in your applications. Rather, this article is for developers who already know what My is about and want behind-the-scenes insight into how My exposes .NET Framework functionality and items in a project.

**Note   **This article provides detailed coverage of the workings of the My feature in Visual Basic 2005. If you are interested in an overview of this feature, please read Navigate the .NET Framework and Your Projects with "My" first.

.NET Framework Classes Available from My

Due to the breadth and depth of the .NET Framework, it can be daunting to find the functionality you need. To address this problem, My provides entry points to frequently used .NET Framework classes and functions. My also exposes new high-level .NET Framework classes that pull together related functionality into task-based APIs.

My exposes functionality either by returning a .NET Framework class that is instantiated and ready to go, by deferring a call to a .NET Framework method, by returning a class that aggregates functionality found in the .NET Framework common to a particular task, or by returning a dynamically generated class that provides access to items in your project.

Direct Exposure

Whenever possible, My serves as a discovery mechanism for existing classes in the .NET Framework, and exposes those types directly. As an example, consider My.Application.Deployment:

Public ReadOnly Property Deployment() As _
    System.Deployment.ApplicationDeployment
        Get
            Return _
            System.Deployment.ApplicationDeployment.CurrentDeployment
        End Get
    End Property

Other examples of direct exposure include: My.Computer.FileSystem.OpenTextFileReader(), which returns a System.IO.StreamReader;

My.Application.OpenForms(), which returns a System.Windows.Forms.FormsCollection;

My.User, which returns System.Security.Principal.IPrincipal and others.

Facade

My also returns new classes that provide task-based APIs for operations that were previously hard to accomplish because it was difficult to find the functionality and/or it required the orchestration of multiple objects.

Common tasks that would require the use of low-level .NET Framework APIs, or that would require multiple lines of code, are greatly simplified. As an example, consider the task of determining if a network connection is available:

Imports System.Net.NetworkInformation
Public ReadOnly Property IsAvailable() As Boolean
    Get
        For Each NetInterface As NetworkInterface In _
NetworkInterface.GetAllNetworkInterfaces()
            If NetInterface.Type <> InterfaceType.Loopback _
            AndAlso NetInterface.Type <> InterfaceType.Tunnel _
            AndAlso NetInterface.OperationalStatus = _
                OperationalStatus.Up Then
                Return True
            End If
        Next
        Return False
    End Get
End Property

The code above entails detailed knowledge of several types in the System.Net.NetworkInformation namespace. Using the facade pattern, My.Computer.Network distills these types and their relationships into a single line of code: My.Computer.Network.IsAvailable().

A facade may also simply pull together related functionality that is otherwise difficult to find because it is defined across various classes within the frameworks. For instance, My.Computer aggregates common functionality related to the computer. It does so by providing a single API that pulls together the functionality of several classes in the framework. As an example, My.Computer.Name() and My.Computer.Screen() expose functionality defined in Environment and PrimaryScreen:

  Public ReadOnly Property Name() As String
    Get
        Return System.Environment.MachineName
    End Get
End Property
Public ReadOnly Property Screen() As System.Windows.Forms.Screen
    Get
        Return System.Windows.Forms.Screen.PrimaryScreen
    End Get
End Property

Other examples of classes in My that aggregate related functionality from multiple .NET Framework types include My.Application, My.Computer, My.Computer.FileSystem, My.Computer.Info, and My.Application.Info and others.

Proxy Classes

A proxy class is an extremely thin class that forwards all calls it receives to an underlying object. For instance, if you call My.Computer.Clipboard.GetText(), you are making a call to the proxy class method ClipboardProxy.GetText(), which is defined as:

Public Function GetText() As String
    Return Clipboard.GetText()
End Function

By convention, proxy classes are always suffixed with Proxy. My utilizes proxies when accessing the clipboard, file system, and registry because the underlying classes being exposed by the proxies are made up of shared methods that otherwise wouldn't be visible in IntelliSense. Proxy classes cannot be instantiated by the user. Performance testing has shown that forwarding calls through proxies is insignificant.

Dynamically Generated Classes

In the spirit of 'functionality at your fingertips,' My provides access to the forms, Web services, resources, and settings defined in your project. If your project contains a reference to the Web service MapDirections, for instance, you can immediately use that Web service without having to know how to spin up an instance of the Web service proxy. You can simply type:

My.WebServices.MapDirections.GetDirections(...)

How does that work? For the forms, Web services, settings, and resources in your project, a factory class is generated by the compiler that returns lazily created instances on demand. The factory classes are tuned to return instances in a way that is appropriate for the type of project (exe/dll/web) in which your code runs. See the section on Threading for more details.

My.Forms

As an example of a dynamically generated class, consider My.Forms. This class is provided for you and defines a factory method for each Form in your project. When you access Form through My.Forms.Form1, the factory method checks to see if an instance of Form1 is already open. If it is, that instance is returned. Otherwise, an instance of Form1 is created and returned. The generated code for a project containing Form1 would look like this:

Class MyForms 
    'Code in bold is generated by the compiler
    Public m_Form1 As Form1
    Public Property Form1() As Form1
        Get
            m_Form1 = Create__Instance__ (Of Form1)(m_Form1) 
            Return m_Form1 
        End Get
        Set(ByVal Value As Form1)
         If Value Is m_Form1
        Return
           End If
           If Not Value Is Nothing Then
              Throw New ArgumentException( _
  "Property can only be set to Nothing.")
           End If
           Dispose__Instance__ (Of Form1)(m_Form1)
        End Set
    End Property
End Class

The Create__Instance__() function is responsible for creating an instance of the form on demand. It checks to see if the form (stored in m_Form1) has been instantiated or not. If it has been, it is returned. Otherwise an instance is created and returned. Create__Instance__() also catches recursive Form creation attempts. Dispose__Instance__() is responsible for closing the Form.

My.Forms also provides the means to reintroduce default instances for forms that you may be familiar with in earlier versions of Visual Basic. Default instances make it possible to refer to an instance of a form without having to explicitly create it first.

For instance, in Visual Basic 6.0 you might have written Form1.Show() instead of:

   Dim Form1Instance as Form1
   Form1Instance = new Form1
   Form1Instance.Show()

Because the compiler in Visual Basic 2005 utilizes My.Forms to create default instances, you can simply write Form1.Show().

My.WebServices

One of the difficulties people encounter coding against a Web service is figuring out which class to code against. My.WebServices takes the guesswork out and provides an instance of the Web service proxy on demand.

My.WebServices is most appropriate for synchronous calls to a Web service. The code generated to provide instances of Web service proxies follows the same pattern as that shown for returning instances of forms.

My.Settings

New in Visual Basic 2005 is the Settings designer that allows you to specify application settings on an application-wide basis or per-user basis. The designer creates a class that provides strongly typed access to your settings. You can see an example of the MySettings class in the sample project by showing all files in the Solution Explorer and looking for the MySettings.vb file under the MySettings.Settings node.

Here is an example of a property generated to manage an application setting named SampleUserSetting:

Partial NotInheritable Class MySettings
Inherits System.Configuration.ApplicationSettingsBase
    <System.Diagnostics.DebuggerNonUserCode(),  _
     System.Configuration.UserScopedSettingAttribute(),  _
     System.Configuration.DefaultSettingValueAttribute("Try Me")>  _
    Public Property SampleUserSetting() As String
        Get
            Return CType(Me("SampleUserSetting"),String)
        End Get
        Set
            Me("SampleUserSetting") = value
        End Set
    End Property
End Class

The generated class does all the heavy lifting for you. All you have to do to access the setting is type:

My.Settings.SampleUserSetting

My.Resources

Also new in Visual Basic 2005 is the Resource designer, which allows you to add resources to your application. The Resource designer also creates a module that provides strongly typed access to the resources in your application. For instance, if you add a bitmap named Smiley to your project, you can access it with My.Resources.Smiley. You can see an example of the generated resource module by showing all files in the Solution Explorer for the sample project and looking for the MyResources.vb file under the MyResources.resx node.

Here is an example from the sample project of a factory property generated to return the Smiley resource:

Public ReadOnly Property Smiley() As System.Drawing.Bitmap
    Get
        Return CType(ResourceManager.GetObject("Smiley", _resCulture),_
                     System.Drawing.Bitmap)
    End Get
End Property

The strongly typed resources module that is generated for you deals with case-sensitive resource IDs, uses the System.Resources.ResourceManager class to retrieve your resources, and manages the details associated with getting the ResourceManager created correctly for your application.

To access the same Smiley bitmap in Visual Basic 2002 or Visual Basic 2003, you first would have had a difficult time putting the bitmap in a .resx file. Instead, you may have put it in the project as an embedded resource and had to remember to change the build action in the property grid for the resource to be Embedded Resource. Then, you would have written something like:

Dim CurrentAssembly As Reflection.Assembly = _
Reflection.Assembly.GetExecutingAssembly
Dim BitMapStream As IO.Stream = _
CurrentAssembly.GetManifestResourceStream( _
"WindowsApplication2.Smiley.bmp")
Dim SmileyBitmap as Drawing.Bitmap = New Bitmap(BitMapStream)

There are some important details you would have to get right in this code. You would have to know to get the currently executing assembly and call GetManifestResourceStream()on it. You would have to remember to qualify the resource name with the root namespace name. You would have to get the casing right because the name passed to GetManifestResourceStream()is case-sensitive. You would have to know where the stream class is defined so you could capture the return value of GetManifestResource in a stream object. You would have to know how to create a bitmap from a stream. You likely would have encountered frustration trying to figure out why BitMapStream kept coming back Nothing, due to one of the aforementioned issues.

Visual Basic 2005 solves the first problem by providing a Resource Editor that makes it easy to put new or existing resources into a .resx file. My then makes it easy to access those resources. All you have to do is write:

Dim SmileyBitmap as Drawing.Bitmap = My.Resources.Smiley

Threading

Instances of classes available from My are exposed in a way that alleviates threading concerns because instances of My objects are provided per-thread. That is, the instance of My.Computer returned on thread 1 will be different than an instance of My.Computer returned on thread 2. This means that you don't have to write synchronization code when using My objects.

In a Web application, the instances returned from My are stored per-request.

Conclusion

We've examined how My exposes .NET Framework classes and how dynamically generated classes are exposed by My to give you access to items in your project, such as resources, settings, Web services, and your forms.

In the end, My is about reducing the number of lines of code you have to write and about providing ready access to the functionality you need most often. It does this in a way that is efficient, robust, and thread-safe.

In an environment where programmer productivity matters more than ever, My can help you get things done faster and more efficiently.

© Microsoft Corporation. All rights reserved.