<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" version="2.0">
  <channel>
    <title>Francesco's blog</title>
    <link>http://www.dotnet2themax.com/blogs/fbalena/</link>
    <description />
    <language>en-us</language>
    <copyright>Francesco Balena</copyright>
    <lastBuildDate>Fri, 11 Jan 2008 23:00:33 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 1.8.5223.1</generator>
    <managingEditor>fbalena@codearchitects.com</managingEditor>
    <webMaster>fbalena@codearchitects.com</webMaster>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <table>
            <tbody>
              <tr>
                <td width="200">
                  <font size="2">
                    <a href="http://www.dotnet2themax.com/blogs/fbalena/www.vbmigration.com">
                      <img height="84" src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/vbmp_logo.jpg" width="142" border="0" />
                    </a>
                  </font>
                </td>
                <td>
                  <font size="2">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.</font>
                  <p>
                    <font size="2">Immediately after publishing my C# 2005 book I started working on a
                     very ambitious software project. Now, after exactly two years of hard work, </font>
                    <a href="http://www.vbmigration.com/">
                      <strong>
                        <font size="2">VB
                     Migration Partner </font>
                      </strong>
                    </a>
                    <font size="2">is in beta test stage and is
                     quickly approaching the public release.</font>
                  </p>
                </td>
              </tr>
              <p>
              </p>
              <p>
              </p>
              <p>
              </p>
            </tbody>
          </table>
        </p>
        <p>
      The name of this tool says it all: in a nutshell, <strong>it is a converter of VB6
      applications to VB.NET</strong>. 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.
   </p>
        <p>
          <strong>A word on the migration-vs-rewriting debate:</strong> 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 <a href="http://msdn2.microsoft.com/en-us/vbrun/ms788707.aspx">Microsoft
      declared that VB6 support ends on March 2008</a>.
   </p>
        <p>
      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.
   </p>
        <p>
      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 <strong>one compilation error every 1,100 lines of
      code</strong>. 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:
   </p>
        <ul>
          <li>
         arrays with nonzero LBound 
      </li>
          <li>
         Gosub, On...Goto, and On ...Gosub 
      </li>
          <li>
         auto-instancing variables (Dim x As New Person), which keep their lazy-instantiation
         feature even under VB.NET 
      </li>
          <li>
         As Any parameters and callback addresses in Declare parameters (e.g. EnumWindows) 
      </li>
          <li>
         default properties resolved correctly even for late-bound variables 
      </li>
          <li>
         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 
      </li>
          <li>
         (limited) support for Variant variable and null propagation in expressions 
      </li>
          <li>
         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 
      </li>
          <li>
         control arrays, including arrays of 3-rd party controls 
      </li>
          <li>
         popup menus 
      </li>
          <li>
         the Controls.Add method and the VBControlExtender object, to dynamically create data-entry
         forms 
      </li>
          <li>
         graphic methods (Line, Circle, PSet, PaintPicture, etc.), with support for any ScaleMode,
         including custom user modes 
      </li>
          <li>
         the Printer object and the Printers collection 
      </li>
          <li>
         OLE drag-and-drop 
      </li>
          <li>
         UserControl classes, with support for advanced features such as the Ambient and Extender
         objects 
      </li>
          <li>
         data-binding to DAO, RDO, and ADO Data controls, ADO recordsets, DataEnvironment objects 
      </li>
          <li>
         non-hierarchical DataEnvironment objects 
      </li>
          <li>
         ADO data source and simple data consumer classes and user controls (e.g. custom ADO
         Data controls) 
      </li>
          <li>
         MultiUse, PublicNotCreatable, GlobalMultiUse classes 
      </li>
          <li>
         persistable classes, MTS/COM+ classes</li>
        </ul>
        <p>
      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.
   </p>
        <p>
      VB Migration Partner can convert <strong>an entire VB6 project group in one operation</strong>,
      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 <strong>code
      analyzer </strong>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.
   </p>
        <p>
      One of the secrets for such high error-free conversion factor is the support for <strong>migration
      pragmas</strong>. 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:<br /><font face="Courier New">          '##project:ArrayBounds
      ForceZero</font><br />
      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:<br /><font face="Courier New">          Sub
      Test()<br />
                   '## ArrayBounds
      Shift<br />
                   '## arr.ArrayBounds
      ForceZero<br />
                   '## names.ShiftIndexes
      1<br />
                   Dim names(1
      To 10) As String<br />
                   Dim values(1
      To 10) As Long<br />
                   Dim arr(1
      To 10) As Integer<br />
                   names(1)
      = "John": names(2) = "Ann"<br />
                   '...<br />
                End Sub</font><br />
      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:<br /><font face="Courier New">          Sub
      Test()<br />
                   Dim names(9)
      As String<br />
                   Dim values(9)
      As Integer<br />
                   Dim arr(0
      To 10) As Short<br />
                   names(0)
      = "John": names(1) = "Ann"<br />
                End Sub</font><br />
      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.
   </p>
        <p>
      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.
   </p>
        <p>
      VB Migration Partner allows you to work around this issue, by means of pragmas and
      what we call <strong>the convert-text-fix cycle methodology</strong>. 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.
   </p>
        <p>
      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:
   </p>
        <p>
          <font face="Courier New">    Function GetValue() As Long    
      =&gt;     Function GetValue() As Integer<br />
             Dim x As Long                         
      Dim x As Integer = 123<br />
             x = 123                               
      ...<br />
             ...                                   
      If x &gt; 0 Then<br />
             If x &gt; 0 Then                            
      Return x<br />
                GetValue = x                       
      ElseIf x = 0 Then<br />
                Exit Function                         
      Return -1<br />
             ElseIf x = 0 Then                     
      End If<br />
                GetValue = -1                      
      ...<br />
                Exit Function                      
      x += 1<br />
             End If                                
      ...<br />
             ...                                
      End Function<br />
             x = x + 1<br />
             ...<br />
          End Function</font>
        </p>
        <p>
      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:
   </p>
        <p>
          <font face="Courier New">    Dim i As Integer               
      =&gt;     For i As Integer = 1 To 100<br />
          For i = 1 To 100                         
      ...<br />
             ...                                
      Next<br />
          Next</font>
        </p>
        <p>
      On top of the cake: in spite of all these features, VB Migration Partner is <strong>up
      to 8 times faster </strong>than the Upgrade Wizard that comes with Visual Studio! 
   </p>
        <p>
      You can learn all about VB Migration Partner on our new site <a href="http://www.vbmigration.com/">www.vbmigration.com</a>.
      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 <a href="http://www.vbmigration.com/blog">new
      blog</a>, entirely devoted to migration techniques. 
   </p>
        <p>
      That's it for now. Stay tuned!<br /></p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9" />
      </body>
      <title>Introducing VB Migration Partner</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9.aspx</link>
      <pubDate>Fri, 11 Jan 2008 23:00:33 GMT</pubDate>
      <description>&lt;p&gt;
   &lt;table&gt;
      &lt;tbody&gt;
         &lt;tr&gt;
            &lt;td width=200&gt;
               &lt;font size=2&gt;&lt;a href="http://www.dotnet2themax.com/blogs/fbalena/www.vbmigration.com"&gt;&lt;img height=84 src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/vbmp_logo.jpg" width=142 border=0&gt; &lt;/a&gt;&lt;/font&gt;&lt;/td&gt;
            &lt;td&gt;
               &lt;font size=2&gt;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.&lt;/font&gt; 
               &lt;p&gt;
                  &lt;font size=2&gt;Immediately after publishing my C# 2005 book I started working on a very
                  ambitious software project. Now, after exactly two years of hard work, &lt;/font&gt;&lt;a href="http://www.vbmigration.com/"&gt;&lt;strong&gt;&lt;font size=2&gt;VB
                  Migration Partner &lt;/font&gt;&lt;/strong&gt;&lt;/a&gt;&lt;font size=2&gt;is in beta test stage and is quickly
                  approaching the public release.&lt;/font&gt;
               &lt;/p&gt;
            &lt;/td&gt;
         &lt;p&gt;
         &lt;p&gt;
         &lt;/p&gt;
         &lt;p&gt;
         &lt;/p&gt;
         &gt;
      &lt;/tbody&gt;
   &lt;/table&gt;
&lt;p&gt;
   The name of this tool says it all: in a nutshell, &lt;strong&gt;it is a converter of VB6
   applications to VB.NET&lt;/strong&gt;. 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.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;A word on the migration-vs-rewriting debate:&lt;/strong&gt; 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 &lt;a href="http://msdn2.microsoft.com/en-us/vbrun/ms788707.aspx"&gt;Microsoft
   declared that VB6 support ends on March 2008&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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 &lt;strong&gt;one compilation error every 1,100 lines of
   code&lt;/strong&gt;. 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&amp;nbsp;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:
&lt;/p&gt;
&lt;ul&gt;
   &lt;li&gt;
      arrays with nonzero LBound 
   &lt;li&gt;
      Gosub, On...Goto, and On ...Gosub 
   &lt;li&gt;
      auto-instancing variables (Dim x As New Person), which keep their lazy-instantiation
      feature even under VB.NET 
   &lt;li&gt;
      As Any parameters and callback addresses in Declare parameters (e.g. EnumWindows) 
   &lt;li&gt;
      default properties resolved correctly even for late-bound variables 
   &lt;li&gt;
      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 
   &lt;li&gt;
      (limited) support for Variant variable and null propagation in expressions 
   &lt;li&gt;
      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 
   &lt;li&gt;
      control arrays, including arrays of 3-rd party controls 
   &lt;li&gt;
      popup menus 
   &lt;li&gt;
      the Controls.Add method and the VBControlExtender object, to dynamically create data-entry
      forms 
   &lt;li&gt;
      graphic methods (Line, Circle, PSet, PaintPicture, etc.), with support for any ScaleMode,
      including custom user modes 
   &lt;li&gt;
      the Printer object and the Printers collection 
   &lt;li&gt;
      OLE drag-and-drop 
   &lt;li&gt;
      UserControl classes, with support for advanced features such as the Ambient and Extender
      objects 
   &lt;li&gt;
      data-binding to DAO, RDO, and ADO Data controls, ADO recordsets, DataEnvironment objects 
   &lt;li&gt;
      non-hierarchical DataEnvironment objects 
   &lt;li&gt;
      ADO data source and simple data consumer classes and user controls (e.g. custom ADO
      Data controls) 
   &lt;li&gt;
      MultiUse, PublicNotCreatable, GlobalMultiUse classes 
   &lt;li&gt;
      persistable classes, MTS/COM+ classes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   VB Migration Partner can convert &lt;strong&gt;an entire VB6 project group in one operation&lt;/strong&gt;,
   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 &lt;strong&gt;code
   analyzer &lt;/strong&gt;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.
&lt;/p&gt;
&lt;p&gt;
   One of the secrets for such high error-free conversion factor is the support for&amp;nbsp;&lt;strong&gt;migration
   pragmas&lt;/strong&gt;. 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:&lt;br&gt;
   &lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '##project:ArrayBounds
   ForceZero&lt;/font&gt;
   &lt;br&gt;
   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:&lt;br&gt;
   &lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sub
   Test()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '## ArrayBounds
   Shift&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '## arr.ArrayBounds
   ForceZero&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '## names.ShiftIndexes
   1&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim names(1
   To 10) As String&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim values(1
   To 10) As Long&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim arr(1
   To 10) As Integer&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; names(1)
   = "John": names(2) = "Ann"&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End Sub&lt;/font&gt;
   &lt;br&gt;
   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:&lt;br&gt;
   &lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sub
   Test()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim names(9)
   As String&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim values(9)
   As Integer&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim arr(0
   To 10) As Short&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; names(0)
   = "John": names(1) = "Ann"&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End Sub&lt;/font&gt;
   &lt;br&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   VB Migration Partner allows you to work around this issue, by means of pragmas and
   what we call &lt;strong&gt;the convert-text-fix cycle methodology&lt;/strong&gt;. 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.
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Function GetValue() As Long&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   =&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Function GetValue() As Integer&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim x As Long&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   Dim x As Integer = 123&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; x = 123&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   ...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   If x &amp;gt; 0 Then&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; If x &amp;gt; 0 Then&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   Return x&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetValue = x&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   ElseIf x = 0 Then&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; Exit Function&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   Return -1&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ElseIf x = 0 Then&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   End If&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; GetValue = -1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   ...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Exit Function&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   x += 1&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; End If&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   ...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   End Function&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; x = x + 1&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; End Function&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;font face="Courier New"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim i As Integer&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   =&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; For i As Integer = 1 To 100&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; For i = 1 To 100&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   ...&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ...&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
   Next&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; Next&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
   On top of the cake: in spite of all these features, VB Migration Partner is &lt;strong&gt;up
   to 8 times faster &lt;/strong&gt;than the Upgrade Wizard that comes with Visual Studio! 
&lt;/p&gt;
&lt;p&gt;
   You can learn all about VB Migration Partner on our new site &lt;a href="http://www.vbmigration.com/"&gt;www.vbmigration.com&lt;/a&gt;.
   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 &lt;a href="http://www.vbmigration.com/blog"&gt;new
   blog&lt;/a&gt;, entirely devoted to migration techniques. 
&lt;/p&gt;
&lt;p&gt;
   That's it for now. Stay tuned!&lt;br&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,2dfe27ee-fe6a-49c5-8e4b-6fa4caa47bc9.aspx</comments>
      <category>Migrating from VB6;Tools</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=ce96d1dc-838b-424d-b571-282e028e9e32</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,ce96d1dc-838b-424d-b571-282e028e9e32.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,ce96d1dc-838b-424d-b571-282e028e9e32.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=ce96d1dc-838b-424d-b571-282e028e9e32</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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:
   </p>
        <p>
          <font color="#0000ff">Dim</font> s <font color="#0000ff">As</font> <font color="#0000ff">String</font> = <font color="#800000">""<br /></font><font color="#0000ff">For</font> i <font color="#0000ff">As</font><font color="#0000ff">Integer</font> =
      1 <font color="#0000ff">To</font> 100000<br />
          s = s + <font color="#800000">"*"<br /></font><font color="#0000ff">Next<br /></font></p>
        <p>
      The solution is of course trivial: just replace the string variable with a <strong>StringBuilder </strong>object.
      Alas, this fix requires that you completely revise your source code, because you need
      to replace all + and &amp; 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.
   </p>
        <p>
      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 &amp; operators, and that supports the implicit conversion to and from the
      System.String type. Authoring such a <strong>StringBuilder6 </strong>class is a matter
      of minutes:
   </p>
        <font color="#008000">
          <p>
      ' a wrapper for the StringBuilder object, with support for + and &amp; operators
   </p>
        </font>
        <font color="#0000ff">
          <p>
      Public
   </p>
        </font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Class</font>
        <font color="#000000"> StringBuilder6</font>
        <p>
          <font color="#0000ff">    Private</font> buffer <font color="#0000ff">As</font><font color="#0000ff">New</font><font color="#000000">System.Text.</font>StringBuilder
   </p>
        <p>
          <font color="#008000">
            <font color="#0000ff">    </font>' return the
      inner string
      </font>
        </p>
        <p>
          <font color="#0000ff">    Public</font>
          <font color="#0000ff">Overrides</font>
          <font color="#0000ff">Function</font> ToString() <font color="#0000ff">As</font><font color="#0000ff">String<br />
              </font><font color="#0000ff">Return</font> buffer.ToString()<br /><font color="#0000ff">    End</font><font color="#0000ff">Function
      </font></p>
        <p>
          <font color="#0000ff">    Public</font>
          <font color="#000000">
          </font>
          <font color="#0000ff">Shared</font>
          <font color="#000000">
          </font>
          <font color="#0000ff">Operator</font>
          <font color="#000000"> +(</font>
          <font color="#0000ff">ByVal</font>
          <font color="#000000"> op1 </font>
          <font color="#0000ff">As</font>
          <font color="#000000"> StringBuilder6, </font>
          <font color="#0000ff">ByVal</font>
          <font color="#000000"> op2 </font>
          <font color="#0000ff">As</font>
          <font color="#000000">
          </font>
          <font color="#0000ff">String</font>
          <font color="#000000">) </font>
          <font color="#0000ff">As</font>
          <font color="#000000"> StringBuilder6<br />
              op1.buffer.Append(op2)<br />
              </font>
          <font color="#0000ff">Return</font>
          <font color="#000000"> op1<br /></font>
          <font color="#0000ff">    End</font>
          <font color="#000000">
          </font>
          <font color="#0000ff">Operator
      </font>
        </p>
        <p>
          <font color="#0000ff">    Public</font>
          <font color="#0000ff">Shared</font>
          <font color="#0000ff">Operator</font> &amp;(<font color="#0000ff">ByVal</font> op1 <font color="#0000ff">As</font> StringBuilder6, <font color="#0000ff">ByVal</font> op2 <font color="#0000ff">As</font><font color="#0000ff">String</font>) <font color="#0000ff">As</font> StringBuilder6<br />
              op1.buffer.Append(op2)<br />
              <font color="#0000ff">Return</font> op1<br /><font color="#0000ff">    End</font><font color="#0000ff">Operator
      </font></p>
        <p>
          <font color="#008000">    ' convert to string
      </font>
        </p>
        <p>
          <font color="#0000ff">    Public</font>
          <font color="#0000ff">Shared</font>
          <font color="#0000ff">Widening</font>
          <font color="#0000ff">Operator</font>
          <font color="#0000ff">CType</font>(<font color="#0000ff">ByVal</font> op <font color="#0000ff">As</font> StringBuilder6) <font color="#0000ff">As</font><font color="#0000ff">String<br />
              </font><font color="#0000ff">Return</font> op.ToString()<br /><font color="#0000ff">    End</font><font color="#0000ff">Operator
      </font></p>
        <p>
          <font color="#008000">    ' convert from string 
      </font>
        </p>
        <p>
          <font color="#0000ff">    Public</font>
          <font color="#0000ff">Shared</font>
          <font color="#0000ff">Widening</font>
          <font color="#0000ff">Operator</font>
          <font color="#0000ff">CType</font>(<font color="#0000ff">ByVal</font> str <font color="#0000ff">As</font><font color="#0000ff">String</font>) <font color="#0000ff">As</font> StringBuilder6<br />
              <font color="#0000ff">Dim</font> op <font color="#0000ff">As</font><font color="#0000ff">New</font> StringBuilder6()<br />
              op.buffer.Append(str)<br />
              <font color="#0000ff">Return</font> op<br /><font color="#0000ff">    End</font><font color="#0000ff">Operator
      </font></p>
        <p>
      End<font color="#000000"></font><font color="#0000ff">Class</font></p>
        <p>
      Once the StringBuilder6 class is in place, you just need to replace the type of the
      String variable:
   </p>
        <p>
          <font color="#0000ff">Dim</font> s <font color="#0000ff">As</font> StringBuilder6
      = <font color="#800000">""<br /></font></p>
        <font color="#000000">
          <p>
      After this edit, the loop runs in <strong>0.008 seconds</strong>, that is <strong><font color="#ff0000">about
      2000 times faster</font></strong>!!! Not bad, for such a simple fix :-)
   </p>
          <p>
      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.
   </p>
          <p>
       
   </p>
        </font>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=ce96d1dc-838b-424d-b571-282e028e9e32" />
      </body>
      <title>Speed up string concatenation in VB.NET apps migrated from VB6</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,ce96d1dc-838b-424d-b571-282e028e9e32.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,ce96d1dc-838b-424d-b571-282e028e9e32.aspx</link>
      <pubDate>Sat, 22 Dec 2007 11:40:06 GMT</pubDate>
      <description>&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#0000ff&gt;Dim&lt;/font&gt; s &lt;font color=#0000ff&gt;As&lt;/font&gt;&amp;nbsp;&lt;font color=#0000ff&gt;String&lt;/font&gt; = &lt;font color=#800000&gt;""&lt;br&gt;
   &lt;/font&gt;&lt;font color=#0000ff&gt;For&lt;/font&gt; i &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Integer&lt;/font&gt; =
   1 &lt;font color=#0000ff&gt;To&lt;/font&gt; 100000&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; s = s + &lt;font color=#800000&gt;"*"&lt;br&gt;
   &lt;/font&gt;&lt;font color=#0000ff&gt;Next&lt;br&gt;
   &lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
   The solution is of course trivial: just replace the string variable with a &lt;strong&gt;StringBuilder &lt;/strong&gt;object.
   Alas, this fix requires that you completely revise your source code, because you need
   to replace all + and &amp;amp; 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.
&lt;/p&gt;
&lt;p&gt;
   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 &amp;amp; operators, and that supports the implicit conversion to and from the
   System.String type. Authoring such a &lt;strong&gt;StringBuilder6 &lt;/strong&gt;class is a matter
   of minutes:
&lt;/p&gt;
&lt;font color=#008000&gt; 
&lt;p&gt;
   ' a wrapper for the StringBuilder object, with support for + and &amp;amp; operators
&lt;/p&gt;
&lt;/font&gt;&lt;font color=#0000ff&gt; 
&lt;p&gt;
   Public
&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Class&lt;/font&gt;&lt;font color=#000000&gt; StringBuilder6&lt;/font&gt;&gt;
&lt;p&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Private&lt;/font&gt; buffer &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;New&lt;/font&gt; &lt;font color=#000000&gt;System.Text.&lt;/font&gt;StringBuilder
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#008000&gt;&lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;' return the inner
   string
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Public&lt;/font&gt; &lt;font color=#0000ff&gt;Overrides&lt;/font&gt; &lt;font color=#0000ff&gt;Function&lt;/font&gt; ToString() &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;String&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Return&lt;/font&gt; buffer.ToString()&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt; &lt;font color=#0000ff&gt;Function
&lt;/p&gt;
&lt;p&gt;
   &gt;&lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Public&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Shared&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Operator&lt;/font&gt;&lt;font color=#000000&gt; +(&lt;/font&gt;&lt;font color=#0000ff&gt;ByVal&lt;/font&gt;&lt;font color=#000000&gt; op1 &lt;/font&gt;&lt;font color=#0000ff&gt;As&lt;/font&gt;&lt;font color=#000000&gt; StringBuilder6, &lt;/font&gt;&lt;font color=#0000ff&gt;ByVal&lt;/font&gt;&lt;font color=#000000&gt; op2 &lt;/font&gt;&lt;font color=#0000ff&gt;As&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;String&lt;/font&gt;&lt;font color=#000000&gt;) &lt;/font&gt;&lt;font color=#0000ff&gt;As&lt;/font&gt;&lt;font color=#000000&gt; StringBuilder6&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; op1.buffer.Append(op2)&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Return&lt;/font&gt;&lt;font color=#000000&gt; op1&lt;br&gt;
   &lt;/font&gt;&lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Operator
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Public&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; &lt;font color=#0000ff&gt;Operator&lt;/font&gt; &amp;amp;(&lt;font color=#0000ff&gt;ByVal&lt;/font&gt; op1 &lt;font color=#0000ff&gt;As&lt;/font&gt; StringBuilder6, &lt;font color=#0000ff&gt;ByVal&lt;/font&gt; op2 &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;String&lt;/font&gt;) &lt;font color=#0000ff&gt;As&lt;/font&gt; StringBuilder6&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; op1.buffer.Append(op2)&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;Return&lt;/font&gt; op1&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt; &lt;font color=#0000ff&gt;Operator
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' convert to string
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Public&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; &lt;font color=#0000ff&gt;Widening&lt;/font&gt; &lt;font color=#0000ff&gt;Operator&lt;/font&gt; &lt;font color=#0000ff&gt;CType&lt;/font&gt;(&lt;font color=#0000ff&gt;ByVal&lt;/font&gt; op &lt;font color=#0000ff&gt;As&lt;/font&gt; StringBuilder6) &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;String&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Return&lt;/font&gt; op.ToString()&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt; &lt;font color=#0000ff&gt;Operator
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' convert from string 
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Public&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; &lt;font color=#0000ff&gt;Widening&lt;/font&gt; &lt;font color=#0000ff&gt;Operator&lt;/font&gt; &lt;font color=#0000ff&gt;CType&lt;/font&gt;(&lt;font color=#0000ff&gt;ByVal&lt;/font&gt; str &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;String&lt;/font&gt;) &lt;font color=#0000ff&gt;As&lt;/font&gt; StringBuilder6&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;font color=#0000ff&gt;Dim&lt;/font&gt; op &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;New&lt;/font&gt; StringBuilder6()&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; op.buffer.Append(str)&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;font color=#0000ff&gt;Return&lt;/font&gt; op&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt; &lt;font color=#0000ff&gt;Operator
&lt;/p&gt;
&lt;p&gt;
   End&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Class&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
   Once the StringBuilder6 class is in place, you just need to replace the type of the
   String variable:
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#0000ff&gt;Dim&lt;/font&gt; s &lt;font color=#0000ff&gt;As&lt;/font&gt;&amp;nbsp;StringBuilder6
   = &lt;font color=#800000&gt;""&lt;br&gt;
   &lt;/font&gt;
&lt;/p&gt;
&lt;font color=#000000&gt; 
&lt;p&gt;
   After this edit, the loop runs in &lt;strong&gt;0.008 seconds&lt;/strong&gt;, that is &lt;strong&gt;&lt;font color=#ff0000&gt;about
   2000 times faster&lt;/font&gt;&lt;/strong&gt;!!! Not bad, for such a simple fix :-)
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   &amp;nbsp;
&lt;/p&gt;
&lt;/font&gt;&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=ce96d1dc-838b-424d-b571-282e028e9e32" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,ce96d1dc-838b-424d-b571-282e028e9e32.aspx</comments>
      <category>Migrating from VB6;Optimization;Visual Basic</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=60e09b96-7033-4883-95b0-8ddb0b205ad3</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,60e09b96-7033-4883-95b0-8ddb0b205ad3.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,60e09b96-7033-4883-95b0-8ddb0b205ad3.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=60e09b96-7033-4883-95b0-8ddb0b205ad3</wfw:commentRss>
      <slash:comments>6</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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.
   </p>
        <p>
      All the files you need are in <a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/asmzip.zip">this
      ZIP archive</a>, 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.
   </p>
        <p>
          <strong>
            <font size="4">Step-by-step</font>
          </strong>
          <br />
      These are the steps you must follow to implement the technique.
   </p>
        <p>
          <strong>1)</strong> Add either the Unzipper.vb or the Unzipper.cs file to the main
      project of your application, depending on the language you've used.
   </p>
        <p>
          <strong>2)</strong> In the Main method, add a statement that initializes the AssemblyUnzipper
      class (which is defined in the Unzipper file you added in step 1).<br />
             ' (Visual Basic 2005)<br />
             CodeArchitects.AssemblyUnzipper.Initialize()<br />
             // Visual C# 2005<br />
             CodeArchitects.AssemblyUnzipper.Initialize();<br />
      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:<br />
            Shared Sub New()<br />
               CodeArchitects.AssemblyUnzipper.Initialize()<br />
            End Sub
   </p>
        <p>
          <strong>3)</strong> compile the project, obviously in Release mode. (You should use
      this technique just before delivering the executable to your customer(s).
   </p>
        <p>
          <strong>4)</strong> open a command prompt window from inside the application's \bin
      directory, and run the AsmZip utility as follows:<br />
                   AsmZip main.exe
      *.dll<br />
      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:<br />
                   AsmZip main.exe
      CodeArchitects*.dll Microsoft*.dll<br />
      There can be a few good reasons not to compress some of the DLLs used by the applications,
      as I'll explain shortly.
   </p>
        <p>
          <strong>5)</strong> 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.
   </p>
        <p>
          <strong>
            <font size="4">Pros</font>
          </strong>
          <br />
      Before proceeding with an explaination of the technique's inner details, let's summarize
      its advantages:
   </p>
        <p>
      a) simpliefied deployment: you need to distribute fewer files (often just the main
      EXE)<br />
      b) more robust applications: end users can't break the application by accidentally
      deleting one of its DLLs<br />
      c) fewer bytes on disk: all DLLs are compressed and appended to the main EXE file<br />
      d) the ability to "hide" some of your trade secrets, for example which 3rd party controls
      you've used<br />
      e) a slightly better protection of your intellectual property: compressed DLLs can't
      be decompiled, at least not as easily as uncompressed DLLs .
   </p>
        <p>
      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.
   </p>
        <p>
      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.
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/AsmZip.gif" border="0" />
        </p>
        <p>
          <br />
          <strong>
            <font size="4">How it works</font>
          </strong>
          <br />
      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.
   </p>
        <p>
      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:<br />
            // the handler for AssemblyResolve event<br />
            static Assembly CurrentDomain_AssemblyResolve(object
      sender, ResolveEventArgs e)<br />
            {<br />
               // find the assembly with given name,
      cause error if not found<br />
               AssemblyInfo info = null;<br />
               if ( AsmInfos.TryGetValue(e.Name,
      out info) )<br />
                  return ExtractAssembly(info);<br />
               // signal error<br />
               Debug.WriteLine("Failed to uncompress
      assembly " + info.Name);<br />
               return null;<br />
            }<br />
      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.
   </p>
        <p>
          <strong>
            <font size="4">Limitations</font>
          </strong>
          <br />
      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.
   </p>
        <p>
      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.
   </p>
        <p>
      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.
   </p>
        <p>
          <strong>Note:</strong> 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.
   </p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=60e09b96-7033-4883-95b0-8ddb0b205ad3" />
      </body>
      <title>Compressing and hiding DLLs</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,60e09b96-7033-4883-95b0-8ddb0b205ad3.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,60e09b96-7033-4883-95b0-8ddb0b205ad3.aspx</link>
      <pubDate>Wed, 11 Apr 2007 16:07:41 GMT</pubDate>
      <description>&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   All the files you need are in &lt;a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/asmzip.zip"&gt;this
   ZIP archive&lt;/a&gt;, 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.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;&lt;font size=4&gt;Step-by-step&lt;/font&gt;&lt;/strong&gt;
   &lt;br&gt;
   These are the steps you must follow to implement the technique.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;1)&lt;/strong&gt; Add either the Unzipper.vb or the Unzipper.cs file to the main
   project of your application, depending on the language you've used.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;2)&lt;/strong&gt; In the Main method, add a statement that initializes the AssemblyUnzipper
   class (which is defined in the Unzipper file you added in step 1).&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' (Visual Basic 2005)&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CodeArchitects.AssemblyUnzipper.Initialize()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Visual C# 2005&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CodeArchitects.AssemblyUnzipper.Initialize();&lt;br&gt;
   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:&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Shared Sub New()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CodeArchitects.AssemblyUnzipper.Initialize()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End Sub
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;3)&lt;/strong&gt; compile the project, obviously in Release mode. (You should use
   this technique just before delivering the executable to your customer(s).
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;4)&lt;/strong&gt; open a command prompt window from inside the application's \bin
   directory, and run the AsmZip utility as follows:&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AsmZip main.exe
   *.dll&lt;br&gt;
   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:&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AsmZip main.exe
   CodeArchitects*.dll Microsoft*.dll&lt;br&gt;
   There can be a few good reasons not to compress some of the DLLs used by the applications,
   as I'll explain shortly.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;5)&lt;/strong&gt; 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.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;&lt;font size=4&gt;Pros&lt;/font&gt;&lt;/strong&gt;
   &lt;br&gt;
   Before proceeding with an explaination of the technique's inner details, let's summarize
   its advantages:
&lt;/p&gt;
&lt;p&gt;
   a) simpliefied deployment: you need to distribute fewer files (often just the main
   EXE)&lt;br&gt;
   b) more robust applications: end users can't break the application by accidentally
   deleting one of its DLLs&lt;br&gt;
   c) fewer bytes on disk: all DLLs are compressed and appended to the main EXE file&lt;br&gt;
   d) the ability to "hide" some of your trade secrets, for example which 3rd party controls
   you've used&lt;br&gt;
   e) a slightly better protection of your intellectual property: compressed DLLs can't
   be decompiled, at least not as easily as uncompressed DLLs .
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/AsmZip.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   &lt;br&gt;
   &lt;strong&gt;&lt;font size=4&gt;How it works&lt;/font&gt;&lt;/strong&gt;
   &lt;br&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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:&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // the handler for AssemblyResolve event&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; static Assembly CurrentDomain_AssemblyResolve(object
   sender, ResolveEventArgs e)&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // find the assembly with given name,
   cause error if not found&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AssemblyInfo info = null;&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ( AsmInfos.TryGetValue(e.Name,
   out info) )&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return ExtractAssembly(info);&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // signal error&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Debug.WriteLine("Failed to uncompress
   assembly " + info.Name);&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return null;&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;&lt;font size=4&gt;Limitations&lt;/font&gt;&lt;/strong&gt;
   &lt;br&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   &lt;strong&gt;Note:&lt;/strong&gt; 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.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=60e09b96-7033-4883-95b0-8ddb0b205ad3" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,60e09b96-7033-4883-95b0-8ddb0b205ad3.aspx</comments>
      <category>.NET Framework;C#;Optimization;Tools;Visual Basic;Windows Forms</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=13bce26d-7755-441e-92b3-1eb5f9e859f9</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=13bce26d-7755-441e-92b3-1eb5f9e859f9</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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, <strong>it comes
      with source code</strong>. You can download it from the <a href="http://www.dotnet2themax.com/ProgrammingVB2005.aspx">home
      page</a> of my Visual Basic 2005 book (together with all other code samples in the
      book) or directly from here:
   </p>
        <p>
      Executable (requires .NET Framework 2.0): <a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester.zip">RegexTester.zip
      (75.57 KB)</a><br />
      Source code (VB2005): <a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester%20source.zip">RegexTester
      source.zip (88.15 KB)</a></p>
        <p>
      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:
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester1.gif" border="0" />
        </p>
        <p>
      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:
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester2.gif" border="0" />
        </p>
        <p>
      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:
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester3.gif" border="0" />
        </p>
        <p>
      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):
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester4.gif" border="0" />
        </p>
        <p>
      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. 
   </p>
        <p>
      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:
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester5.gif" border="0" />
        </p>
        <p>
      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:
   </p>
        <p>
       
   </p>
        <p>
          <img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester6.gif" border="0" />
        </p>
        <p>
      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.
   </p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=13bce26d-7755-441e-92b3-1eb5f9e859f9" />
      </body>
      <title>YART - Yet Another Regex Tester</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx</link>
      <pubDate>Tue, 29 Aug 2006 19:00:02 GMT</pubDate>
      <description>&lt;p&gt;
   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, &lt;strong&gt;it comes
   with source code&lt;/strong&gt;. You can download it from the &lt;a href="http://www.dotnet2themax.com/ProgrammingVB2005.aspx"&gt;home
   page&lt;/a&gt; of my Visual Basic 2005 book (together with all other code samples in the
   book) or directly from here:
&lt;/p&gt;
&lt;p&gt;
   Executable (requires .NET Framework 2.0): &lt;a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester.zip"&gt;RegexTester.zip
   (75.57 KB)&lt;/a&gt;
   &lt;br&gt;
   Source code (VB2005): &lt;a href="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester%20source.zip"&gt;RegexTester
   source.zip (88.15 KB)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester1.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester2.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester3.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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):
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester4.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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. 
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester5.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
   &lt;img src="http://www.dotnet2themax.com/blogs/fbalena/content/binary/RegexTester6.gif" border=0&gt;
&lt;/p&gt;
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=13bce26d-7755-441e-92b3-1eb5f9e859f9" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx</comments>
      <category>C#;Regex;Tools;Visual Basic</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=fc6a58f2-9682-4e6c-97a4-20ad0facb5e6</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,fc6a58f2-9682-4e6c-97a4-20ad0facb5e6.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,fc6a58f2-9682-4e6c-97a4-20ad0facb5e6.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=fc6a58f2-9682-4e6c-97a4-20ad0facb5e6</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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 <a href="http://www.dotnet2themax.com/ProgrammingCSharp2005.aspx">home
      page</a> for the book, from where you can download two sample chapters: Chapter 5 <strong>"Arrays
      and Collection"</strong> and Chapter 10, <strong>"Custom Attributes", </strong>as
      well as all the code samples and the errata doc.
   </p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=fc6a58f2-9682-4e6c-97a4-20ad0facb5e6" />
      </body>
      <title>Two sample chapters from "Programming Microsoft Visual C# 2005: The Base Class Library"</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,fc6a58f2-9682-4e6c-97a4-20ad0facb5e6.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,fc6a58f2-9682-4e6c-97a4-20ad0facb5e6.aspx</link>
      <pubDate>Sat, 01 Jul 2006 09:35:25 GMT</pubDate>
      <description>&lt;p&gt;
   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 &lt;a href="http://www.dotnet2themax.com/ProgrammingCSharp2005.aspx"&gt;home
   page&lt;/a&gt; for the book, from where you can download two sample chapters: Chapter 5 &lt;strong&gt;"Arrays
   and Collection"&lt;/strong&gt; and Chapter 10, &lt;strong&gt;"Custom Attributes", &lt;/strong&gt;as
   well as all the code samples and the errata doc.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=fc6a58f2-9682-4e6c-97a4-20ad0facb5e6" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,fc6a58f2-9682-4e6c-97a4-20ad0facb5e6.aspx</comments>
      <category>Books;C#</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=f604c9a6-7804-4736-b7f6-6e445a097113</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,f604c9a6-7804-4736-b7f6-6e445a097113.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,f604c9a6-7804-4736-b7f6-6e445a097113.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=f604c9a6-7804-4736-b7f6-6e445a097113</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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 <a href="http://www.dotnet2themax.com/ProgrammingVB2005.aspx">book's home
      page</a>. 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).
   </p>
        <p>
      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 <strong>Dan Karmann</strong>, who actually methodically
      proof-read the book and found about twenty of them. 
   </p>
        <p>
       
   </p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=f604c9a6-7804-4736-b7f6-6e445a097113" />
      </body>
      <title>New errata document for "Programming Microsoft Visual Basic 2005: The Language"</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,f604c9a6-7804-4736-b7f6-6e445a097113.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,f604c9a6-7804-4736-b7f6-6e445a097113.aspx</link>
      <pubDate>Sat, 01 Jul 2006 08:01:26 GMT</pubDate>
      <description>&lt;p&gt;
   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 &lt;a href="http://www.dotnet2themax.com/ProgrammingVB2005.aspx"&gt;book's home
   page&lt;/a&gt;. 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).
&lt;/p&gt;
&lt;p&gt;
   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 &lt;strong&gt;Dan Karmann&lt;/strong&gt;, who actually methodically
   proof-read the book and found about twenty of them. 
&lt;/p&gt;
&lt;p&gt;
   &amp;nbsp;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=f604c9a6-7804-4736-b7f6-6e445a097113" /&gt;</description>
      <comments>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,f604c9a6-7804-4736-b7f6-6e445a097113.aspx</comments>
      <category>Books;Visual Basic</category>
    </item>
    <item>
      <trackback:ping>http://www.dotnet2themax.com/blogs/fbalena/Trackback.aspx?guid=669b012d-46d6-402f-b8d9-b7553b44451c</trackback:ping>
      <pingback:server>http://www.dotnet2themax.com/blogs/fbalena/pingback.aspx</pingback:server>
      <pingback:target>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,669b012d-46d6-402f-b8d9-b7553b44451c.aspx</pingback:target>
      <dc:creator>fbalena@codearchitects.com (Francesco Balena)</dc:creator>
      <wfw:comment>http://www.dotnet2themax.com/blogs/fbalena/CommentView,guid,669b012d-46d6-402f-b8d9-b7553b44451c.aspx</wfw:comment>
      <wfw:commentRss>http://www.dotnet2themax.com/blogs/fbalena/SyndicationService.asmx/GetEntryCommentsRss?guid=669b012d-46d6-402f-b8d9-b7553b44451c</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
      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.
   </p>
        <font color="#0000ff">
          <font color="#008000">
            <p>
      ' to display this code, open the Application page of the My Project designer and click
      the Application Events button<br /></p>
          </font>Namespace</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">My<br />
      </font>
        <font color="#0000ff">Partial</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Friend</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Class</font>
        <font color="#000000"> MyApplication<br />
         </font>
        <font color="#0000ff">Private</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Sub</font>
        <font color="#000000"> MyApplication_StartupNextInstance(</font>
        <font color="#0000ff">ByVal</font>
        <font color="#000000"> sender </font>
        <font color="#0000ff">As</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Object</font>
        <font color="#000000">, </font>
        <font color="#0000ff">ByVal</font>
        <font color="#000000"> e </font>
        <font color="#0000ff">As</font>
        <font color="#000000"> Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs) </font>
        <font color="#0000ff">Handles</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Me</font>
        <font color="#000000">.StartupNextInstance<br /></font>
        <font color="#008000">         ' another
   instance of this application has been launched<br /></font>
        <font color="#0000ff">      End</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Sub<br />
      </font>
        <font color="#0000ff">End</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Class<br />
   End</font>
        <font color="#000000">
        </font>
        <font color="#0000ff">Namespace</font>
        <font color="#000000">
          <p>
      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 <em>another </em>application is launched,
      not if the current applicatin is the first and only running instance.
   </p>
          <p>
      .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 &gt;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.
   </p>
          <p>
      In addition to the PrevInstance property - which returns False if the application
      was the only running instance <em>when the application was launched </em>- the following
      VB6App class exposes also the InstanceCount property, which returns the total number
      of instances <em>in that moment </em>(the current app is included in the count). Here's
      the VB2005 version of this class:
   </p>
          <font color="#0000ff">
            <p>
      Class
   </p>
          </font> VB6App<br />
      <font color="#008000">' the default instance 
   <br />
      </font><font color="#0000ff">Private</font><font color="#0000ff">Shared</font> DefValue <font color="#0000ff">As</font><font color="#0000ff">New</font> VB6App
   <p><font color="#008000">   ' the system-wide semaphore<br />
         </font><font color="#0000ff">Private</font> semaphore <font color="#0000ff">As</font> System.Threading.Semaphore<br /><font color="#008000">   ' initial count for the semaphore (very high value)<br />
         </font><font color="#0000ff">Private</font><font color="#0000ff">Const</font> MAXCOUNT <font color="#0000ff">As</font><font color="#0000ff">Integer</font> =
      10000
   </p><p><font color="#008000">   </font><font color="#0000ff">Private</font><font color="#0000ff">Sub</font><font color="#0000ff">New</font>()<br />
            <font color="#008000"><font color="#0000ff">Dim</font><font color="#000000">ownership</font><font color="#0000ff">As</font><font color="#0000ff">Boolean</font> = <font color="#0000ff">False<br /></font><font color="#000000">      <font color="#008000">' create
      a unique name, but strip invalid characters<br /></font>      </font><font color="#0000ff">Dim</font><font color="#000000">name</font><font color="#0000ff">As</font><font color="#0000ff">String</font> = <font color="#800000">"VB6App_"</font> &amp; <font color="#000000">System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(</font><font color="#800000">":"</font><font color="#000000">,</font><font color="#800000">""</font>)<font color="#000000">.Replace(</font><font color="#800000">"\"</font>, <font color="#800000">""</font><font color="#000000">)</font><br /><font color="#000000">      </font><font color="#000000">semaphore</font> = <font color="#0000ff">New</font><font color="#000000">System.Threading.Semaphore(MAXCOUNT,
      MAXCOUNT, name, ownership)<br /></font><font color="#000000">      </font><font color="#008000">'
      decrement its value <br /></font><font color="#000000">      </font><font color="#000000">semaphore.WaitOne()</font><br /><font color="#000000">      </font><font color="#008000">'
      if we got ownership, this app has no previous instances<br /></font><font color="#000000">      </font><font color="#000000">m_PrevInstance </font>= <font color="#0000ff">Not</font><font color="#000000">ownership</font><br /></font><font color="#008000">   </font><font color="#0000ff">End</font><font color="#0000ff">Sub
      </font></p></font>
        <p>
          <font color="#008000">   ' the PrevInstance property returns True if there
      was a previous instance running <br />
         </font>
          <font color="#008000">' when the default instance was created<br />
         </font>
          <font color="#0000ff">Private</font>
          <font color="#0000ff">Shared</font> m_PrevInstance <font color="#0000ff">As</font> <font color="#0000ff">Boolean</font></p>
        <p>
          <font color="#0000ff">
            <font color="#008000">   </font>Public</font>
          <font color="#0000ff">Shared</font>
          <font color="#0000ff">ReadOnly</font>
          <font color="#0000ff">Property</font> PrevInstance() <font color="#0000ff">As</font><font color="#0000ff">Boolean<br /></font><font color="#008000">      </font><font color="#0000ff">Get<br /></font><font color="#008000">         </font><font color="#0000ff">Return</font> m_PrevInstance <br /><font color="#008000">      </font><font color="#0000ff">End</font><font color="#0000ff">Get<br /></font><font color="#008000">   </font><font color="#0000ff">End</font><font color="#0000ff">Property
      </font></p>
        <p>
          <font color="#008000">   ' return the total number of instances of the same
      application that are currently running <br />
         </font>
          <font color="#0000ff">Public</font>
          <font color="#0000ff">Shared</font>
          <font color="#0000ff">ReadOnly</font>
          <font color="#0000ff">Property</font> InstanceCount() <font color="#0000ff">As</font><font color="#0000ff">Integer<br /></font><font color="#008000">      </font><font color="#0000ff">Get<br /></font><font color="#008000">         ' release
      the semaphore and grab the previous count <br />
               </font><font color="#0000ff">Dim</font> prevCount <font color="#0000ff">As</font><font color="#0000ff">Integer</font> =
      DefValue.semaphore.Release()<br /><font color="#008000">         ' acquire the
      semaphore again<br />
               </font>DefValue.semaphore.WaitOne()<br /><font color="#008000">         ' eval the
      number of other instances that are currently running <br />
               </font><font color="#0000ff">Return</font> MAXCOUNT
      - prevCount<br /><font color="#008000">      </font><font color="#0000ff">End</font><font color="#0000ff">Get<br /></font><font color="#008000">   </font><font color="#0000ff">End</font><font color="#0000ff">Property
      </font></p>
        <p>
          <font color="#0000ff">
            <font color="#008000">   </font>Protected</font>
          <font color="#0000ff">Overrides</font>
          <font color="#0000ff">Sub</font> Finalize()<br /><font color="#008000">      ' increment the semaphore when
      the application terminates<br />
            </font>semaphore.Release()<br /><font color="#008000">   </font><font color="#0000ff">End</font><font color="#0000ff">Sub
      </font></p>
        <p>
      End <font color="#0000ff">Class
      </font></p>
        <p>
      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.
   </p>
        <p>
      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:
   </p>
        <p>
          <font color="#0000ff">Private</font>
          <font color="#0000ff">Sub</font> Form1_Load(<font color="#0000ff">ByVal</font> sender <font color="#0000ff">As</font><font color="#0000ff">Object</font>, <font color="#0000ff">ByVal</font> e <font color="#0000ff">As</font> System.EventArgs) <font color="#0000ff">Handles</font><font color="#0000ff">Me</font>.Load<br /><font color="#008000">   </font><font color="#0000ff">If</font> <font color="#0000ff">Not</font> VB6App.PrevInstance <font color="#0000ff">Then</font> <br /><font color="#0000ff">      <font color="#008000">' open
      the common log file<br /><font color="#0000ff">      </font><font color="#008000">' ...<br /></font></font>   End</font><font color="#0000ff">If<br /></font><font color="#0000ff">End</font><font color="#0000ff">Sub 
      <p></p></font></p>
        <p>
          <font color="#0000ff">Private</font>
          <font color="#0000ff">Sub</font> Form1_FormClosing(<font color="#0000ff">ByVal</font> sender <font color="#0000ff">As</font><font color="#0000ff">Object</font>, <font color="#0000ff">ByVal</font> e <font color="#0000ff">As</font> System.Windows.Forms.FormClosingEventArgs) <font color="#0000ff">Handles</font><font color="#0000ff">Me</font>.FormClosing<br />
         <font color="#0000ff">If</font> VB6App.InstanceCount = 1 <font color="#0000ff">Then<br />
            </font><font color="#008000">' close the common log
      file.<br /><font color="#0000ff">      </font><font color="#008000">' ...<br /></font>   </font><font color="#0000ff">End</font><font color="#0000ff">If<br /></font><font color="#0000ff">End</font><font color="#0000ff">Sub
      </font></p>
        <p>
      Here's the C# version of the class. <em>(I fixed the original version to fix two syntax
      errors mentioned in one of the comments below.)</em></p>
        <p>
          <font color="#0000ff">public</font>
          <font color="#0000ff">class</font>
          <font color="#008080">VB6App</font>
          <br />
      { 
      <br />
         <font color="#008000">// the default instance <br /></font><font color="#000000">   </font><font color="#0000ff">private</font><font color="#0000ff">static</font><font color="#008080">VB6App</font> DefValue
      = <font color="#0000ff">new</font><font color="#008080">VB6App</font>(); <br />
         <font color="#008000">// the system-wide semaphore<br /></font><font color="#000000">   </font><font color="#0000ff">private</font> System.Threading.<font color="#008080">Semaphore</font> semaphore; <br />
         <font color="#008000">// initial count for the semaphore (very high value)<br /></font><font color="#000000">   </font><font color="#0000ff">private</font><font color="#0000ff">const</font><font color="#0000ff">int</font> MAXCOUNT
      = 10000; 
   </p>
        <p>
        </p>
        <p>
          <font color="#0000ff">
            <font color="#000000">   </font>private</font> VB6App() <br />
         { <br />
            <font color="#008000">// create a named (system-wide
      semaphore)<br /></font><font color="#000000">      </font><font color="#0000ff">bool</font> ownership
      = <font color="#0000ff">false</font>; <br />
            <font color="#008000">// create the semaphore or get
      a reference to an existing semaphore</font></p>
        <p>
          <font color="#008000">
            <font color="#0000ff">      string</font>
            <font color="#000000">name</font> = <font color="#800000">"VB6App_"</font> <font color="#0000ff">+</font><font color="#000000">System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(<font color="#800000">":"</font><font color="#000000">,</font><font color="#008000"></font><font color="#800000">""</font><font color="#008000">)</font><font color="#000000">.Replace(</font><font color="#800000">"\\"</font><font color="#008000">, </font><font color="#800000">""</font><font color="#000000">);</font><br /></font></font>      semaphore = <font color="#0000ff">new</font> System.Threading.<font color="#008080">Semaphore</font>(
      MAXCOUNT, MAXCOUNT, name, <font color="#0000ff">out </font>ownership); <br />
            <font color="#008000">// decrement its value 
      <br />
            </font>semaphore.WaitOne(); <br /><font color="#008000"><font color="#000000">      </font><font color="#008000">//
      if we got ownership, this app has no previous instances<br /></font><font color="#000000">      </font><font color="#000000">m_PrevInstance </font>= <font color="#0000ff">!</font><font color="#000000">ownership;</font></font><br />
         } 
   </p>
        <p>
        </p>
        <p>
          <font color="#008000">   // the PrevInstance property returns True if there
      was a previous instance running 
      <br />
         </font>
          <font color="#008000">// when the default instance was created
      </font>
        </p>
        <p>
        </p>
        <p>
          <font color="#0000ff">
            <font color="#008000">   </font>private</font>
          <font color="#0000ff">static</font>
          <font color="#0000ff">bool </font>m_PrevInstance
      ; 
   </p>
        <p>
        </p>
        <p>
          <font color="#0000ff">
            <font color="#008000">   </font>public</font>
          <font color="#0000ff">static</font>
          <font color="#0000ff">bool</font> PrevInstance <br /><font color="#008000">   </font>{ <br /><font color="#008000">      </font><font color="#0000ff">get</font> <br /><font color="#008000">      </font>{ <br /><font color="#008000">         </font><font color="#0000ff">return</font> m_PrevInstance ; <br /><font color="#008000">      </font>} <br /><font color="#008000">   </font>} 
   </p>
        <p>
        </p>
        <p>
          <font color="#008000">   // return the total number of instances of the
      same application that are currently running 
      </font>
        </p>
        <p>
        </p>
        <p>
          <font color="#0000ff">
            <font color="#008000">   </font>public</font>
          <font color="#0000ff">static</font>
          <font color="#0000ff">int</font> InstanceCount <br /><font color="#008000">   </font>{ <br /><font color="#008000">      </font><font color="#0000ff">get</font> <br /><font color="#008000">      </font>{ <br /><font color="#008000">         // release
      the semaphore and grab the previous count <br />
               </font><font color="#0000ff">int</font> prevCount
      = DefValue.semaphore.Release(); <br /><font color="#008000">         // acquire
      the semaphore again<br />
               </font>DefValue.semaphore.WaitOne(); <br /><font color="#008000">         // eval the
      number of other instances that are currently running <br />
               </font><font color="#0000ff">return</font> MAXCOUNT
      - prevCount; <br /><font color="#008000">      </font>} <br /><font color="#008000">   </font>} 
   </p>
        <p>
        </p>
        <p>
          <font color="#008000">   </font>~VB6App() <br /><font color="#008000">   </font>{ <br /><font color="#008000">      // increment the semaphore when
      the application terminates<br />
            </font>semaphore.Release(); <br /><font color="#008000">   </font>} 
      <br />
      } 
   </p>
        <img width="0" height="0" src="http://www.dotnet2themax.com/blogs/fbalena/aggbug.ashx?id=669b012d-46d6-402f-b8d9-b7553b44451c" />
      </body>
      <title>How many instances are running?</title>
      <guid>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,669b012d-46d6-402f-b8d9-b7553b44451c.aspx</guid>
      <link>http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,669b012d-46d6-402f-b8d9-b7553b44451c.aspx</link>
      <pubDate>Wed, 31 May 2006 09:10:12 GMT</pubDate>
      <description>&lt;p&gt;
   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.
&lt;/p&gt;
&lt;font color=#0000ff&gt;&lt;font color=#008000&gt; 
&lt;p&gt;
   ' to display this code, open the Application page of the My Project designer and click
   the Application Events button&lt;br&gt;
&lt;/font&gt;Namespace&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;My&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Partial&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Friend&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Class&lt;/font&gt;&lt;font color=#000000&gt; MyApplication&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Sub&lt;/font&gt;&lt;font color=#000000&gt; MyApplication_StartupNextInstance(&lt;/font&gt;&lt;font color=#0000ff&gt;ByVal&lt;/font&gt;&lt;font color=#000000&gt; sender &lt;/font&gt;&lt;font color=#0000ff&gt;As&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Object&lt;/font&gt;&lt;font color=#000000&gt;, &lt;/font&gt;&lt;font color=#0000ff&gt;ByVal&lt;/font&gt;&lt;font color=#000000&gt; e &lt;/font&gt;&lt;font color=#0000ff&gt;As&lt;/font&gt;&lt;font color=#000000&gt; Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs) &lt;/font&gt;&lt;font color=#0000ff&gt;Handles&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Me&lt;/font&gt;&lt;font color=#000000&gt;.StartupNextInstance&lt;br&gt;
&lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&amp;nbsp;another
instance of this application has been launched&lt;br&gt;
&lt;/font&gt;&lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Sub&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Class&lt;br&gt;
End&lt;/font&gt;&lt;font color=#000000&gt; &lt;/font&gt;&lt;font color=#0000ff&gt;Namespace&lt;/font&gt;&gt;
&lt;font color=#000000&gt; 
&lt;p&gt;
   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 &lt;em&gt;another &lt;/em&gt;application is launched,
   not if the current applicatin is the first and only running instance.
&lt;/p&gt;
&lt;p&gt;
   .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 &amp;gt;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.
&lt;/p&gt;
&lt;p&gt;
   In addition to the PrevInstance property - which returns False if the application
   was the only running instance &lt;em&gt;when the application was launched &lt;/em&gt;- the following
   VB6App class exposes also the InstanceCount property, which returns the total number
   of instances &lt;em&gt;in that moment &lt;/em&gt;(the current app is included in the count). Here's
   the VB2005 version of this class:
&lt;/p&gt;
&lt;font color=#0000ff&gt; 
&lt;p&gt;
   Class
&lt;/font&gt; VB6App&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;font color=#008000&gt;' the default instance 
&lt;br&gt;
&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; DefValue &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;New&lt;/font&gt; VB6App&gt;
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; ' the system-wide semaphore&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt; semaphore &lt;font color=#0000ff&gt;As&lt;/font&gt; System.Threading.Semaphore&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; ' initial count for the semaphore (very high value)&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Const&lt;/font&gt; MAXCOUNT &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Integer&lt;/font&gt; =
   10000
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Sub&lt;/font&gt; &lt;font color=#0000ff&gt;New&lt;/font&gt;()&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color=#008000&gt;&lt;font color=#0000ff&gt;Dim&lt;/font&gt; &lt;font color=#000000&gt;ownership&lt;/font&gt; &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Boolean&lt;/font&gt; = &lt;font color=#0000ff&gt;False&lt;br&gt;
   &lt;/font&gt;&lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;font color=#008000&gt;'&amp;nbsp;create
   a unique name, but strip invalid characters&lt;br&gt;
   &lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Dim&lt;/font&gt; &lt;font color=#000000&gt;name&lt;/font&gt; &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;String&lt;/font&gt; = &lt;font color=#800000&gt;"VB6App_"&lt;/font&gt; &amp;amp; &lt;font color=#000000&gt;System.Reflection.Assembly.GetExecutingAssembly().Location.Replace(&lt;/font&gt;&lt;font color=#800000&gt;":"&lt;/font&gt;&lt;font color=#000000&gt;,&lt;/font&gt; &lt;font color=#800000&gt;""&lt;/font&gt;)&lt;font color=#000000&gt;.Replace(&lt;/font&gt;&lt;font color=#800000&gt;"\"&lt;/font&gt;, &lt;font color=#800000&gt;""&lt;/font&gt;&lt;font color=#000000&gt;)&lt;/font&gt;
   &lt;br&gt;
   &lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#000000&gt;semaphore&lt;/font&gt; = &lt;font color=#0000ff&gt;New&lt;/font&gt; &lt;font color=#000000&gt;System.Threading.Semaphore(MAXCOUNT,
   MAXCOUNT, name, ownership)&lt;br&gt;
   &lt;/font&gt;&lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#008000&gt;'
   decrement its value&amp;nbsp;&lt;br&gt;
   &lt;/font&gt;&lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#000000&gt;semaphore.WaitOne()&lt;/font&gt;
   &lt;br&gt;
   &lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#008000&gt;' if
   we got ownership, this app has no previous instances&lt;br&gt;
   &lt;/font&gt;&lt;font color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#000000&gt;m_PrevInstance &lt;/font&gt;= &lt;font color=#0000ff&gt;Not&lt;/font&gt; &lt;font color=#000000&gt;ownership&lt;/font&gt;
   &lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Sub
&lt;/p&gt;
&lt;/font&gt; 
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; ' the PrevInstance property returns True if there
   was a previous instance running&amp;nbsp;&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#008000&gt;' when the default instance was created&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; m_PrevInstance &lt;font color=#0000ff&gt;As&lt;/font&gt;&amp;nbsp;&lt;font color=#0000ff&gt;Boolean&lt;/font&gt; 
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#0000ff&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;Public&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; &lt;font color=#0000ff&gt;ReadOnly&lt;/font&gt; &lt;font color=#0000ff&gt;Property&lt;/font&gt; PrevInstance() &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Boolean&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Get&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Return&lt;/font&gt; m_PrevInstance&amp;nbsp;&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Get&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Property
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; ' return the total number of instances of the same
   application that are currently running&amp;nbsp;&lt;br&gt;
   &amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Public&lt;/font&gt; &lt;font color=#0000ff&gt;Shared&lt;/font&gt; &lt;font color=#0000ff&gt;ReadOnly&lt;/font&gt; &lt;font color=#0000ff&gt;Property&lt;/font&gt; InstanceCount() &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Integer&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Get&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' release
   the semaphore and grab the previous count&amp;nbsp;&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Dim&lt;/font&gt; prevCount &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Integer&lt;/font&gt; =
   DefValue.semaphore.Release()&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' acquire the
   semaphore again&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;DefValue.semaphore.WaitOne()&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' eval the number
   of other instances that are currently running&amp;nbsp;&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;Return&lt;/font&gt; MAXCOUNT
   - prevCount&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Get&lt;br&gt;
   &lt;/font&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Property
&lt;/p&gt;
&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;&lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;Protected&lt;/font&gt; &lt;font color=#0000ff&gt;Overrides&lt;/font&gt; &lt;font color=#0000ff&gt;Sub&lt;/font&gt; Finalize()&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' increment the semaphore when
   the application terminates&lt;br&gt;
   &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;semaphore.Release()&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Sub
&lt;/p&gt;
&lt;p&gt;
   End&gt; &lt;font color=#0000ff&gt;Class
&lt;/p&gt;
&gt; 
&lt;p&gt;
   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.
&lt;/p&gt;
&lt;p&gt;
   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:
&lt;/p&gt;
&lt;p&gt;
   &lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Sub&lt;/font&gt; Form1_Load(&lt;font color=#0000ff&gt;ByVal&lt;/font&gt; sender &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Object&lt;/font&gt;, &lt;font color=#0000ff&gt;ByVal&lt;/font&gt; e &lt;font color=#0000ff&gt;As&lt;/font&gt; System.EventArgs) &lt;font color=#0000ff&gt;Handles&lt;/font&gt; &lt;font color=#0000ff&gt;Me&lt;/font&gt;.Load&lt;br&gt;
   &lt;font color=#008000&gt;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#0000ff&gt;If&lt;/font&gt;&amp;nbsp;&lt;font color=#0000ff&gt;Not&lt;/font&gt; VB6App.PrevInstance &lt;font color=#0000ff&gt;Then&lt;/font&gt;&amp;nbsp;&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;font color=#008000&gt;'&amp;nbsp;open the
   common log file&lt;br&gt;
   &lt;font color=#0000ff&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;font color=#008000&gt;'&amp;nbsp;...&lt;br&gt;
   &lt;/font&gt;&lt;/font&gt;&amp;nbsp;&amp;nbsp; End&lt;/font&gt; &lt;font color=#0000ff&gt;If&lt;br&gt;
   &lt;/font&gt;&lt;font color=#0000ff&gt;End&lt;/font&gt; &lt;font color=#0000ff&gt;Sub 
   &lt;p&gt;
   &lt;/p&gt;
   &lt;/font&gt; 
&lt;p&gt;
   &lt;font color=#0000ff&gt;Private&lt;/font&gt; &lt;font color=#0000ff&gt;Sub&lt;/font&gt; Form1_FormClosing(&lt;font color=#0000ff&gt;ByVal&lt;/font&gt; sender &lt;font color=#0000ff&gt;As&lt;/font&gt; &lt;font color=#0000ff&gt;Object&lt;/font&gt;, &lt;font 