KnowDotNet Visual Organizer

Using your Environment

by William Ryan
Print this Article Discuss in Forums

.NET's Environment Class has a whole host of features that you will probably want to take advantage of at some point in your programming if you haven't done so already.  Below is simple a code snippet and above each line, a description of what it does.  This namespace is very easy to use and gives you a lot of out of the box functionality.  What I'd like to do is show you how it works from the code side, and then examine what's happening in the disassembly.  As you'll see, this class mimicks the actual IL instructions very closely and is very efficient to use.  Also, note that all of the properties and methods are static/shared so you don't need to instantiate an instance of the Environment object to use it.  That allows you to write less code and makes it very easy to use.

//5.0.2195.0
Debug.WriteLine(Environment.OSVersion.Version);
//SomeDomainName
Debug.WriteLine(Environment.UserDomainName);
//WR-Primary
Debug.WriteLine(Environment.MachineName);
//wryan
Debug.WriteLine(Environment.UserName);
//C:\RatesManager\bin\Debug
Debug.WriteLine(Environment.CurrentDirectory);
//491509031
Debug.WriteLine(Environment.TickCount.ToString());
//1.1.4322.573  - Shows that the Autobuild is working
Debug.WriteLine(Environment.Version.ToString());
//This is the '.NET' way to insert a new line or carriage return
//Using this method, you add a new line based on the operating system
//version.  So when .NET is implemented on different operating systems,
//you won't have to rework your code to accomodate differences between
//Unix, Linux, Windows, MAC etc
Debug.WriteLine(Environment.NewLine);
//20746240
Debug.WriteLine(Environment.WorkingSet.ToString());
//And Remember, this is the first call of the program!
//at System.Environment.GetStackTrace(Exception e)
//at System.Environment.GetStackTrace(Exception e)
//at System.Environment.get_StackTrace()
//at RatesManager.Utility..ctor() in c:\ratesmanager\utility.cs:line 31
//at RatesManager.Form1.Form1_Load(Object sender, EventArgs e) in c:\ratesmanager\form1.cs:line 325
//at System.Windows.Forms.Form.OnLoad(EventArgs e)
//at System.Windows.Forms.Form.OnCreateControl()
//at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
//at System.Windows.Forms.Control.CreateControl()
//at System.Windows.Forms.Control.WmShowWindow(Message& m)
//at System.Windows.Forms.Control.WndProc(Message& m)
//at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
//at System.Windows.Forms.ContainerControl.WndProc(Message& m)
//at System.Windows.Forms.Form.WmShowWindow(Message& m)
//at System.Windows.Forms.Form.WndProc(Message& m)
//at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
//at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
//at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr  lparam)
//at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow)
//at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
//at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
//at System.Windows.Forms.Control.set_Visible(Boolean value)
//at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
//at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
//at System.Windows.Forms.Application.Run(Form mainForm)
//at RatesManager.Form1.Main() in c:\ratesmanager\form1.cs:line 321
Debug.WriteLine(Environment.StackTrace.ToString());


At this point, you should notice a few things.  Right off the bat the Environment knows quite a bit about who you are and where you are doing it from.  This, combined with some Code Access Security can give you some real flexibility in making secure and responsive applications.  Since this is one of the easiest classes to use, I think the above code is pretty much self explanatory.  However, I think it's important to understand what's going on behind the scenes.  So what does the disassembly look like?

.method public hidebysig specialname rtspecialname
        instance void  .ctor() cil managed
{
  // Code size       138 (0x8a)
  .maxstack  2
  .locals init ([0] int32 CS$00000002$00000000,
           [1] int64 CS$00000002$00000001)
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  call       class [mscorlib]System.OperatingSystem [mscorlib]System.Environment::get_OSVersion()
  IL_000b:  callvirt   instance class [mscorlib]System.Version [mscorlib]System.OperatingSystem::get_Version()
  IL_0010:  call       void [System]System.Diagnostics.Debug::WriteLine(object)
  IL_0015:  call       string [mscorlib]System.Environment::get_UserDomainName()
  IL_001a:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_001f:  call       string [mscorlib]System.Environment::get_MachineName()
  IL_0024:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0029:  call       string [mscorlib]System.Environment::get_UserName()
  IL_002e:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0033:  call       string [mscorlib]System.Environment::get_CurrentDirectory()
  IL_0038:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_003d:  call       int32 [mscorlib]System.Environment::get_TickCount()
  IL_0042:  stloc.0
  IL_0043:  ldloca.s   CS$00000002$00000000
  IL_0045:  call       instance string [mscorlib]System.Int32::ToString()
  IL_004a:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_004f:  call       class [mscorlib]System.Version [mscorlib]System.Environment::get_Version()
  IL_0054:  callvirt   instance string [mscorlib]System.Version::ToString()
  IL_0059:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_005e:  call       string [mscorlib]System.Environment::get_NewLine()
  IL_0063:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0068:  call       int64 [mscorlib]System.Environment::get_WorkingSet()
  IL_006d:  stloc.1
  IL_006e:  ldloca.s   CS$00000002$00000001
  IL_0070:  call       instance string [mscorlib]System.Int64::ToString()
  IL_0075:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_007a:  call       string [mscorlib]System.Environment::get_StackTrace()
  IL_007f:  callvirt   instance string [mscorlib]System.String::ToString()
  IL_0084:  call       void [System]System.Diagnostics.Debug::WriteLine(string)
  IL_0089:  ret
} // end of method Utility::.ctor



If you examine the disassembly, it's pretty evident what's happening behind the scenes, and it's very simple.  You can see that unlike some other features of .NET the disassembly instructions mimick the C# code almost verbatim.  This shows you that there isn't a high layer of abstraction between the IL and the code in your language of choice.  You will also invariably see what happens when you call Debug.WriteLine...namely it's a void function that passes in a string argument.  This is pretty much what you would expect...that's why everything that isn't a string needs converted to a string before you pass it into Debug.WriteLine in C#, and in VB.NET, the same must occur if you have on Option Strict.  If you don't (you deserve all the associated evil with leaving it off) you can easily see by the IL what has to happen.  The function has to take in a string...so the CLR has to do this for you.  This has to happen at runtime, which makes it late bound...slowing down everything and forcing the runtime to guess what your intention is as opposed to knowing it explicitly.  I suspect if more folks looked at the disassembled versions of their code, they wouldn't be so quick to defend some of their bad habits.

I'd like to point out another thing.  Look at instruction   IL_0054:.  You'll see that it appends a ToString() call at the end of it.  Since Version already returns a string in the first place, adding a ToString() is unnecessary and causes extra effort to be exerted to do the exact same thing.  I did this on purpose to show why knowing what's happening under the hood is so important.  If you don't, you may end up writing many other things like the one above that compiles, seems to work fine, but has built in inefficiency.

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing