Welcome to MSDN Blogs Sign in | Join | Help

Common Issue: Security Exceptions

If you are hosting a control in the browser and have given it elevated permissions (we will use FullTrust for this example) you might notice that you are still getting security exceptions when you try to call protected APIs. 

Here's what is going on.

When Internet Explorer creates an AppDomain to host your control it does so without any consideration to the strong name permissions on your assembly.  This is perfectly sensible because the strong name evidence doesn't apply to the AppDomain, only to those assemblies which are signed.  The AppDomain will be created based on Zone and Url evidence which is available.

Usually this means that the AppDomain will be granted either the Internet or LocalIntranet PermissionSet.

Later when your assembly is loaded into the domain, it will be granted the permissions that were assigned to the strong name.

At this point your AppDomain looks something like this:

+------------------------------------------------------------+
| AppDomain: PermissionSet=Internet                          |
|                                                            |
|  +-------------------+     +----------------+ +----------+ |
|  | YourAssembly      |     | System.dll     | | ...      | |
|  |   FullTrust       |     |   FullTrust    | +----------+ |
|  |   Strong Name     |     |   Strong Name  |              |
|  +-------------------+     +----------------+              |
|                                                            |
+------------------------------------------------------------+

(excuse the ASCII diagram)

When you call an API which has a security demand on it, you can imagine your call stack looks something like this:

System.Diagnostics.Process.Start() FullTrust <-- Demand 
MyControl.button1_Click(...)       FullTrust        Demand succeeds
System.Windows.Forms.Stuff         FullTrust        Demand succeeds
AppDomain                          Internet zone    Demand FAILS

How do you fix this?  You have two choices.  The better option is to explicitly Assert each permission before using it (this is what I did in the event sample before calling the JavaScript code on the hosting page http://blogs.msdn.com/andrewdownum/archive/2006/01/26/ControlInBrowserEvents.aspx).

The call stack would look like this instead

System.Diagnostics.Process.Start() FullTrust <-- Demand 
MyControl.button1_Click(...)       FullTrust <-- ASSERT   Demand SUCCEEDS
System.Windows.Forms.Stuff         FullTrust              Not evaluated
AppDomain                          Internet zone          Not evaluated

The other option would be to change security policy for the zone or URL instead of strong name.  I don't recommend this because you are weakening the security policy on the machine for your convenience.

Posted by AndrewDownum | 1 Comments
Filed under:

Strong names for controls in the browser

If you have a control hosted in the browser and you try to strong name it, you may find that it stops working.  Looking at the IEHostLog (http://blogs.msdn.com/andrewdownum/archive/2006/02/10/Using_DebugIEHost.aspx) you might see something like this:

Microsoft.IE.SecureFactory: System.Security.SecurityException: That assembly does not allow partially trusted callers.
   at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(Assembly asm, PermissionSet granted, PermissionSet refused, RuntimeMethodHandle rmh, SecurityAction action, Object demand, IPermission permThatFailed)
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
   at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
   at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
   at System.Activator.CreateComInstanceFrom(String assemblyName, String typeName, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm)
   at System.AppDomain.CreateComInstanceFrom(String assemblyFile, String typeName, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm)
   at System.AppDomain.CreateComInstanceFrom(String assemblyFile, String typeName, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm)
   at Microsoft.IE.SecureFactory.CreateInstanceWithSecurity(Int32 dwFlag, Int32 dwZone, String pURL, String uniqueIdString, String link, String licenses)
The action that failed was:
LinkDemand

As the error message implies, you need to add the AllowPartiallyTrustedCallers attribute to your assembly. This happens if the AppDomain hosting the control is partially trusted.

See http://support.microsoft.com/?kbid=839300 for more discussion.

Posted by AndrewDownum | 6 Comments
Filed under:

Diagnosing Control in browser issues

Controls hosted in the browser can often be difficult to debug.  If anything goes wrong with loading the dll or constructing the control instance, you will get nothing but the standard IE "something is wrong" image (the small icon in the top-left with colored shapes). 

Here's how I recommend diagnosing these issues:

1.  Turn on the debug log for IEHost

  • Add a DWORD to
    HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework called "DebugIEHost" and set the value to 1
  • Add a String to
    HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework called "IEHostLogFile" with the full path (I use c:\windows\temp\IEHostLog.log)

2.  Restart Internet Explorer (the log won't work if you don't) and navigate to your control.
3.  Look for the log file (it will be in the location you specified and appended with a number (usually 1).
 
It will look something like this for a successful control instantiation:
Creating security manager
Microsoft.IE.Manager: Microsoft.IE.Manager: unique id lgth = 46
Microsoft.IE.SecureFactory: Create SecureFactory() with security information
Microsoft.IE.Manager: Created secure factory
Microsoft.IE.SecureFactory: Creating instance of the object in the correct domain
Microsoft.IE.SecureFactory: pUrl = http://ControlHostDomain/path/to/page/test.html
Microsoft.IE.SecureFactory: id = 86474707A3163707E65647D24756374777562610000000
Microsoft.IE.SecureFactory: link =
Microsoft.IE.SecureFactory: licenses =
Microsoft.IE.Manager: Url = http://ControlHostDomain/path/to/page/test.html
Microsoft.IE.Manager: UrlGetPartW returned 0
Microsoft.IE.Manager: UrlGetPartW returned 80070057
Microsoft.IE.Manager: CodeBase = http://ControlHostDomain
Microsoft.IE.Manager: Application = path/to/page
Microsoft.IE.Manager: Found a codebase
Microsoft.IE.Manager: UrlCanonicalize returned 0
Microsoft.IE.SecureFactory: URL codeBase: http://ControlHostDomain/
Microsoft.IE.SecureFactory: URL application: path/to/page
Microsoft.IE.SecureFactory: Locating domain for http://ControlHostDomain/
Microsoft.IE.IDKey: Created key
Microsoft.IE.Manager: The domain does not exist.
Microsoft.IE.IDKey: Created key
Microsoft.IE.Manager: The domain does not exist.
Microsoft.IE.SecureFactory: Need to create domain
Microsoft.IE.SecureFactory: Application base: http://ControlHostDomain/
Microsoft.IE.SecureFactory: Private Bin Path: bin
Microsoft.IE.IDKey: Created key
Microsoft.IE.SecureFactory: Trying to create instance of type http://ControlHostDomain/path/to/page/MyControl.dll#MyControl
Microsoft.IE.SecureFactory: Created instance of type http://ControlHostDomain/path/to/page/MyControl.dll::MyControl
If you don't get a log file at all, it is probably because the control assembly could not be downloaded at all.  Check to ensure that the classid property is correct and that your web server is configured to serve .dll files

Posted by AndrewDownum | 2 Comments
Filed under:

Exposing Events from managed controls

Earlier we talked about adding properties and methods to a managed control so that the page hosting the control can interact directly with the managed control programmatically.

This time we will add events which is a little bit more difficult.  To really understand how this works, you have to understand the COM event model (Eric Lippert started a series talking about how this works particularly as it relates to script languages here: http://blogs.msdn.com/ericlippert/archive/2005/09/09/463215.aspx). 

At a very high level, COM events work by allowing someone who wants to listen to events to implement an interface where each method on the interface represents an event that could be fired.  The implementation of this COM interface is then provided to the event source which can call any one of the provided methods when an event is "fired".  .NET has built in support for a more flexible event model based on delegates, but it does provide support for COM-style events through the System.Runtime.InteropServices.ComSourceInterfacesAttribute.

To take advantage of this in a control in the browser, we just need to define a COM interface that contains the signature of each event that we want to expose.  It would look something like this:

using System.Runtime.InteropServices;
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleEvents {
  [DispId(1)]//Each event must have a unique DispId
  void ButtonClicked();
}

The interface has a single method/event "ButtonClicked".

To get our to use this COM event, we just need to add the System.Runtime.InteropServices.ComSourceInterfacesAttribute.

I expanded on the control we had earlier to add a button to the control in the browser which fires a "ButtonClicked" event which is handled by script in the hosting page.

Host.html

<html>
  <head><title>Simple control host</title></head>
<body>
  <H1>Simple control host page</H1>
  <object id=simpleControl width=200 height=200 classid="SimpleControl.dll#SimpleControl"></object>
  <br>
  <a href="javascript:simpleControl.SayHello();">Say Hello</a>
  <br>
  <input type=text id=colorName value="Green"><input type=button onclick="simpleControl.ThemedBackgroundColor=colorName.value">
  <script language=JavaScript>
    function simpleControl::ButtonClicked() {
      alert("Managed button clicked");
    }
  </script>
</body>
</html>

 

SimpleControl.cs

using System;
using System.Drawing;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Runtime.InteropServices;

[ComSourceInterfaces(typeof(ISimpleEvents))]
public sealed class SimpleControl : Control {
  private Button button1;

  public SimpleControl() {
   this.BackColor = Color.Green;
   button1 = new Button();
   button1.Text = "Click Me!";
   button1.Width= 100;
   button1.Click += new EventHandler(HandleButtonClick);
   Controls.Add(button1);
  }

  private void HandleButtonClick(object sender, EventArgs e) {
    try {
      new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
      MethodInvoker h = ButtonClicked;
      if ( null != h) {
        ButtonClicked();
      }
    } catch (Exception ex) {
      MessageBox.Show(ex.ToString());
    }
  }

  public event MethodInvoker ButtonClicked;

  public void SayHello() {
    MessageBox.Show("Hello from Windows Forms");
  }

  public string ThemedBackgroundColor {
    get { return ColorTranslator.ToHtml(this.BackColor); }
    set { this.BackColor = ColorTranslator.FromHtml(value); }
  }
}

[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleEvents {
  [DispId(1)]//Each event must have a unique DispId
  void ButtonClicked();
}

 

NOTE: firing the ButtonClickedEvent requires UnmanagedCode permissions (essentially FullTrust) to execute.  This is because you are causing script code to execute which is not part of the managed CAS system and therefore considered unmanaged. 

In order to get this sample to work, you will need to "trust" the assembly hosting your managed control.

I did this for mine by adding my site to the "trusted sites zone" in internet explorer and with the command line:

caspol -m -ag 1.5 -url http://blogs.msdn.com/andrewdownum/* FullTrust

 

Posted by AndrewDownum | 3 Comments
Filed under:

Properties and Methods

Now that we have a simple control hosted in the WebBrowser, it would be nice to be able to interact with it from the page.  Using JavaScript, we can call methods on the managed control, and also get/set properties (which are essentially just method calls themselves).

We'll expand our simple control from before to include a property "ThemedBackgroundColor" and a method "SayHello" and then call them from JavaScript on the page.

A couple of things to notice about my sample implementation.

1.  I have introduced new properties and methods for my interactions even when the ones defined on System.Windows.Forms.Control might suffice.  I do this so that I can explicitly control the interactions between script and my control.  Soon, we will restrict all interactions to a defined interface to make this more explicit.

2.  I have used the string type for my color property rather than the System.Drawing.Color type.  I do this because JavaScript is a "stringy" language and I want to provide properties and methods that are as friendly as possible to the script world.  (Internally I use the System.Drawing.ColorTranslator to convert between these strings and the managed Color instances that I want).

Here is where we stand right now:

Host.html

<html>
  <head><title>Simple control host</title></head>
<body>
  <H1>Simple control host page</H1>
  <object id=simpleControl width=200 height=200 classid="SimpleControl.dll#SimpleControl"></object>
  <br>
  <a href="javascript:simpleControl.SayHello();">Say Hello</a>
  <br>
  <input type=text id=colorName value="Green"><input type=button onclick="simpleControl.ThemedBackgroundColor=colorName.value">
</body>
</html>

SimpleControl.cs

using System;
using System.Drawing;
using System.Windows.Forms;

public class SimpleControl : Control {
  public SimpleControl() {
   this.BackColor = Color.Green;
  }

  public void SayHello() {
    MessageBox.Show("Hello from Windows Forms");
  }

  public string ThemedBackgroundColor {
    get { return ColorTranslator.ToHtml(this.BackColor); }
    set { this.BackColor = ColorTranslator.FromHtml(value); }
  }
}

 

Notice now that after you deploy this control, you can click on the link "Say Hello" to cause a Windows Forms MessageBox to appear, and that entering an HTML color into the text box and clicking the button next to it changes the color of the managed control

Posted by AndrewDownum | 0 Comments
Filed under:

Windows Forms Control In Browser scenario (introduction)

Recently I have been hearing from more and more people who are embedding Windows Forms controls into webpages and I thought that I would put together a series of blog posts on this technique.

Since the original release of .NET Windows Forms has supported hosting controls inside of a web browser (Internet Explorer).  The control would be loaded inside the internet explorer process and would be visually represented inline with the other contents of the html page.  Here's how it works.

First we'll create a really simple control:

File: SimpleControl.cs

using System;
using System.Drawing;
using System.Windows.Forms;

public class GreenControl:Control {
  public RedControl() {
   this.BackColor = Color.Green;
  }
}

And a really simple web page to host it:

File: Host.html

<html>
  <head><title>Simple control host</title></head>
<body>
  <H1>Simple control host page</H1>
  <object width=20 height=20 classid="SimpleControl.dll#GreenControl"></object>
</body>
</html>

Then compile the control with the command line: csc /target:library SimpleControl.cs

I now have 3 files (SimpleControl.cs Host.html and SimpleControl.dll) in a directory.  I copy the last two files (.html and .dll) to a web server somewhere and navigate to the html file to see a completely uninteresting web page with a small green square of managed code.

Posted by AndrewDownum | 4 Comments
Filed under:

Finalization revisited

Last time (http://blogs.msdn.com/andrewdownum/archive/2004/12/13/282135.aspx) I talked about the consumer side of the dispose pattern (i.e. how to correctly use an object which supports the IDisposable pattern to ensure that its non-memory resources are cleaned up promptly).  This time, we get to talk about the producer (that is the definition of a disposable object itself).

 

Say that you are developing an object which will wrap an unmanaged resource (like a database connection, a file handle, a network socket or anything which has a greater significance than its memory space implies).  In this case you need to ensure that this resource gets freed up when it is no longer in use.  The simple way to do this is to provide a Dispose() method which frees the resources when called (either explicitly, or indirectly via the using statement)

 

public class DisposableObject : IDisposable

{

      private bool disposed = false;

      public void Dispose()

      {

            if (!disposed)

            {

                  disposed = true;

                  CleanupNativeResources();

            }

      }

      protected virtual void CleanupNativeResources()

      { /*Do some interesting work here*/ }

}

 

The class above implements the IDisposable interface and when disposed, it will call CleanupNativeResources(), a virtual method which is assumed to actually implement whatever cleanup logic is required (for example calling a Win32 API to close a file handle).

 

But what happens if the consumer of this object forgets to call dispose.  Even after all the warning given that it is the consumer’s responsibility to call the Dispose() method, sometimes these objects never get properly disposed.

 

Enter Finalization.

 

Finalization is a process through which an object can be registered with the runtime to have a special finalize method which will be called at some point after it can be guaranteed that there are no longer any references to the object.  As mentioned earlier, this could happen at any time after there are no references remaining to the object (this could be a long time if the Garbage Collector doesn’t detect any reason it needs to collect) but it makes for a good last chance to clean up these resources.

 

public class FinalizableObject : IDisposable

{

      private bool disposed = false;

 

      public void Dispose()

      {

            if (!disposed)

            {

                  disposed = true;

                  CleanupNativeResources();

                  GC.SuppressFinalize(this);

            }

      }

 

      ~FinalizableObject()

      { Dispose(); }

 

      protected virtual void CleanupNativeResources()

      { /*Do some interesting work here*/ }

}

 

This latest snipped adds a finalizer method (in C# this uses the syntax of a method ~%ClassName%() where you would replace %ClassName% with the actual name of the class you are defining a finalizer for).  In this case the finalizer simply calls the dispose method (which is generally a good idea).

 

There is also a call inside of the dispose method to GC.SuppressFinalize(this);

which notifies the runtime that the object no longer requires finalization and can be collected normally.  This avoids the performance hit incurred by having a finalizer if it is not needed.

 

Now we can write a class which can be disposed by a caller that no longer needs it, and which will be disposed by the runtime automatically at some later date if that doesn’t happen, but we would like to encourage the consumer to use dispose functionality and to notify them when they are not doing so.

 

We can do this in a very similar way to what Visual C++ does for memory leaks.  We simply keep track of all of the objects which are finalized (i.e. weren’t properly disposed) and then log some kind of information for them.

 

Following is the pattern that I use:

 

public class FinalizableObject : IDisposable

{

#if DEBUG

      private System.Diagnostics.StackTrace allocStack;

#endif //DEBUG

#if THREADSAFE

      //Use an int here so that we can later use Interlocked.CompareExchange

      private int disposed = DISPOSED_FALSE;

      private const int DISPOSED_TRUE = 1;

      private const int DISPOSED_FALSE = 0;

#else //!THREADSAFE

      private bool disposed = false;

#endif //THREADSAFE

 

      public bool Disposed

      {

            get

            {

#if THREADSAFE

                  return DISPOSED_TRUE == disposed;

#else //!THREADSAFE

                  return disposed;

#endif //THREADSAFE

            }

      }

 

      public FinalizableObject()

      {

#if DEBUG

            allocStack = new System.Diagnostics.StackTrace();

#endif //DEBUG

      }

 

      public void Dispose()

      {

#if THREADSAFE

            if(DISPOSED_FALSE == System.Threading.Interlocked.Exchange(ref disposed, DISPOSED_TRUE))

            {

#else //!THREADSAFE

            if (!disposed)

            {

                  disposed = true;

#endif //THREADSAFE

                  CleanupNativeResources();

                  GC.SuppressFinalize(this);

            }

      }

 

      ~FinalizableObject()

      {

#if DEBUG

            System.Diagnostics.Debug.Assert(false, "FinalizableObject was not disposed" + allocStack.ToString());

#endif //DEBUG

            Dispose();

      }

 

      protected virtual void CleanupNativeResources()

      { /*Do some interesting work here*/ }

}

 

Notice that if the debug compilation flag is set I capture the stack trace when the object is allocated and then fire a debug assertion in the finalizer that complains about the object being finalized.  The assert then has enough information to figure out where the object was allocated and why it wasn’t released.

 

This code also optionally supports threadsafe handling (so that dispose can be called concurrently by multiple threads.

 

Hope this snipped helps someone out there who is struggling with finalization.

Dispose revisited

I know that everyone and their uncle has blogged about their Dispose pattern flavor of the week, but I feel the need to add my 2 cents to the discussion.

 

I’d like to start with Andrew’s Cardinal Rule of Disposable objects.

 

“If your object has a Dispose method, call it as soon as you are done with the object.”

 

If you follow this simple rule with the Disposable objects that you use in your application, then you will always be ensured that system resources are released as promptly as possible.  You can rest easily tonight.  If you are like me however, just being told to call Dispose isn’t satisfying.  I want to know exactly what is going on “behind the curtain” and more importantly, I want to know why.

 

When the CLR and managed code came onto the scene, there was a great deal of noise made about the Garbage Collector (GC) and how it was going to solve all of the world’s problems (similar musings have been made about other GC languages including Java).  Simple fact of the matter is that the Garbage Collector is very good at what it is designed to do, collecting memory and only memory.  For the host of other resource allocation challenges, the runtime’s GC does not do this for you magically.  What it does do is define a set of patterns to make this as easy for you to manage yourself as possible.

 

The basic “problem” with the GC in this sense, is that it has no knowledge whatsoever of what resources are held by any object beyond the memory space it consumes. 

 

Consider the canonical example which reads in a file with an integer value on each line, adds it to a list of integers, sorts them and then outputs the sorted list to the same file:

 

const string FILEPATH = @"c:\foo.txt";

List<int> data = new List<int>();

StreamReader reader = new StreamReader(FILEPATH);

while (!reader.EndOfStream)

{

      int temp;

      if (int.TryParse(reader.ReadLine(), out temp))

      { data.Add(temp); }

}

data.Sort();

StreamWriter writer = new StreamWriter(FILEPATH);

foreach (int i in data)

{ writer.WriteLine(i); }

 

If you try to run this on your machine it is very likely that you will get an exception on the line:

StreamWriter writer = new StreamWriter(FILEPATH);

The problem is that you already opened that file earlier for read access (the new StreamReader(FILEPATH);) part) and never released it back to the operating system.  When you try to open it again, the OS denies the request because the file is already opened by someone else.

 

We know that the garbage collector is designed to clean up memory when it is no longer used, so it should be able to close the file automatically each time we are done with it (both when we are done reading it in, and when we are done writing it out).  But it doesn’t seem to do that.  Why?  For very good performance reasons (Rico’s article explains these quite well) the GC is “lazy” which means that it won’t clean up memory until it realizes that there is some advantage to doing so.  When you are done with the StreamReader, and trying to open up the StreamWriter to output, there is still plenty of memory available on your machine which is available to your app, so the GC sees no advantage in taking the time to clean up right away.

 

We could force the GC to clean everything up if we wanted to, but that’s a very costly (i.e. time-consuming) process and there are much better ways.

 

WARNING: Do not write code like this or your hair may fall out, your peers will likely laugh at you, and your code will certainly be suboptimal.

 

GC.Collect();

GC.WaitForPendingFinalizers();

 

You could also have used simply:

 

GC.GetTotalMemory(true);

 

Note: for this to work as you might expect, you will need to make sure that you build a release optimized version of your assembly (i.e. without debug settings).  When the runtime encounters a debug assembly, it intentionally prolongs the lifetime of managed objects through the entire scope of a method call to ease debugging.  As a result, the runtime keeps the file open through the entire method, and won’t free it up after it’s last use.

 

This causes the garbage collector to free up all available memory, wait for any finalizers which were triggered by the process (more on this later) and then collect again to get rid of the memory space taken by the finalized objects.  In the process when the garbage collector gets to the StreamReader it closes the file which is then available to be written to.

 

The key point to take away from this is that the GC, because it only tracks memory resources didn’t identify that there was any advantage to cleaning up the StreamReader right away (because there was plenty of memory available) and thus didn’t get around to it.  That’s why it’s possible (though quite unlikely) that if you ran the sample above it would work (if for some unrelated reason the GC decided to collect at an opportune moment for you, say while data.Sort() was executing).

 

So how do we fix the problem?  With Dispose of course.

 

Changing the above sample to this:

 

const string FILEPATH = @"c:\foo.txt";

List<int> data = new List<int>();

StreamReader reader = new StreamReader(FILEPATH);

while (!reader.EndOfStream)

{

      int temp;

      if (int.TryParse(reader.ReadLine(), out temp))

      { data.Add(temp); }

}

reader.Dispose();//<<<<<<<<<

data.Sort();

StreamWriter writer = new StreamWriter(FILEPATH);

foreach (int i in data)

{ writer.WriteLine(i); }

writer.Dispose();//<<<<<<<<<

 

Lets us explicitly say we are done with the file each time and ensures that it is available to be re-opened by the StreamWriter.

 

In C# there is shortcut syntax for this, the using statement:

 

const string FILEPATH = @"c:\foo.txt";

List<int> data = new List<int>();

using (StreamReader reader = new StreamReader(FILEPATH))

{

      while (!reader.EndOfStream)

      {

            int temp;

            if (int.TryParse(reader.ReadLine(), out temp))

            { data.Add(temp); textBox1.Text += temp.ToString() + "\r\n"; }

      }

}//reader is disposed here automatically

data.Sort();

using (StreamWriter writer = new StreamWriter(FILEPATH))

{

      foreach (int i in data)

      {

            writer.WriteLine(i);

            textBox2.Text += i.ToString() + "\r\n";

      }

}//writer is disposed here automatically

 

The using statement ensures that the Disposable object created inside the statement will be disposed upon leaving the code block (delimited by the curly braces) regardless of any exceptions which may be thrown.  This makes it yet more robust than the code which explicitly calls dispose.

 

Simple isn’t it?

 

Next post it will get more complicated, as I will talk about defining a disposable object itself (the other side of the curtain).

Hello, World

As is customary in the world of computers, I had to start with a Hello World.

Linked is a collection of Hello World programs written in a variety of languages

http://en.wikipedia.org/wiki/Hello_world_program

Here's a simple example of what you could do with the Windows Forms and C# in the 2.0 release (compatible with the publicly released beta http://lab.msdn.microsoft.com/express/vcsharp/)

using System;

using System.Windows.Forms;

 

namespace HelloWhidbey

{

      class HelloForm:Form

      {

            [STAThread]static void Main()

            { Application.Run(new HelloForm()); }

            public HelloForm()

            {

                  Text = "Hello, World!" + new string(' ', 20);

                  Timer t = new Timer();

                  t.Tick += delegate { Text = Text.Substring(1) + Text.Substring(0, 1); };

                  t.Start();

            }

      }

}

 

 
Page view tracker