Introduction
Although I'm primarily a C# guy, often I have to look at other languages in the .NET universe as well. Actually I come from a big Visual Basic background in the past (in the pre-.NET era which started for me in early 2001) having written several applications in Visual Basic. Currently I'm working on the Visual Basic 2005 Coach for MSDN Belux (more info will follow later) and concentrating on VB again. I'm pretty happy to see a bunch of new features in VB2005 which a ton of developers will benefit from in their daily lives, including stuff like generics, nullable types, operator overloading, partial types, my favorite using-statement (cf. IDisposable pattern), unsigned types (such as UInteger), the Continue statement for usage in loops, XML documentation support, etc. Also the rediscovery of edit and continue is certainly a big plus in VB2005 compared to VB.NET 2002/2003.
The "My" Thing
However, one of the things that deserves a bit of additional explanation is the My namespace in VB2005 to get easier access to several "things". Let's take a look. The idea if fairly straightforward: using the My namespace you can gain easy access to a wide variety of interesting objects and stuff on the system, related to the computer, the application, the user, etc. It's rather useless to start looking at all of the stuff accessible through the My namespace because the key goal is just to provide an easy way to "unlock the potential" of the framework through an approachable namespace that fits it all. So, the best recommendation I can do is to take a look at the namespace yourself in the Visual Studio 2005 environment with IntelliSense.
Just to name one example... I can't recall the number of times I've been looking for code to determine the current screen resolution in C#. Although it's a pretty simple piece of code I don't seem to remember the exact way to do it. Using the My namespace it's as easy as My.Computer.Screen.WorkingArea. Nice isn't it?
Diving deeper
Now, how does this work? Just create a simple Windows Forms project in Visual Basic 2005 and compile it. Then go to the Visual Studio 2005 Command Line and take a look at the .exe file's IL-code using ildasm.exe. The output will look somewhat like this:
Basically, the application's entrypoint lives in the My.MyApplication object (the Main sub). As you can see, the MyApplication object is an extension of some base class in the Microsoft.VisualBasic namespace:
.class private auto ansi beforefieldinit OhMy.My.MyApplication
extends [Microsoft.VisualBasic]Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
.custom instance void [System]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System]System.ComponentModel.EditorBrowsableState) = ( 01 00 01 00 00 00 00 00 )
.custom instance void [System]System.CodeDom.Compiler.GeneratedCodeAttribute::.ctor(string,
string) = ( 01 00 0A 4D 79 54 65 6D 70 6C 61 74 65 07 38 2E // ...MyTemplate.8.
30 2E 30 2E 30 00 00 ) // 0.0.0..
} // end of class OhMy.My.MyApplication
One particular point of interest in here is the fact that the object is annotated with a GeneratedCodeAttribute which indicates this code is being generated by a tool, in this case the Visual Basic compiler. The same approach is taken for things such as MyComputer, which is also inheriting from some base class that's defined in the Microsoft.VisualBasic namespace. Just take a look at the different defined namespaces to get more info about the different. For example, the syntax My.Computer will be translated to OhMy.My.MyProject.Computer (with OhMy the namespace for my project). In IL the equivalent is of course OhMy.My.MyProject.get_Computer which returns an instance of type OhMy.My.MyComputer. This property getter is responsible to get an instance of the Microsoft.VisualBasic.Devices.Computer object:
.method assembly specialname static class OhMy.My.MyComputer
get_Computer() cil managed
{
.custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 )
// Code size 16 (0x10)
.maxstack 1
.locals init ([0] class OhMy.My.MyComputer Computer)
IL_0000: nop
IL_0001: ldsfld class OhMy.My.MyProject/ThreadSafeObjectProvider`1<class OhMy.My.MyComputer> OhMy.My.MyProject::m_ComputerObjectProvider
IL_0006: callvirt instance !0 class OhMy.My.MyProject/ThreadSafeObjectProvider`1<class OhMy.My.MyComputer>::get_GetInstance()
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret
} // end of method MyProject::get_Computer
This is implementing a thread-safe singleton-like pattern to retrieve the Computer object using a generic class ThreadSafeObjectProvider that instantiates the given type using the default constructor:
.method assembly specialname instance !T
get_GetInstance() cil managed
{
.custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( 01 00 00 00 )
// Code size 38 (0x26)
.maxstack 2
.locals init ([0] !T GetInstance,
[1] bool VB$CG$t_bool$S0)
IL_0000: nop
IL_0001: ldsfld !0 class OhMy.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_ThreadStaticValue
IL_0006: box !T
IL_000b: ldnull
IL_000c: ceq
IL_000e: stloc.1
IL_000f: ldloc.1
IL_0010: brfalse.s IL_001c
IL_0012: call !!0 [mscorlib]System.Activator::CreateInstance<!T>()
IL_0017: stsfld !0 class OhMy.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_ThreadStaticValue
IL_001c: ldsfld !0 class OhMy.My.MyProject/ThreadSafeObjectProvider`1<!T>::m_ThreadStaticValue
IL_0021: stloc.0
IL_0022: br.s IL_0024
IL_0024: ldloc.0
IL_0025: ret
} // end of method ThreadSafeObjectProvider`1::get_GetInstance
This piece of code checks whether the field m_ThreadStaticValue (with the generic type) is not null. If so, it's just returned (path IL_0010 --> IL_001c), otherwise the object is instantiated using an Activator (that finally calls the default constructor) and stored in the field m_ThreadStaticValue:
.field private static !T m_ThreadStaticValue
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.custom instance void [mscorlib]System.ThreadStaticAttribute::.ctor() = ( 01 00 00 00 )
<Remark>Notice that the .NET Framework code guidelines inside the VB implementation itself (looking at the IL) are somewhat violated! One has defined a property GetInstance in the generic ThreadSafeObjectProvider, which should really be a methodin my opinion. Properties have to be lightweight whileas the getter of the GetInstance property over here will even invoke the Activator (one time). Beside of that, a property starting with Get looks a bit weird.</Remark>
Now, going a little further, how does the compiler get the instructions to insert this piece of code? Basically, it will always do this regardless of the build environment or the tools you're using. For example, a simple application like
Module Test
Sub Main
End Sub
End Module
compiled through vbc.exe will emit a My namespace automatically (check!). However, the "MyApplicationCodeGenerator" which you can find in the .vbproj file of your project, also guides the exact compilation process. Let's take a look:
<ItemGroup>
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
</ItemGroup>
As you can see, the .myapp file of the project is included in the build process somewhere. What's in this file?
<?xml version="1.0" encoding="utf-8"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MySubMain>true</MySubMain>
<MainForm>Form1</MainForm>
<SingleInstance>false</SingleInstance>
<ShutdownMode>0</ShutdownMode>
<EnableVisualStyles>true</EnableVisualStyles>
<AuthenticationMode>0</AuthenticationMode>
<ApplicationType>0</ApplicationType>
<SaveMySettingsOnExit>true</SaveMySettingsOnExit>
</MyApplicationData>
This piece of XML is mapped on a bunch of settings you can set on the project's properties in Visual Studio. One example is the MySubMain setting, which indicates that the Main entrypoint of the application is generated automatically in the OhMy.My.MyApplication class.
What about C#?
Some of you will wonder whether it's possible to use this flexibility in C# too. The short answer is: "not directly". In order to get it working, add a reference to Microsoft.VisualBasic.dll and import the namespace Microsoft.VisualBasic.Devices as follows:
using
My = Microsoft.VisualBasic.Devices;
Once you've done so, you can use (some of) the functionality of the VB My namespace in C# as well, e.g.:
new
My.Computer().Screen.WorkingArea;
Notice you have to instantiate the object in C# because we hook in to the My namespace using a different approach, i.e. no stubs with accessors for "Computer" have been created as this was the case in VB with the MyComputer generated class inheriting from the Microsoft.VisualBasic.Devices.Computer using a singleton pattern behind the scenes. Of course you could create some stubs yourself which are easy to recycle whenever you need them (e.g. by changing the MSBuild files to insert that piece of logic into every application). To mimic this behavior completely, you could insert the following class to your root app namespace in C#:
class My
{
private static Computer computer;
public static Computer Computer
{
get
{
if (computer == null)
computer = Activator.CreateInstance<Computer>();
return computer;
}
}
}
A more generic way of doing this looks as follows (including some basic sample code of using it in a form):
using System;
using System.Drawing;
using Microsoft.VisualBasic.Devices;
namespace OhMy
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Rectangle r = My.Computer.Screen.WorkingArea;
}
}
internal class ThreadSafeObjectProvider<T>
{
private T theInstance;
public T GetInstance()
{
if (theInstance == null)
theInstance = Activator.CreateInstance<T>();
return theInstance;
}
}
internal class My
{
private static ThreadSafeObjectProvider<Computer> computerObjectProvider;
static My()
{
computerObjectProvider = new ThreadSafeObjectProvider<Computer>();
}
public static Computer Computer
{
get
{
return computerObjectProvider.GetInstance();
}
}
}
}
Also notice we're creating a class over here, instead of a namespace. In Visual Basic, My is really a namespace but the easy access to things such as Computer is done through the use of a new attribute, called Microsoft.VisualBasic.HideModuleNameAttribute. This pattern in VB (which can be used to extend the My namespace easily) looks like this:
Namespace My
<HideModuleName()> _
Module CustomMyThing
Private ReadOnly thingie As MyThing = New MyThing
Public ReadOnly Property Thing() As MyThing
Get
Return thingie
End Get
End Property
End Module
End Namespace
Class MyThing
' Stuff goes here
End Class
Basically, the shortcut My.Thing will work only because the Visual Basic tools support this shorthand convenient syntax when applying the attribute.
A Visual Studio Template for C#
Let's bring the My stuff to C# in a convenient way! To do so, I created a .vsi package that contains an item template to include the needed My stuff to a Visual Studio 2005 C# project with support for My.Computer (other stuff has not been implemented yet, will take a look at it if I have some spare free time left). If you don't know how the creation of a Visual Studio template looks like, keep an eye on my blog. I'll try to post some notes about it over here pretty soon. In essence you have to create a .vsi file which is really a .zip file containing another zip with the template files and some XML files describing what's in the template and how it should be displayed in VS2005.
To install the template, do the following:
- Download the My CSharp template over here.
- Rename the file to MyCSharp.vsi (stripping off the .zip portion)
- Double-click the file in Windows Explorer. This will bring up the VS Content Installer to install the template.

- Click Next, confirm to install the content by clicking Yes on the "No Signature Found" dialog (if you trust me :-)) and finally click Finish.

- Open Visual Studio 2005, create a Visual C# project (e.g. Windows Forms).
- Go to Solution Explorer, click the project node and choose Add Item. The My namespace supporting file will appear in the list:

- Now you have enabled "My namespace" like support in your C# project.

Have fun!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks