Francesco's blog

 Wednesday, April 11, 2007

I like the ability to extend the power of my applications by simply adding a reference to an assembly that contains the functions or the controls that I need. I like much less, however, the need to distribute and deploy many DLLs together with my executables. In this post I show a technique that I use to compress (nearly) all the DLLs of a Windows Forms application and "merge" them with the main EXE.

All the files you need are in this ZIP archive, which contains the AsmZip.exe utility (which you run from the command prompt) and two source files, Unzipper.cs and Unzipper.vb. I suggest that you copy the AsmZip utility in a directory listed on the system path, to run it easily.

Step-by-step
These are the steps you must follow to implement the technique.

1) Add either the Unzipper.vb or the Unzipper.cs file to the main project of your application, depending on the language you've used.

2) In the Main method, add a statement that initializes the AssemblyUnzipper class (which is defined in the Unzipper file you added in step 1).
       ' (Visual Basic 2005)
       CodeArchitects.AssemblyUnzipper.Initialize()
       // Visual C# 2005
       CodeArchitects.AssemblyUnzipper.Initialize();
It is essential that this statement runs before any other statement in the application, particularly before showing a form that contains a control implemented in one of the DLLs you want to compress. If you are working with VB and the application has a startup form (and therefore you don't have a Sub Main method), you should initialize the AssemblyUnzipper class from inside the startup form's static constructor:
      Shared Sub New()
         CodeArchitects.AssemblyUnzipper.Initialize()
      End Sub

3) compile the project, obviously in Release mode. (You should use this technique just before delivering the executable to your customer(s).

4) open a command prompt window from inside the application's \bin directory, and run the AsmZip utility as follows:
             AsmZip main.exe *.dll
where main.exe is the name of the main executable. The above command compresses *all* the DLLs in the directory and appends the compressed data to the main.exe file. If you want to compress just a subset of the DLLs that the application uses, you should specify their names, as in this example:
             AsmZip main.exe CodeArchitects*.dll Microsoft*.dll
There can be a few good reasons not to compress some of the DLLs used by the applications, as I'll explain shortly.

5) You can now delete all the DLLs that you have compressed, because the application - thanks to the AssemblyUnzipper class - is able to find them at the end of its executable file, to decompress them, and to load them in memory.

Pros
Before proceeding with an explaination of the technique's inner details, let's summarize its advantages:

a) simpliefied deployment: you need to distribute fewer files (often just the main EXE)
b) more robust applications: end users can't break the application by accidentally deleting one of its DLLs
c) fewer bytes on disk: all DLLs are compressed and appended to the main EXE file
d) the ability to "hide" some of your trade secrets, for example which 3rd party controls you've used
e) a slightly better protection of your intellectual property: compressed DLLs can't be decompiled, at least not as easily as uncompressed DLLs .

The last two points aren't a real protection against even unexperienced malicious hackers, if he or she is determined to peek into your application. To do so he would just need to decompile the main EXE, understand how the AssemblyZipper class works, and write a short programma that works similarly but saves the uncompressed assemblies to disk. In other words, don't rely on this technique to protect your code from reverse engineering.

The AsmZip tool relies on the GZipStream class to compress the original DLLs, therefore the compression factor that you achieve with this technique is lower than the one you can obtain with WinZip or WinRar, but it is usually more than adequate, as the following figure shows.


How it works
This technique relies on the AssemblyResolve event of the AppDomain object. This event fires when the CLR loads an assembly referenced by the running application. By handling this event you can perform some nice tricks that wouldn't be possible otherwise. For example, you might load satellite assemblies from a network share or from a binary field in a database.

The AssemblyUnzippere class uses this event to search the required assembly from a compressed stream that has been appended to the application's main EXE file:
      // the handler for AssemblyResolve event
      static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
      {
         // find the assembly with given name, cause error if not found
         AssemblyInfo info = null;
         if ( AsmInfos.TryGetValue(e.Name, out info) )
            return ExtractAssembly(info);
         // signal error
         Debug.WriteLine("Failed to uncompress assembly " + info.Name);
         return null;
      }
Each AssemblyInfo object keeps track of where, in the main EXE file, the compressed data for each DLL is located. The AsmInfos dictionary enables the code to quickly locate the information associated with a DLL with given name. This dictionary is created inside the Initialize method, when the application is launched, and is then used each time the application attempts to load an assembly. For more details, see comments in either the VB or the C# source code.

Limitations
I tried this technique with several Windows Forms apps, without any problem. The main issue is that compressed assemblies loaded programmatically have their Location property set to null/Nothing, but if you don't use reflection to explore the assembly's feature you might never realize that the assembly was loaded in a nonstandard way. For example, if your app dynamically loads all the assemblies in a given directory, for example to explore their attributes, it is evident that it won't work as intended if these DLLs have been compressed and then deleted. In such cases, you should exclude these DLLs from compression.

The AssemblyZipper class works only with Windows Forms applications. For what I know, it is possible to use the AssemblyResolve event inside ASP.NET applications, but it isn't possible to use the AssemblyUnzipper in that context. However, the problems that this technique solves aren't considered as real issues under ASP.NET, therefore I don't think it makes sense to use it in web applications.

The only other limitation is that this technique works with DLLs but not with the main EXE. If you have a large EXE that uses some small DLLs, you won't achieve an interesting compression factor. In such a case, you might want to move your forms from the main EXE into a DLL and then compress the DLL with AsmZip. Even better, the main EXE might contain only the splash screen (if you have one) and it should load the startup form from the DLL that contains the actual application. Using this approach it is often possible to achieve an overall compression factor near or above 60 percent.

Note: in the first implementation of this technique I managed to successfully compress even the main EXE and used a small “stub” executable whose only job was to decompress and launch the actual EXE. After some tests, however, I found that the technique wasn’t very stable and I fell back to the technique described in this article.

4/11/2007 5:07:41 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [6]  | 
 Wednesday, May 31, 2006

Visual Basic 6's App object exposes a useful property named PrevInstance, which allows you to determine whether there are other instances of the same application running. This property has never been implemented in VB.NET, even though a VB2005 application can use the StartupNextInstance event for this purpose.

' to display this code, open the Application page of the My Project designer and click the Application Events button
Namespace
My
  
Partial Friend Class MyApplication
     
Private Sub MyApplication_StartupNextInstance(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
         ' another instance of this application has been launched
      End Sub
  
End Class
End
Namespace

The problem of this approach is that the event is raised in the first instance of the application, not in the new instance, therefore when the VB.NET application starts you can't determine for sure whether it is the only instance in the system. In other words, you can learn only whether and when another application is launched, not if the current applicatin is the first and only running instance.

.NET developers have solved this problem in a variety of ways, for example using Mutexes or by iterating over the collection returned by the Process.GetProcesses method. In .NET we have a new and more elegant alternative, based on the System.Threading.Semaphore type. A semaphore is a counter which can be incremented and decremented. If its value becomes zero, a thread can't decrement it and must wait for another thread to increment it to a value >0. Interestingly, if the semaphore has a name it becomes a system-wid object which can be shared by all the applications that ask for a reference to a semaphore with that specific name. It is therefore sufficient that the app creates a semaphore with a unique name just after its launch (e.g. it can be a name that includes the executable's path) to have all the instances of a given app share the same semaphore and therefore the same counter. The only problem left to solve is ensure that the semaphore's counter be correctly resotred when the application terminates, but this is easy to achieve by means of a Finalize method.

In addition to the PrevInstance property - which returns False if the application was the only running instance when the application was launched - the following VB6App class exposes also the InstanceCount property, which returns the total number of instances in that moment (the current app is included in the count). Here's the VB2005 version of this class:

Class VB6App
   ' the default instance
  
Private Shared DefValue As New VB6App

   ' the system-wide semaphore
  
Private semaphore As System.Threading.Semaphore
   ' initial count for the semaphore (very high value)
  
Private Const MAXCOUNT As Integer = 10000

   Private Sub New()
      Dim ownership As Boolean = False
      ' create a unique name, but strip invalid characters
     
Dim name As String = "VB6App_" & System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\", "")
      semaphore = New System.Threading.Semaphore(MAXCOUNT, MAXCOUNT, name, ownership)
      ' decrement its value 
      semaphore.WaitOne()
      ' if we got ownership, this app has no previous instances
      m_PrevInstance = Not ownership
   End Sub

   ' the PrevInstance property returns True if there was a previous instance running 
  
' when the default instance was created
  
Private Shared m_PrevInstance As Boolean

   Public Shared ReadOnly Property PrevInstance() As Boolean
      Get
         Return m_PrevInstance 
      End Get
   End Property

   ' return the total number of instances of the same application that are currently running 
  
Public Shared ReadOnly Property InstanceCount() As Integer
      Get
         ' release the semaphore and grab the previous count 
        
Dim prevCount As Integer = DefValue.semaphore.Release()
         ' acquire the semaphore again
        
DefValue.semaphore.WaitOne()
         ' eval the number of other instances that are currently running 
        
Return MAXCOUNT - prevCount
      End Get
   End Property

   Protected Overrides Sub Finalize()
      ' increment the semaphore when the application terminates
     
semaphore.Release()
   End Sub

End Class

Notice that this class has a Finalize method without implementing IDisposable. It is one of those special cases when it is OK to violate the Dispose-Finalize pattern.

To understand how the class works, just keep in mind that the Semaphore.Release method increments the internal counter, whereas the WaitOne method decrements it. You must test the VB6App.PrevInstance property as soon as the application starts, for example in the Sub Main method or in the Load event hander for the main form, as to let the VB6App class store the boolean value internally. The same form might then test the value of the InstanceCount property on exit, for example if you need to run some cleanup code when the last application instance is about to terminate:

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
   If Not VB6App.PrevInstance Then 
      ' open the common log file
      ' ...
   End
If
End Sub

Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
   If VB6App.InstanceCount = 1 Then
     
' close the common log file.
      ' ...
  
End If
End Sub

Here's the C# version of the class. (I fixed the original version to fix two syntax errors mentioned in one of the comments below.)

public class VB6App
{
   // the default instance 
   private static VB6App DefValue = new VB6App(); 
   // the system-wide semaphore
   private System.Threading.Semaphore semaphore; 
   // initial count for the semaphore (very high value)
   private const int MAXCOUNT = 10000;

   private VB6App() 
   { 
      // create a named (system-wide semaphore)
      bool ownership = false
      // create the semaphore or get a reference to an existing semaphore

      string name = "VB6App_" + System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(":", "").Replace("\\", "");
      semaphore = new System.Threading.Semaphore( MAXCOUNT, MAXCOUNT, name, out ownership); 
      // decrement its value
     
semaphore.WaitOne(); 
      // if we got ownership, this app has no previous instances
      m_PrevInstance = !ownership;

   }

   // the PrevInstance property returns True if there was a previous instance running
  
// when the default instance was created

   private static bool m_PrevInstance ;

   public static bool PrevInstance 
  
      get 
     
         return m_PrevInstance ; 
     
   }

   // return the total number of instances of the same application that are currently running

   public static int InstanceCount 
  
      get 
     
         // release the semaphore and grab the previous count 
        
int prevCount = DefValue.semaphore.Release(); 
         // acquire the semaphore again
        
DefValue.semaphore.WaitOne(); 
         // eval the number of other instances that are currently running 
        
return MAXCOUNT - prevCount; 
     
   }

   ~VB6App() 
  
      // increment the semaphore when the application terminates
     
semaphore.Release(); 
   }
}

5/31/2006 10:10:12 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, May 29, 2006

I have been so busy in May that I couldn't update the blog, not even to mention that my latest Microsoft Press book had been released and is now available on all major US bookstores. With fewer than 600 pages, Programming Microsoft Visual C# 2005: The Base Class Library is surely the shortest book I wrote. Not only that: it's also the book that took me less time to wrote. In fact, this book is basically the translation to C# of the second half of my VB 2005 book, more precisely of the chapters that have to do more with the .NET Framework and less with the C# language itself. Here's the Table of Contents:

1. .NET Framework Basic Types
2. Object Lifetime
3. Interfaces
4. Generics
5. Arrays and Collections
6. Regular expressions
7. Files, directories, and streams
8. Assemblies and resources
9. Reflection
10. Custom attributes
11. Threads
12. Object serialization
13. PInvoke and COM interop

Even though the book isn't specifically on the C# language, it adequately covers most of the new features of C# 2.0, such as generics, iterators, and anonymous methods.

Why a book on the BCL? Well, in these years I realized that far too many developers focus solely on high-level features - such as Windows Forms, ADO.NET, and ASP.NET - and often fail to leverage the full potential from other portions of the .NET Framework. For example, I have seen many apps that use verbose and unefficient validation rules that might be replaced by a single regular expression. Or apps that could be written in a fraction of time (and lines of code) if the author had been conscious of the full potential of reflection and custom attributes. Not to mention the fact that new .NET 2.0 features, such as generics, could make things only worse.

In general I find that most books that are "translated" from a different programming language are disappointing, so you might wonder why this book should be different. First, when I signed the contract for my VB 2005 book I already knew that the book would have been translated to C#, thus I planned the book so that its structure wouldn't be too VB-centric. Secoond, in the last four years I have been using C# in virtually all my programming projects - in fact I have surely written more C# code than VB code. For this reason, you'll find that the C# code is carefully optimized to use all the usual C#-specific techniques, such as iterators and anonymous delegates.

Shortly I will prepare a home page for the book on this site, with a couple of sample chapters. In the meantime, you can read more about the book (and hopefully order it) on Amazon's home page.

5/29/2006 8:50:45 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, December 08, 2005

Last spring I co-authored this book, Practical Guidelines and Best Practices for Microsoft Visual Basic .NET and Visual C# Developers, arguably the longest title in Microsoft Press's history. The book is a reasoned list of guidelines that all .NET developers should follow, actually is by far the largest collection of its kind you can find anywhere. It covers language syntax, memory usage, Windows Forms and ASP.NET applications, security, and more.

Unlike most other similar collections, though, we clearly divide the "rules" in guidelines (naming guidelines, comment usage, etc.) and best practices. The difference is subtle but important: most guidelines are primarily a style matter, whereas best practices impact the scalability, the speed, or the robustness of your application. This means that our guidelines are arbitrary and in fact we often offer alternate rules and clearly explain the pros and cons of each style.

You can learn more about the principles we used in the book's Introduction and in John Robbins's Foreword. (Unlike most foreword writers, John actually read each and every page in the manuscript and gave us some great advice about improving it.) Or click the figure to jump to the book's home page, where you can read three sample chapters and download the book's source code.

Today I have uploaded a 30-page Word document that contains a summary of all the rules covered in the book, orderly grouped by topic and with a reference where in the book each rule is explained. You can edit this document as you see fit, delete or edit the guidelines you aren't interested in, and so forth. We routinely use this document in internal code reviews or when we consult at customers' places, so we hope it will be useful to you as well.

P.S. You must register to access this material. We swear we'll never send you anything that vaguely resemble spamming, just 100% technical contents!

12/8/2005 2:50:28 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, November 25, 2005

Every now and then I get an email from a reader or a customer, who asks for clarifications on object finalization and disposing. As far as I know, the best article on this topic is this essay by Joe Duffy. It's over 25-page long, covers both .NET 1.1 and 2.0, and includes comments from gurus such as Jeffrey Richter e Chris Brumme. This is easily the definitive article on this topic and I urge you to read it if you haven't already.

The Dispose-Finalize pattern is objectively a complex matter. However, in most cases it can be simplified significantly if you use the following approach: (1) the class with the Dispose/Finalize method should wrap only one single unmanaged resource, and (2) this finalizable class should be private and nested inside another disposable (but not finalizable) type. The outer class is the only class that can use the finalizable class.

This simple trick enables the GC to immediately release all the memory used by the wrapper (disposable) class even in the worst case - that is, if the client code omits to invoke the Dispose method - and simplifies the structure of the type that uses the unmanaged resource. A listing is worth one thousand words, thus here is the C# version of what I mean:

// the class that clients use to work with the unmanaged resource
class WinResource : IDisposable
{
  // private field that creates a wrapper for the unmanaged resource
  private UnmanagedResourceWrapper wrapper = null;
  // this is true if the object has been disposed of
  bool disposed = false;

  public WinResource(string someData)
  {
    // allocate the unmanaged resource here
    wrapper = new UnmanagedResourceWrapper(someData);
  }

  // a public method that clients call to work with the unmanaged resource
  public void DoSomething()
  {
    // throw if the object has been already disposed of
    if ( disposed )
      throw new ObjectDisposedException("");

    // this code can pass the wrapper.Handle value to API calls.
    // ...
  }

  public void Dispose()
  {
    // avoid issues when multiple threads call Dispose at the same time.
    lock ( this )
    {
      // do nothing if already disposed of
      if ( disposed )
        return;
      // dispose of all the disposable objects used by this instance
      // including the one that wraps the unmanaged resource
      // ...
      wrapper.Dispose();
      // remember this object has been disposed of
      disposed = true;
    }
  }
 
  // the nested private class that allocates and release the unmanaged resource
  private sealed class UnmanagedResourceWrapper : IDisposable
  {
    // an invalid handle value, that the wrapper class can use to check
    // whether the handle is valid
    public static readonly IntPtr InvalidHandle = new IntPtr(-1);

    // a public field, but accessible only from inside the WinResource class 
    public IntPtr Handle = InvalidHandle;

    // the constructor takes some data and allocates the unmanaged resource (eg a file)
    public UnmanagedResourceWrapper(string someData)
    {
      // this is just a demo...
      this.Handle = new IntPtr(12345);
    }

    // the Dispose method can be invoked only by WinResource class
    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    // the finalizer
    ~UnmanagedResourceWrapper()
    {
      Dispose(false);
    }

    // This is where the unmanaged resource is actually disposed of.
    // Notice that it takes an argument only for compliance with .NET coding standards
    // but the disposing argument is never used, because in all cases this class
    // can access and release only the single unmanaged resource it wraps.
    private void Dispose(bool disposing)
    {
      // exit now if this object didn't completed its constructor correctly
      if ( this.Handle == InvalidHandle )
        return;
  
      // release the unmanaged resource 
      // eg. CloseHandle(Handle);
      
      // finally, invalidate the handle
      this.Handle = InvalidHandle;
    }
  }
}

Notice that, if the unmanaged resource must interact with other fields, this interaction should be taken care of inside the WinResource class, not in the nested class. The UnmanagedResourceWrapper works only as a wrapper for the handle and shouldn't contain other fields or methods, besides those shown in the above listing. The code in the WinResource class must coordinate all the resources being used, both managed and unmanaged ones, and must release all of them in its Dispose method. But if the client code omits to call the Dispose method, the destructor in the nested class will orderly release the unmanaged resource during the next garbage collection.

Let's see all the advantages of this simplified approach.

  • The requirement that you shouldn't access reference fields from inside the Finalize method is automatically satisfied, because the only field of the UnmanagedResourceWrapper type is a handle (a value type).
  • If the client code omits to invoke the WinResource.Dispose method before the WinResource object goes out of scope, the WinResource object is removed from the heap anyway at the first GC; only the few bytes used by the UnmanagedResourceWrapper object survive in the heap and will be promoted to generation 1 or 2. Therefore this technique is more efficient than writing a single finalizable object that allocates both managed and unmanaged resource.
  • The UnmanagedResourceWrapper class is private and you can't inherit from it, therefore you can mark it as sealed. This means that you never have to worry about the Dispose/Finalize pattern in derived classes - a topic on which tons of digital ink has been spilled. It is possible to inherit from WinResource as you'd do with disposable class, therefore there are no limitations in this respect. (It's exactly like when you inherit from other disposable classes such as FileStream.)
  • The UnmanagedResourceWrapper is private and nested in another type and it isn't possible to achieve a refernence to one of its instances; therefore, a client can't "resurrect" a UnmanagedResourceWrapper object during the finalization step, a technique that is rarely useful and often dangerous. (Even though I show in my Programming Visual Basic .NET book how you can use it to implement an object pool.)
  • The UnmanagedResourceWrapper constructor performs a single "atomic" action; if this action fails, the value of the handle is still qual to InvalidHandle, therefore the code in the Finalize method can detect this special value and do nothing in that case. There are only two cases: either the unmanaged resource has been correctly allocated or an exception prevented it from being created, and you don't have to worry about an object that has been built only partially because of an exception in its constructor.
  • Many other recommendations related to the Dispose/Finalize pattern become void, such as the one that dictates that you should neither write finalizers inside structures nor calling virtual methods from inside the finalizer. In fact, the UnmanagedResourceWrapper class is sealed and has no virtual methods. Nor do you have to worry about versioning issues.
  • Another advantage: the UnmanagedResourceWrapper class is so simple and generic that you can ofter reuse it as-is (or with minor edits) inside other classes, by means of a plain copy-and-paste action. Being a nested class, you don't even need to change its name to avoid name collisions.

I am sure that in some cases this simplified pattern can't be used, though it always worked well in my applications. I believe that it's quite odd that this simplified approach is rarely mentioned in articles and books on this topic.


Technical matters aside, I think that another kind of consideration about the Dispose/Finalize pattern is in order.

In my opinion, it is essential to put a lot of emphasis on the fact that the Dispose/Finalize pattern should be used only when your type invokes unmanaged code that allocates unmanaged resources (including unmanaged memory) and returns an handle that you must use eventually to release the resource. If the unmanaged resource is already wrapped by a .NET object (e.g. a FileStream or a SqlConnection) or a COM object, the .NET class that uses the resource must implement IDisposable, not the finalizer. And you must implement the Finalize method only if your code assigns the handle to a class-level field. If the handle is assigned to a local variable and the unmanaged resource is released before exiting the method - possibly in the Finally section of a Try block - you don't even have to implement the IDisposable interface. One of the few exceptions to this rule is when your managed code allocates unmanaged memory directly, by means of the System.Runtime.InteropServices.Marshal type.

I talked to many developers who believe that, in doubt, they should always implement the Finalize method, just in case. This is a common mistake. Defining a finalizable class without a real reason to do so can hurt performance, because the CLR takes slightly longer to allocate finalizable objects, because it has to register them in the f-reachable queue. And in the worst case - that is if the caller omits to call to the Dispose method - a finalizable object can have even more impact on performance, because it will be promoted to a higher generation without any real reason for such an overhead.

To recap: when do you really need to implement the Finalize method? Thinking of all the commercial apps I worked on in these years, I'd say that I used this pattern no more than 4 or 5 times. For sure, I used it more frequently in books and articles than in the real world. :-)

11/25/2005 10:37:38 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, November 16, 2005

I am reviewing the chapter devoted to the My Namespace and I am adding a few details that I overlooked in the first draft, such as the creation of custom setting providers (to save settings on a medium other than the configuration file, e.g. a database) and the ability to bind a control's property to a user sertting. Custom setting providers are relatively complex and are of interest for a relatively small number of users, whereas setting binding is a simpler topic that will surely draw the attention of any developer working with Windows Forms applications.

I found many articles and posts on the My.Settings object (VB) and Settings object (C#), but most of them omit to emphasize the ability to bind a user setting to a form property, such as the Size and Location properties. This feature enables you - among the many things - to restore the size and position of a form from a previous session. The .NET infrastructure automatically assigns these properties when the form is loaded and save them when the form is resized or moved.

Figure 1. Visual Studio 2005 enables you to define user-level and application-level settings

Figure 2. How to bind a property to a user-level setting.

The great thing of this technique is that you don't need to write a single line of code. In fact, you just need to define one or more settings in the Settings page of the My Project designer (Visual Basic) or Properties designer (C#), for example the MainFormLocation and MainFormSize settings (see Figure 1). It is crucial that these settings are defined as user settings, because application-level settings are read-only. Next, you can select the form, switch to the Properties window, open the (Application Settings) section, click on the arrow near the ClientSize and Location properties, and select the user setting you want to bind the property to. If you haven't created the user setting yet, you can do it now by clicking on the New element. (See Figure 2.)

As I already noted, the noteworthy detail is that these settings are automatically updated when the end user moves or resizes the form. You can bind other properties, for example Text, BackColor, etc. If you perform this action for all the forms in the application, you can implement a simple yet powerful persistance mechanism for all user's preferences, again without writing code!

Obviously, you can extend this mechanism to properties of individual controls. Not all properties can notify to the world that they have been modified, though. More precisely, the control that exposes the property must implement the IBindableComponent interface and must expose an event named XxxxChanged for each property, or it must implement the INotifyPropertyChanged interface (new in .NET 2.0). Most Windows Forms controls, but not all of them, implement these interfaces. For example, the ToolStripItem control doesn't implement it. In this case, the property is assigned correctly when the form loads, but you must update the corresponding user setting via code.

 

11/16/2005 8:57:59 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [3]  | 
 Monday, October 31, 2005

VB.NET and C# compilers manage string constants in a rather smart way: all strings with same value are stored in a common area known as string intern pool. The following code snippet shows this compiler feature in action:

' VB.NET
Dim s1 As String = "ABCDE"
Dim s2 As String = "ABC" & "DE"
' Prove that s1 and s2 point to the same element in the intern pool
Console.WriteLine(s1 Is s2) ' => True

// C#
string s1 = "ABCDE";
string s2 = "ABC" + "DE";
// Prove that s1 and s2 point to the same element in the intern pool
Console.WriteLine(String.ReferenceEquals(s1, s2)); // => True

This optimization technique doesn't really have any impact on the amount of memory used by most client applications, but it makes a difference if used inside types that are instantiated thousand times, as it often happens in server applications. The problem is, this optimization is applied only to string constants, not to strings built at runtime:

' VB.NET ...continuing previous example...
Dim s3 As String = "ABC"
s3 &= "DE"
' s1 and s3 contain the same value but point to a different string
Console.WriteLine(s1 = s3) ' => True
Console.WriteLine(s1 Is s3) ' => False

// C# ... continuing previous example...
string s3 "ABC";
s3 += "DE";
Console.WriteLine(s1 == s3) // => True
Console.WriteLine(String.ReferenceEquals(s1, s3) // => False

Now, let's suppose you have a component in the data tier and this component contains the the connection string for the database. This connection string is read from somewhere - typically the configuration file - when it's time to open the connection, therefore the compiler can't store the string in the intern pool. If this component is instantiated N times, there will be N copies of the same string in memory, which clearly is a waste if the string is long and N is high. There are two ways to avoid this waste, depending on how the connection string can vary.

If the connection string is guaranteed to be the same for all the instances, then you can store it in a static variable (a Shared variable in VB), so that the string is shared among all the instances of the component. This is the simplest case and I assume you know how to implement it, so let's move to the more interesting situation.

If the connection string can vary - for example, if the data component can connect to two or more different databases or if the connection string can use different login information - you can't store it in a static field. In this case you can resort to a technique based on the String.Intern method. This method receives a string argument and searches the argument in the intern pool: if the search is successful, the method returns a pointer to the existing string in the pool; if the search fails, the method inserts the string in the pool and returns a pointer to the element just added. Here's how you might implement the ConnectionString property in the hypothetical data component to better leverage the intern pool:

' VB.NET
Dim m_ConnectionString As String

Property ConnectionString() As String
   Get
      Return m_ConnectionString
   End Get
   Set(ByVal Value As String)
      m_ConnectionString = String.Intern(Value)
   End Set
End Property
 

// C#
private string m_ConnectionString;

public string ConnectionString
{
  get { return m_ConnectionString; }
  set { m_ConnectionString = String.Intern(value);}
}

The first time a given value is assigned to the ConnectionString property, the search in the pool fails, the String.Intern method adds the string in the pool and returns a pointer to the new pool element. If the same connection string is eventualy assigned to a different instance of the data component, the String.Intern pool returns a pointer to the element already in the pool and doesn't create any duplicate. The total amount of memory that the application uses is reduced and so is the number of garbage collections that occur during the application's lifetime.

10/31/2005 6:14:23 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Saturday, October 29, 2005

First of all, some background on delegate covariance and contravariance in C# 2.0. Let's suppose you have the following delegate definition:

delegate object GetControlData(TextBox ctrl);

Thanks to delegate covariance, this delegate can point to a method whose return value inherits from the delegate's return type. For example, covariance enables a GetControlData delegate to point to any method that takes a TextBox argument, regardless of its return value, because all .NET types inherit from System.Object. The only condition is that the method actually returns something, therefore the delegate can't point to a void method. For example, you can create a GetControlData delegate that points to the following method, because the String type inherits from Object:

string GetText(TextBox ctrl) 
{ return ctrl.Text; }

Delegate contravariance lets you create a delegate that points to a method whose argument is the base type of the argument that appears in the delegate's signature. In previous example, contravariance enables you to create a GetControlData delegate that points to a method that accepts a Control or Object value, because these types are both base types for the TextBox class specified in the delegate signature. For example, the GetControlData delegate can point to this method:

object GetTag(Control ctrl) 
{ return ctrl.Tag; }

It should be evident that covariance and contravariance don't impact code robustness and can't cause type mismatch errors at runtime. Obviously, covariance and contravariance can be combined, thus a GetControlData delegate can point to the following method:

string GetText(Control ctrl) 
{ return ctrl.Text; }

It's important to notice that - even if the target method accepts a generic Control instance - if you call this method through the delegate, you must pass a TextBox argument, because this is the type that appears in the delegate's signature.

You can find more details on what I've explained so far virtually anywhere on the 'Net, with many more examples. It's time to go back to the main reason for this post.

In case you wondered why I used C# for all the examples, here's the reason: Visual Basic 2005 supports neither covariance nor contravariance.

Is that absolutely correct? Well, yes and no. It's true because VB 2005 doesn't support these feature natively, but you can leverage them all the same. It isn't immediately apparent that covariance and contravariance are supported at the .NET Framework level. In other words, they are a feature of .NET 2.0 delegates, not just C# 2.0. In fact, in .NET 2.0 it is possible to use reflection to create a delegate that has both these properties. Here's how you can proceed if you code with Visual Basic 2005. Say you have the following delegate definition and the following method inside a Windows Form class:

Delegate Function GetControlData(ByVal ctrl As TextBox) As Object

Function GetText(ByVal ctrl As Control) As String
   Return ctrl.Text
End Function

VB 2005 doesn't support covariance and controvariance natively, therefore it isn't possible to create a GetControlData instance that points to the GetText method using pure VB code. However, you can get there anyway via reflection, by creating a MethodInfo object that points to the target method and then passing this MethodInfo object to the Delegate.CreateDelegate static method:

' the target method
Dim targetMethod As MethodInfo = Me.GetType().GetMethod("GetText")
' build the delegate through reflection
Dim deleg As GetControlData = DirectCast([Delegate].CreateDelegate( _
   GetType(GetControlData), Me, targetMethod), GetControlData)
' show that the delegate works correctly
Console.WriteLine(deleg(Me.TextBox1))

This code is only slightly slower than the direct creation of the delegate (that you can implement only in C# 2.0), but this is hardly a serious issue, because you typically create a delegate once and use it many times. Another minor problem is that this code can fail at runtime if the method name is mistyped, but on the other hand you would spot this bug the very first time you run the code.

NOTE: I haven't tested this code against the RTM version yet, but under the RC release I found the .NET support for covariance and contravariance isn't perfect. In fact, not all the overloads of the Delegate.CreateDelegate method support this feature. For example, the overload that takes the name of the target method (instead of the MethodInfo that points to the method) causes a runtime error (ArgumentException: Error binding to target method) if you attempt to create a delegate whose signature doesn't match perfectly the target method's signature.

10/29/2005 5:47:43 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 
Get RSS/Atom Feed
RSS 2.0 | Atom 1.0
Search in the blog
Archive
<May 2008>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
Categories