Francesco's blog

 Friday, January 11, 2008

Many readers asked whether I was about to release a book about VB 2008 and, more in general, how come I have reduced my writing activity so dramatically. The answer is in this post.

Immediately after publishing my C# 2005 book I started working on a very ambitious software project. Now, after exactly two years of hard work, VB Migration Partner is in beta test stage and is quickly approaching the public release.

The name of this tool says it all: in a nutshell, it is a converter of VB6 applications to VB.NET. I won’t waste your time trying to emphasize how weak the Upgrade Wizard that comes with Visual Studio is: if you’ve never tried it out, just google for “vb6 migration” and read what developers and bloggers have to say about it. Our goal at Code Architects was to build a better mouse trap than that.

A word on the migration-vs-rewriting debate: Many industry experts suggest that you should rewrite your VB6 from scratch rather than trying to port it to .NET automatically using a conversion tool, because the two environments are just too different. In theory this argument makes a lot of sense; in practice, however, when you have a real-world business app with some hundreds thousands lines of code – or even millions of lines! – a rewrite from scratch is simply too expensive, especially when the developers who wrote it originally aren’t working with the company any longer, the documentation is poor, remarks are scarce, etc. When facing the perspective of investing many years/man in this daunting task, it’s no surprise that many companies have postponed the migration to “some other time.” Now they are realizing, however, that the migration step can’t be postponed any longer, either because a few VB6 apps don’t work under Microsoft Vista – possibly because they use 3rd-party controls that are incompatible with the new Microsoft operating system – or, above all, because Microsoft declared that VB6 support ends on March 2008.

The problem is, automatic code converters don’t have a good reputation and rarely keep their promises, regardless of the language you are migrating from/to. In most cases, they manage to convert 95% of existing code, or less. With a 100,000 line application – a medium-size application of average complexity, then – having to manually fix the remaining 5% means that you have to fix about five thousand lines. It doesn’t sound too bad, until you realize that most of these “fixes” might take hours or even days. For example, consider that VB6 and VB.NET have two vastly different (and incompatible) programming models for drag-and-drop. Therefore one OLEDrag method counts as an individual problematic statement, yet it forces you to rewrite a large portion of your code. Drag-and-drop isn’t the only example, because the same concept applies to graphic statements, printing, data-binding, object finalization, component initialization, and many others.

We tested our VB Migration Partner on hundreds of VB6 apps – for well over one million lines – and found out that it delivers code that compiles and runs correctly most of the times, with an average of one compilation error every 1,100 lines of code. This corresponds to an accuracy higher than 99.9%. Not bad, uh? Of course the actual accuracy depends on how well the VB6 code is and on the features that the application uses, thus you can get a better idea of how good our tool is by having a look at the VB6 features that itsupports ...and that are out of reach for most other conversion tool on the market:

  • arrays with nonzero LBound
  • Gosub, On...Goto, and On ...Gosub
  • auto-instancing variables (Dim x As New Person), which keep their lazy-instantiation feature even under VB.NET
  • As Any parameters and callback addresses in Declare parameters (e.g. EnumWindows)
  • default properties resolved correctly even for late-bound variables
  • deterministic finalization for objects such as Connection and Recordset, to ensure that the connection is correctly closed when the variable is set to Nothing or the current scope is exited
  • (limited) support for Variant variable and null propagation in expressions
  • all 60+ controls in the VB6 toolbox, with the only exceptions of OLE container and Repeater, plus the support for common ActiveX controls and components such as WebBrowser, ScriptControl, Scripting runtime, and the MSWLess library
  • control arrays, including arrays of 3-rd party controls
  • popup menus
  • the Controls.Add method and the VBControlExtender object, to dynamically create data-entry forms
  • graphic methods (Line, Circle, PSet, PaintPicture, etc.), with support for any ScaleMode, including custom user modes
  • the Printer object and the Printers collection
  • OLE drag-and-drop
  • UserControl classes, with support for advanced features such as the Ambient and Extender objects
  • data-binding to DAO, RDO, and ADO Data controls, ADO recordsets, DataEnvironment objects
  • non-hierarchical DataEnvironment objects
  • ADO data source and simple data consumer classes and user controls (e.g. custom ADO Data controls)
  • MultiUse, PublicNotCreatable, GlobalMultiUse classes
  • persistable classes, MTS/COM+ classes

The number of supported features is so high that it is simpler to list the features that are not supported: UserDocument, PropertyPage, WebClass and DHTML Page components; undocumented methods (VarPtr, ObjPtr, StrPtr); “classic” drag-and-drop, and little else.

VB Migration Partner can convert an entire VB6 project group in one operation, in which case it typically delivers better quality code because it can see “inside” a DLL that belongs to the group. The tool also includes a very sophisticated code analyzer that can spot unused members: a large project that has evolved over many years can easily include 5-10% of “dead code”, thus this feature alone can save you a lot of precious time because it allows you to focus on just the code that is really important.

One of the secrets for such high error-free conversion factor is the support for migration pragmas. A migration pragma is a special remark that you can add to the original VB6 code and that teaches VB Migration Partner how a given portion of code must be translated, if many alternatives exist. For example, the following ArrayBounds pragma tells VB Migration Partner that all the arrays in the current projects must have their lower bound index forced to zero:
          '##project:ArrayBounds ForceZero
Pragmas can have project, class, method, or variable scope. A pragma scoped at the variable- or method-level always have higher priority than pragmas scoped at the project- or class-level. For example, you can override the previous pragma inside a method, and then override this latter pragma for a given array variable:
          Sub Test()
             '## ArrayBounds Shift
             '## arr.ArrayBounds ForceZero
             '## names.ShiftIndexes 1
             Dim names(1 To 10) As String
             Dim values(1 To 10) As Long
             Dim arr(1 To 10) As Integer
             names(1) = "John": names(2) = "Ann"
             '...
          End Sub

The Shift option tells VB Migration Partner that the LBound and UBound indexes must be shifted towards zero, so that the total number of elements in the array is preserved. The ShiftIndexes pragma –applied only to the names array – ensures that constant indexes are correctly shifted too:
          Sub Test()
             Dim names(9) As String
             Dim values(9) As Integer
             Dim arr(0 To 10) As Short
             names(0) = "John": names(1) = "Ann"
          End Sub

VB Migration Partner supports over 50 pragmas. For example, the SetName pragma changes the name of a control or variable during the migration, SetType changes its type, AutoNew implements the auto-instancing behavior of As New variables, AutoDispose implements the deterministic finalization when the variable is set to Nothing or the current scope is exited, and so forth.

One of the main defects of a “traditional” conversion tool is that – after the first migration – there is no relationship between the original VB6 project and the new VB.NET project. If the migration process takes weeks or months – which is common for large projects – and if the VB6 application must evolve in the meantime with new features and bug fixing, then at the end of the migration process the VB.NET code is already outdated and must be manually patched. The margin for mistakes in this phase is very high.

VB Migration Partner allows you to work around this issue, by means of pragmas and what we call the convert-text-fix cycle methodology. In a nutshell, you can iteratively migrate a given VB6 project by reaching the zero compilation errors stage and then the zero runtime error stage by simply inserting pragmas in the original project, but without modifying any VB6 executable statement. Thanks to the convert-test-fix cycle you can face complex migration tasks and keep the VB6 and VB.NET versions of your app always in sync.

I am especially proud of VB Migration Partner’s refactoring engine, which is able to apply many optimization techniques the result of the “raw” conversion. For example, it can merge variable declarations and the initializations, to generate Return statements, and to leverage the compound assignment operators (e.g. +=), and more:

    Function GetValue() As Long     =>     Function GetValue() As Integer
       Dim x As Long                          Dim x As Integer = 123
       x = 123                                ...
       ...                                    If x > 0 Then
       If x > 0 Then                             Return x
          GetValue = x                        ElseIf x = 0 Then
          Exit Function                          Return -1
       ElseIf x = 0 Then                      End If
          GetValue = -1                       ...
          Exit Function                       x += 1
       End If                                 ...
       ...                                 End Function
       x = x + 1
       ...
    End Function

Other refactoring techniques can be achieved by means of pragmas. For example, you can move the declaration of a variable inside a For or For Each method, as in this example:

    Dim i As Integer                =>     For i As Integer = 1 To 100
    For i = 1 To 100                          ...
       ...                                 Next
    Next

On top of the cake: in spite of all these features, VB Migration Partner is up to 8 times faster than the Upgrade Wizard that comes with Visual Studio!

You can learn all about VB Migration Partner on our new site www.vbmigration.com. The web site hosts a Resource section where we dissect each and every difference between VB6 and VB.NET – most of which have never been documented anywhere – thus you can find many useful tips even if you don’t plan to use our tool. I also opened a new blog, entirely devoted to migration techniques.

That's it for now. Stay tuned!

1/11/2008 11:00:33 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [4]  | 
 Saturday, December 22, 2007

When you migrate code from VB6 - regardless of whether you are using an automatic conversion tool - chances are that string-intensive code will actually run slower under VB.NET, if it uses a lot of string concatenation operations. For example, this code takes 2.8 seconds when it runs in VB6 and 27 seconds after its conversion to VB.NET on my 3GHz system:

Dim s As String = ""
For i As Integer = 1 To 100000
    s = s + "*"
Next

The solution is of course trivial: just replace the string variable with a StringBuilder object. Alas, this fix requires that you completely revise your source code, because you need to replace all + and & operators with the Append method, not to mention cases where the StringBuilder is used as an argument to string functions such as Trim or Len.

Is there a way to speed up the previous code with a minimal impact on the code itself? The answer is yes and the solution is actually very simple: you just need to create a VB.NET class that is based on the System.Text.StringBuilder object, that redifines the + and & operators, and that supports the implicit conversion to and from the System.String type. Authoring such a StringBuilder6 class is a matter of minutes:

' a wrapper for the StringBuilder object, with support for + and & operators

Public Class StringBuilder6

    Private buffer As New System.Text.StringBuilder

    ' return the inner string

    Public Overrides Function ToString() As String
       
Return buffer.ToString()
    End Function

    Public Shared Operator +(ByVal op1 As StringBuilder6, ByVal op2 As String) As StringBuilder6
        op1.buffer.Append(op2)
       
Return op1
    End Operator

    Public Shared Operator &(ByVal op1 As StringBuilder6, ByVal op2 As String) As StringBuilder6
        op1.buffer.Append(op2)
        Return op1
    End Operator

    ' convert to string

    Public Shared Widening Operator CType(ByVal op As StringBuilder6) As String
       
Return op.ToString()
    End Operator

    ' convert from string

    Public Shared Widening Operator CType(ByVal str As String) As StringBuilder6
        Dim op As New StringBuilder6()
        op.buffer.Append(str)
        Return op
    End Operator

End Class

Once the StringBuilder6 class is in place, you just need to replace the type of the String variable:

Dim s As StringBuilder6 = ""

After this edit, the loop runs in 0.008 seconds, that is about 2000 times faster!!! Not bad, for such a simple fix :-)

Regardless of whether you are migrating code from VB6 or you've written VB.NET or C# code from scratch, the StringBuilder class gives you a quick and simple way to check whether your string concatenations can be optimized by resorting to a StringBuilder.

 

12/22/2007 11:40:06 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 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]  | 
 Tuesday, August 29, 2006

I know, I know, there are soooo many regular expression tester tools available on the 'Net, but I couldn't help creating my own. It's very simple, yet it supports all the basic features you'd espect from a tool of its kind, including code generation (VB and C#) and compilation to stand-alone assemblies. Best of all, it comes with source code. You can download it from the home page of my Visual Basic 2005 book (together with all other code samples in the book) or directly from here:

Executable (requires .NET Framework 2.0): RegexTester.zip (75.57 KB)
Source code (VB2005): RegexTester source.zip (88.15 KB)

Using the tool is quite simple. The main window is divided in three panes: (a) the pane where you enter the regex, (b) the pane where you load the text the regex must be applied to, (c) the result pane. A fourth pane appears when you select the Replace item from the Commands menu, and it's where you enter the replace pattern. As you can see in the image below, you can enter most regex patterns by selecting them from the context menu:

You select the kind of command (Find, replace, Split) from the Commands menu and you select one or more regex options from the Options menu:

After selecting the proper options, you press the F5 key (the Run item in the Commands menu) to execute the regex. Results are displayed in the bottom pane in a variety of formats and sort orders, which you can select from the Results menu, and the status bar displays the number of matches, the execution time, and the properties of the result currently highlighted in the result pane:

Alternatively, you can set all these options from the Properties dialog box (the Properties command in the File menu, or just press the F4 key):

Assigning a name to the current regex is important because you can save it on disk in a file with .regex extension, for later retrieval.

The Commands menu contains a couple of other interesting items. First, you can generate the C# or VB code for the current regular expression and copy it to the Clipboard:

Second, and more interesting, you can compile one or more regular expressions (including saved .regex projects) into a compiled assembly, which you can later reference from any .NET application. Using such compiled regexes is obviously faster than defining them in code, because you skip the parsing step:

 

That's it. You can use the YART tool for your own use, study its source code, modify and expands it as you like. If you find any major problems or add some noteworthy feature, just let me know.

C# | Regex | Tools | Visual Basic
8/29/2006 8:00:02 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
 Saturday, July 01, 2006

My new C# book has been released a few months ago, but I was so busy with my everyday activities that I didn't even blogged about it. Instead of going to the beach, today I sat down a prepare a home page for the book, from where you can download two sample chapters: Chapter 5 "Arrays and Collection" and Chapter 10, "Custom Attributes", as well as all the code samples and the errata doc.

Books | C#
7/1/2006 10:35:25 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [4]  | 

At last I found the time to sort all the notes from readers, pointing at typos and mistakes in my VB2005 book, and to update the errata document that you can download from the book's home page. I have clearly marked the additions with a "New" marker. Most of these notes are typos that don't affect how code works, with the notable exception of the fix to the Evaluate function in Chapter 16 (regular expression).

My grat thanks to all the readers who took the time to write me to inform of the mistakes they found, with a special mention to Dan Karmann, who actually methodically proof-read the book and found about twenty of them.

 

7/1/2006 9:01:26 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
 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]  | 
 Wednesday, May 03, 2006

Yesterday I got an email from reader Claudio Fontana, with the following, deceiptively simple request: how can you avoid flickering while updating many controls on a form? The problem is especially serious when you need to add thousands of items to a ListView or a TreeView.

In VB6 this problem can be solved quite simply by temporarily setting the Visible (or Enabled) property to False for all the controls about to be updated: the control isn't actually hidden, yet the result of the update operation appears istantaneously when the property is reset to True. Just as interesting: the update operation is carried out much faster if the control is invisible, often twice as faster. Alas, this trick doesn't work in .NET, because as soon as you set the Visible property to False the control is immediately hidden. It's necessary to find another solution.

A few Windows Forms controls - namely the ListBox, ComboBox, ListView, and TreeView controls - do expose the BeginUpdate and EndUpdate methods, which allow you to "freeze" the control while you add items to it. Not only do they solve the flickering problem, they also speed up the update operation, tipically by a factor of 2.5x. However, if your form contains many controls that do NOT expose these methods, you must devise something else, and this was exactly the problem that Claudio submitted, after he unsuccessfully googled around on the 'Net looking for a solution.

The problem is quite intriguing, thus I decided to spend some time on it, until I came to the following solution. The idea is simple, and can be split in the following steps: (1) take a snapshot of the current form's appearance, by making a pixel-by-pixel copy into a bitmap, (2) create a PictureBox control as large as the form, and load the bitmap into the PictureBox, (3) add the PictureBox to the form's Controls collection and bring the PictureBox in front of all other controls, (4) while the user looks at the "frozen" image of the form, update your controls, using the BeginUpdate/EndUpdate mthods if possible to speed up execution, (5) when the update operation is completed, remove the PictureBox from the Controls collection, so that the user can now see the real form.

You just need one dozen statements to implement this algorithim, but I prepared a class to make the code more reusable and to ensure that it releases all resources correctly:

Public Class FormFreezer
  
Implements IDisposable

   ' The form being frozen
   Dim Form As Form
   ' the auxiliary PictureBox that will cover the form
   Dim PictureBox As PictureBox
   ' the number of times the Freeze method has been called
   Dim FreezeCount As Integer = 0

   ' create an instance associated with a given form
   ' and optionally freeze the form right away
   Public Sub New(ByVal form As Form, Optional ByVal freezeIt As Boolean = False)
      Me.Form = form
      If freezeIt Then Me.Freeze()
   End Sub

   ' freeze the form 
   Public Sub Freeze()
      ' Remember we have frozen the form once more
      FreezeCount += 1
      ' Do nothing if it was already frozen
      If FreezeCount > 1 Then Exit Sub

      ' Create a PictureBox that resizes with its contents
      PictureBox = New PictureBox()
      PictureBox.SizeMode = PictureBoxSizeMode.AutoSize
      ' create a bitmap as large as the form's client area and with same color depth
      Dim frmGraphics As Graphics = Form.CreateGraphics()
      Dim rect As Rectangle = Form.ClientRectangle
      PictureBox.Image = New Bitmap(rect.Width, rect.Height, frmGraphics)
      frmGraphics.Dispose()

      ' copy the screen contents, from the form's client area to the hidden bitmap
      Dim picGraphics As Graphics = Graphics.FromImage(PictureBox.Image)
      picGraphics.CopyFromScreen(Form.PointToScreen(New Point(rect.Left, rect.Top)), New Point(0, 0), New Size(rect.Width, rect.Height))
      picGraphics.Dispose()

      ' Display the bitmap in the picture box, and show the picture box in front of all other controls
      Form.Controls.Add(PictureBox)
      PictureBox.BringToFront()
   End Sub

   ' unfreeze the form
   ' Note: calls to Freeze and Unfreeze must be balanced, unless force=true 
   Public Sub Unfreeze(Optional ByVal force As Boolean = False)
      ' exit if nothing to unfreeze
      If FreezeCount = 0 Then Exit Sub
      ' remember we've unfrozen the form, but exit if it is still frozen
      FreezeCount -= 1
      ' force the unfreeze if so required
      If force Then FreezeCount = 0
      If FreezeCount > 0 Then Exit Sub

      ' remove the picture box control and clean up
      Form.Controls.Remove(PictureBox)
      PictureBox.Dispose()
      PictureBox = Nothing
   End Sub

   ' return true if the form is currently frozen
   Public ReadOnly Property IsFrozen() As Boolean
      Get
         Return FreezeCount > 0
      End Get
   End Property

   ' ensure that resources are cleaned up correctly
   Public Overridable Sub Dispose() Implements IDisposable.Dispose
      Me.Unfreeze(True)
   End Sub
End
Class

This is the C# version, translated from VB by Claudio:

public class FormFreezer: IDisposable
{

   // The form being frozen

   Form form;

   // the auxiliary PictureBox that will cover the form

   PictureBox pictureBox;

   // the number of times the Freeze method has been called

   int FreezeCount = 0;

 

   // create an instance associated with a given form

   // and freeze the form in base of flag freezeIt

   public FormFreezer(Form form, bool freezeIt)
   {

      this.form = form;

      if (freezeIt) this.Freeze();

   }

 

   // freeze the form 

   public void Freeze()
   {

      // Remember we have frozen the form once more

      // Do nothing if it was already frozen

      if (++FreezeCount > 1) 
         return;

      // Create a PictureBox that resizes with its contents

      pictureBox = new PictureBox();

      pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;

      

      // create a bitmap as large as the form's client area and with same color depth

      Graphics frmGraphics = form.CreateGraphics();

      Rectangle rect = form.ClientRectangle;

      pictureBox.Image = new Bitmap(rect.Width, rect.Height, frmGraphics);

      frmGraphics.Dispose();

 

      // copy the screen contents, from the form's client area to the hidden bitmap

      Graphics picGraphics = Graphics.FromImage(pictureBox.Image);

      picGraphics.CopyFromScreen(form.PointToScreen(new Point(rect.Left, rect.Top)), new Point(0, 0), new Size(rect.Width, rect.Height));

      picGraphics.Dispose();

 

      // Display the bitmap in the picture box, and show the picture box in front of all other controls

      form.Controls.Add(pictureBox);

      pictureBox.BringToFront();

   }

 

   // unfreeze the form

   // Note: calls to Freeze and Unfreeze must be balanced, unless force=true

   public void Unfreeze(bool force)
   {

      // exit if nothing to unfreeze

      if ( FreezeCount == 0 ) 
         return ;

      // remember we've unfrozen the form, but exit if it is still frozen

      FreezeCount -= 1;

      // force the unfreeze if so required

      if (force) 
         FreezeCount = 0;

      if (FreezeCount > 0) 
         return;

      // remove the picture box control and clean up

      pictureBox.Controls.Remove(pictureBox);

      pictureBox.Dispose();

      pictureBox = null;

   }

 

   // return true if the form is currently frozen

   public bool IsFrozen
   {

      get { return (FreezeCount > 0); }

   }

 

   void IDisposable.Dispose()

   {

      this.Unfreeze(true);

   }

}

Using the FormFreezer class is quite simple. Here's a code sample, which assumes that it is located inside a form class so that the Me keyword points to the current form:

   Dim ff As New FormFreezer(Me, True)
   ' update controls here
  
' ...
  
ff.Unfreeze()

The class implements IDisposable, thus you can bracket the update code in a Using block, either in C# or in VB2005, and avoid an explicit call to Unfreeze:

   Using New FormFreezer(Me, True)
      ' Update controls here
     
' ...
  
End Using

Notice that calls to Freeze and Unfreeze must be balanced. If you call Freeze twice you then need two calls to Unfreeze to actually restore the updated form. This behavior allows you to call Freeze and then invoke a method that calls Freeze again and still have the code work correctly (provided that all methods use the same instance of the FormFreeze class).

5/3/2006 12:16:30 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [5]  | 
 Monday, May 01, 2006

Many, if not most, Windows Forms samples you can find on the 'net include one or more calls to unmanaged code in Windows DLLs, often in the form of calls to the SendMessage API methods to fix some of the (very few) missing