Tag Archives: Vb6

Change the runtime of an IIS 7 application pool

The following quick SDK sample demonstrates how to list and change the managed runtime version of application pools programatically in IIS 7.

[VB]

Imports System
Imports Microsoft.Web.Administration
Public Class AppPoolSample
 Shared manager As ServerManager = New ServerManager()
  ' Main application processing
  Public Shared Sub Main(ByVal args As String())
      ' Get the apppool to change
      Dim iPool As Integer = GetAppPool()
      ' Get the framework version desired
      Dim rtVersion As String = GetVersion()
      ' Set the apppool runtime
      Dim poolToSet As ApplicationPool = manager.ApplicationPools(iPool)
      Console.WriteLine(
            "Setting application pool '{0}' to runtime version: {1}...", _
            poolToSet.Name, rtVersion)
      poolToSet.ManagedRuntimeVersion = rtVersion
      ' Commit the changes and recycle the application pool
      manager.CommitChanges()
      poolToSet.Recycle()
      Console.WriteLine("Your changes have been committed.")
  End Sub

  ' Prompts the user to select an application pool
  Public Shared Function GetAppPool() As Integer
    Dim pool As String = String.Empty
    Dim iPool As Integer = 0
    While (Not Integer.TryParse(pool, iPool))
      Console.WriteLine("Available ApplicationPools: Managed runtime version")
      Dim i As Integer
      For i = 0 To manager.ApplicationPools.Count - 1 Step i + 1
        Dim appPool As ApplicationPool = manager.ApplicationPools(i)
        Console.WriteLine("{3}{0,3}.{3}{1}: {2}", i + 1, _
            appPool.Name, appPool.ManagedRuntimeVersion, vbTab)
      Next
      Console.Write("{0}Choose an application pool to change: ", vbCrLf)
      pool = Console.ReadLine()
    End While
    Return iPool - 1
  End Function

    ' Prompts a user to select the version of runtime they would like
    ' the application pool to use
    Public Shared Function GetVersion() As String
        Dim rtVersion As String = String.Empty
        Dim iVersion As Integer = 0
        While (Not Integer.TryParse(rtVersion, iVersion))
            Console.WriteLine("{0}  1.{0}Framework version 1.0", vbTab)
            Console.WriteLine("{0}  2.{0}Framework version 1.1", vbTab)
            Console.WriteLine("{0}  3.{0}Framework version 2.0", vbTab)
            Console.Write("Choose the new managed runtime version: ")
            rtVersion = Console.ReadLine()
        End While
        Select Case iVersion
            Case 1
                rtVersion = "v1.0"
            Case 2
                rtVersion = "v1.1"
            Case 3
                rtVersion = "v2.0"
        End Select
        Return rtVersion
    End Function
End Class

[C#]

using System;
using Microsoft.Web.Administration;

public class AppPoolSample 
{
  static ServerManager manager = new ServerManager();
  // Main application processing
  public static void Main(string[] args)  
  {
    // Get the apppool to change
    int iPool = GetAppPool();
    // Get the framework version desired
    string rtVersion = GetVersion();
    // Set the apppool runtime
    ApplicationPool poolToSet = manager.ApplicationPools[iPool];
    Console.WriteLine(
        "Setting application pool '{0}' to runtime version: {1}...",
        poolToSet.Name, rtVersion);
    poolToSet.ManagedRuntimeVersion = rtVersion;
    // Commit the changes and recycle the application pool
    manager.CommitChanges();
    poolToSet.Recycle();
    Console.WriteLine("Your changes have been committed.");
  }
  // Prompts the user to select an application pool
 public static int GetAppPool()
 {
    string pool = String.Empty;
    int iPool = 0;
    while ((!int.TryParse(pool, out iPool)) ||
            (iPool > manager.ApplicationPools.Count || iPool <= 0))
    {
      Console.WriteLine(
                    "Available ApplicationPools: Managed runtime version");
      for (int i = 0; i <= manager.ApplicationPools.Count - 1; i++)
      {
        ApplicationPool appPool = manager.ApplicationPools[i];
        Console.WriteLine("t{0,3}.t{1}: {2}", i + 1, 
            appPool.Name, appPool.ManagedRuntimeVersion);
      }
      Console.Write("rnChoose an application pool to change: ");
      pool = Console.ReadLine();
  }
  return iPool -1;
}
  // Prompts a user to select the version of runtime they would like
  // the application pool to use
  public static string GetVersion()
  {
    string rtVersion = String.Empty;
    int iVersion = 0;
    while ((!int.TryParse(rtVersion, out iVersion)) ||
            (iVersion > 3 || iVersion < 1))
    {
      Console.WriteLine("rnt   1.tFramework version 1.0");
      Console.WriteLine("t   2.tFramework version 1.1");
      Console.WriteLine("t   3.tFramework version 2.0");
      Console.Write("Choose the new managed runtime version: ");
      rtVersion = Console.ReadLine();
    }
    switch (iVersion)
    {
        case 1:
            rtVersion  = "v1.0";
            break;
        case 2:
            rtVersion = "v1.1";
            break;
        case 3:
            rtVersion = "v2.0";
            break;
    }
    return rtVersion;
  }
}

Online Training: Beginners Guide to VB.NET

Microsoft Learning has put together a great series of training courses which are available now at a substantial discount.  Take advantage of the deal while it lasts (sale ends June 30th)!

https://www.microsoftelearning.com/visualstudio2005/#upgradingfromVB60

Also, don’t forget to check out the free training videos for VB.NET Express Edition. They provide a great starting point for anyone looking to break into the Visual Basic .NET market.

http://msdn.microsoft.com/vstudio/express/vb/learning/

Threading issue with VB.NET Default Instances

Here is an issue that showed up for one of our customers in the Microsoft forums for Visual Basic .NET.  It is NOT the typical ”cross-threaded UI update” issue that you might think. No, this one, IMO, is slightly harder to catch since no exception is thrown to let you know something has gone wrong.

One of the many new features of Visual Basic .NET are default form instances.  As the feature lists explain, a default form instance prevents you from having to “new up” an instance of a form before acting on it.  So, instead of using:

Dim frm As New frmMain
frm.Show()

I can simply use:

frmMain.Show()

This is handy, particularly for the folks coming from VB6 who are used to forms that behave in this manner.  However, default instances present a very interesting problem.

Take, for instance, a project that I create with a form (named frmMain) and a module (named BackgroundMethods.vb) .  For the form, I have simply added a multi-line text box (named txtOutput) and a button control (named button1). 

Here is the code I have in my project:

[frmMain]

Imports System.Threading
Public Class frmMain
  Private Sub Button1_Click( ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                             Handles Button1.Click
     Dim t As Thread = New Thread(AddressOf GetData)
     t.Start()
     txtOutput.Text &= "Updates complete"
     ' Break after the call above to read the value
     ' in txtOutput.Text
  End Sub
End Class

[BackgroundMethods.vb]

Imports System.Threading
Module BackgroundMethods
   Public Sub GetData()
      WaitForData("Message 1")
      WaitForData("Message 2")
   End Sub
   Public Sub WaitForData(ByVal strMessage As String)
      ' Presumably this method would actually be
      ' waiting for data from a network connection,<
      ' serial port, or other source
      Thread.Sleep(2000)
      My.Forms.frmMain.txtOutput.Text &= (vbCrLf & Now().ToShortTimeString() & _
      vbTab & strMessage)
      ' Break after the call above to read the value
      ' in My.Forms.frmMain.txtOutput.Text
   End Sub
End Module

So the simple example is that I have a button which, when clicked, will execute the “GetData” method asynchronously in a module.  That method is going to call the WaitForData method that updates the UI with two different messages (“message1” and “message2”).  WaitForData is supposed to simulate a long-running process, so I threw in the typical “thread.sleep” call to make this illusion. 

If you run this code, you will notice that no exceptions are thrown, but the UI for your form is also not updated.  Why is this?  You would have at least expected a cross-thread exception, right?

In any other managed language, this likely wouldn’t happen — namely because the “My” application is specific to VB, as well as default form instances!  In C#, if I try to update the UI from another thread, I would get an exception stating: “Cross-thread operation not valid: Control ‘txtSerialIn’ accessed from a thread other than the thread it was created on.” which could be solved by using the “Invoke” method.   

The issue with Visual Basic, in this instance, is that the default form instances are thread-specific.  So, when I try to access the form using My.Forms.frmMain from within the worker thread, a NEW default instance is created under the covers.  My calls to update the text box are then executed on the NEW instance of the form which resides in the same thread as the call to update it — hence it doesn’t throw a cross-thread exception. In the mind of the VB program, the request to update the textbox occurred without an error.  When the worker thread dies, the second instance of the form (which was never displayed) is now a candidate for garbage collection.  Meanwhile, back on the original thread and original form the textbox is left blank.  You can validate this by running the program (I have attached sample code to this blog post) and setting breakpoints on the textbox update line in the WaitForData method, and in the last line of the button1_click event handler of the code.  You will notice that after the second call to WaitForData (before the debugger exits the Sub) that My.Forms.frmMain.txtOutput.Text has been properly set to the value you expected.  However, remember that this is on a second background instance of the form, not the original one you expected.  Once the debugger hits the last line in the button1_click event handler, read the value of txtOutput.Text and realize THAT instance of the textbox was never updated.

So what is the solution?

First off, you still have to use the “Invoke solution” that is often times bandied about in threading discussions.  To do this in VB.NET (and particularly in our solution), do the following:

1. Add the following code to the top of your BackgroundMethods.vb file:

Delegate Sub UpdateTextHandler(ByVal strMessage As String

This allows you to create a delegate that can be invoked on the UI.

2. Add the following method to your frmMain file:

Public Sub UpdateTextMethod(ByVal strMessage As String)
   txtOutput.Text &= (vbCrLf & Now().ToShortTimeString() & vbTab & strMessage)
End Sub

This creates the method that will actually be executed via your delegate from the worker thread.

3. Change your WaitForData method as follows:

Public Sub WaitForData(ByVal strMessage As String)
    ' Presumably this method would actually be
    ' waiting for data from a network connection
    ' serial port, or other source
    Thread.Sleep(2000)
    Dim f As frmMain = My.Application.OpenForms("frmMain")
    f.Invoke(New UpdateTextHandler(AddressOf f.UpdateTextMethod), _
             New Object() {strMessage})
End Sub

This method now uses the form’s “Invoke” method (actually defined in Control) to execute UpdateTextMethod on the original form via the UpdateTextHandler delegate.

So what is happening is that your new thread is getting an instance to the existing frmMain instance by going through the OpenForms call.  Once I have that instance, I can invoke a delegate that points to the “UpdateTextMethod” of the existing form ( passing in the message in the object array ).  By invoking, I am able to get back on the UI’s thread and that call can execute any updates to the UI that it wishes.

Keep this in mind the next time you are not receiving errors and your UI isn’t getting updated how you would expect — particularly if you code communicates with the network, a serial device, or other device which communicates asynchronously.

202 VB.NET Samples

I just wanted to bring some attention to a resource that I think is extremely valuable for VB developers (and really, any .NET developer using any other language for that matter).  MSDN has a website with 101 Code Samples for VB.NET 2005.  There is also another website for those of you that haven’t upgraded to 2005 yet: 101 Code Samples for VB.NET 2003.  Both of these sites have very valuable sample code for doing things such as network programming, using regular expressions, executing transactions, handling/changing ACL’s on files, accessing data, performing bulk data transactions, executing asynchronous operations (one of my favorite topics), using ClickOnce, creating ASP.NET pages and more!  It is a great resource for those of you that just want to get it done and don’t want to waste a lot of time reading specifications on each class in System.Net or System.Data.  The samples are robust and provide some great starting points for anyone interested in the many topics available in these samples.  Check them out and let me know what you think!