Convert WinForms and Web Application VB .NET Projects to C#

Category: .Net
Steps to successfully convert VB .NET to C# Projects

Have you inherited VB .Net projects those were developed over years by multiple developers? You would definitely resonate my feeling when you think about converting VB .Net projects to C#. Of course, this conversion is not for faint of heart but careful planning, a good conversion tool and ample testing will help you successfully convert the project.

I have successfully converted WinForms and Web Forms applications from VB .Net to C# with shunya zero nada issues.

Conversion issues and resolutions heavily depend on underlying code but this article gives you guideline and ways to tackle various issues.

Conversion tool can convert the code from VB .Net to C# as accurately as the quality of your VB .Net code. If your VB .Net code is in poor quality, your C# conversion will be abysmal at best. You will spend your entire life comparing the VB .Net with converted C# logic to make sure the complex logic is preserved.

The first step in VB .Net to C# conversion is to prepare your project/solution for conversion.

VB .Net to C# Conversion Preparation

The preparation is the single most important part of your successful conversion. This holds true regardless of the project type or size you are working on.

VB .Net is notorious for allowing developers write bad code. VB .Net project settings allow loosely typed, case insensitive code. We want to force the VB .Net code to be as strongly typed as possible.

Set Option Explicit to On

If you already have Option Explicit set to On, skip this step and go to the next step.

For all projects in a solution, go to Project Properties > Compile > set Option Explicit to On.

VB Project Compile Options

Compile the solution.

Mostly your code will compile with no errors. If you get compiler errors, this is a warning sign of lots of errors ahead due to extremely poor quality code.

If you get compiler errors, at this stage, you will get errors for undefined variables. Define those variables to make compiler happy.

Set Option Strict to On

If you already have Option Strict set to On, skip this step and go the next step.

For all projects in a solution, go to Project Properties > Compile > set Option Strict to On.

Compile the solution.

At this stage all the errors are due to loosely typed code. Either variables are not defined with proper data type or proper casting/conversion is not performed.

You may fix these errors by defining variables with correct data type and making sure proper data type conversion/casting is taken place.

Set Option Infer to Off

If you already have Option Infer set to Off, skip this step and go the next step.

For all projects in a solution, go to Project Properties > Compile > set Option Infer to Off.

Compile the solution.

This is an extension to Option explicit On. Mostly variables are not defined with proper data type or proper casting/conversion is not performed.

You may fix these errors by defining variables with correct data type and making sure proper data type conversion/casting is taken place.

Verify Individual Code File for Options

In VB .Net you can set Option Explicit, Option Infer and Option Strict at the beginning of VB file. It overrides those settings defined at the project level. You want to verify each VB file to make sure that these options are correctly set or does not exists within individual VB file.

If the options are not correctly set, you may want to completely remove those options. So now code relies on project level settings.

Compile the solution.

If compiler gives error, fix those errors by referring to resolution in previous steps.

Convert Module to Class

In C#, there is no concept of Module. You are better off converting Module to Class before C# conversion.

VB .Net Module may encompass following items:

  • Variable Declaration
  • Methods
  • Class/Enum definitions
Module GlobalConstants
	
	Public PATH_CSVFILES As String = ConfigurationManager.AppSettings.Get("exportToCsvFilePath")
	Public PATH_VIRTUALTEMPDIR As String = ConfigurationManager.AppSettings.Get("tempVirtualPath")

	Public Const EMAILFORMAT_HTML As Boolean = True
	Public Const EMAILFORMAT_TEXT As Boolean = False
	
	Public Sub LogError(ByVal errorMessage As String)
		' some logic to log error
	End Sub
	
	' Enum definition within module
	Public Enum PRIVILEGE_LEVELS
          Admin
          SuperUser
          Manager
          User
	End Enum
	
	' class definition within module
	
	Public Class BigButton

          Public Sub Render()
		' some code for rendering button
          End Sub
    End Class
	
End Module

For each of these items, you will need to tweak your code in the following way.

Change Variable Declaration

Change any non-constant variables to shared variables and rename it with following naming convention

As per above module example, rename the variable PATH_CSVFILES to GlobalConstants_SOME_RANDOM_ALPHANUM_PATH_CSVFILES.

Tips: Use Visual Studio refactoring tool to rename this variable in entire solution.

Rename the variable EMAILFORMAT_HTML to GlobalConstants_SOME_RANDOM_ALPHANUM_ EMAILFORMAT_HTML.

Rename all constant variables to this specific naming convention. If you need to know the reason why we are renaming it this specific way, keep reading and you will find the answer.

Change Method Declaration

Change method to Shared and rename the method with following naming convention.

Rename the sub LogError to GlobalConstants_SOME_RANDOM_ALPHANUM_LogError

Move Class/Enum Definition

Move Class/Enum definition out of module and preferably in its own file.

Change Module to Class

Now we are ready to convert GlobalConstants module to class. Just replace the word “Module” with “Class” within the definition.

Using Visual Studio, blindly search and replace “GlobalConstants_SOME_RANDOM_ALPHANUM_” with “GlobalConstants.”. Of course, this action will rename your variables and methods within GlobalConstants as well so you will need to fix those in a single file.

Now when you compile the code, it should compile without any error and you will have the same functionality.

Fix Common Function Calls Without Parentheses

VB .Net allows you to call functions without using parentheses. For example some_str_var.toupper is a perfectly valid code in VB .Net. You may want to change the function name to proper case along with parentheses. You may use Visual Studio Search and Replace dialog box and use following negative lookahead regex to replace these common functions.

  • Replace .ToString(?!() with .ToString()
  • Replace .Trim(?!() with .Trim()
  • Replace .ToUpper(?!() with .ToUpper()
  • Replace .ToLower(?!() with .ToLower()

You may also fix any other function calls that has missing parentheses using similar regex.

Explicitly Initialize Enum

Uninitialized enum property has different default value in VB .Net and C#. Make sure to manually verify each enum property or variable has default value.

Convert VB .Net to C#

We have used Instant C# tool for VB .Net to C# conversion but you can use any tool that is available at your disposal.

For us, Instant C# gave us highly accurate result because the above preparation converted poorly written code into satisfactory code.

Post Conversion Issues and Fixes

Even though Instant C# can convert VB .Net to C# with high accuracy, you may still find some issues with conversion. Some issues are flagged by compiler and easy to fix whereas some issues are logical issues and you will need to manually verify the conversion.

Gotchas for CInt, CDec, CBool Equivalent Conversion

VB .Net CInt, CDec, CBool and other C* functions are converted to equivalent int.Parse, decimal.Parse, bool.Parse or Convert.ToInt32, Convert.ToDecimal, Convert.ToBoolean.

VB .Net handles CInt, CDec, CBool more graciously but C# equivalents are very strict.

For example CInt can convert string “2.45” to integer 2 but int.Parse or Convert.ToInt32 will throw an error.

CBool can convert string “0”, “no” to False but bool.Parse or Convert.ToBoolean will throw an error.

You will need to search for these usage within entire solution and manually verify what kind of data you are feeding to these functions.

If needed, you may create a utility class with static function to handle your own conversion and replace *.Parse or Convert.To* with your own static function.

Methods with Optional Parameters

VB .Net Methods with optional parameters are converted into multiple methods. In rare scenario, it will result in two methods having the same name and same number of parameters. You will need to manually modify the needed code and use correct method.

Select Case to Switch/if-else Conversion

Select Case to Switch conversion is straight forward. But in complex scenario, the tool may change certain variable and its data type. You may want to have a glance at all switch/if-else statements to make sure that the logic and variable data types are correct.

Reference Variable Nightmare

In .Net, you will not need to pass variable parameter by reference in 99.99% of cases for applications.

If you are passing variables by reference in VB .Net, it will be a nightmare after conversion. You must verify the code to make sure the logic is kept intact.

I had to deal with reference variable in my project. Even though the application did not need reference variables, the projects I inherited had many functions with parameters passed by reference. Even the custom dlls were created with function parameters passed by reference.

Wherever I could change, I removed reference variable from the source code.

Wherever I am forced to use reference variable by calling in-house dll functions, I created static functions within Utility class or used extension methods to fix the issues.

Function Calls Without Parentheses

Note: Compiler can easily catch this error and it is easier to fix.

VB .Net allows calling functions without parentheses. For example some_str_var.toupper is perfectly valid in VB .Net. You need to replace those function calls with parentheses and proper case so replace it with some_str_var.ToUpper().

Function Call vs. Indexers

Note: Compiler can easily catch this error and it is easier to fix.

VB .Net uses parentheses for function call as well as for indexer property. When the code is converted to C#, parentheses are kept as-is for indexer property. Compiler will show an error that you cannot call a function. All you need to do is replace parentheses “( )” with square brackets “[ ]” to make the compiler happy.

RadioButton.CheckedChanged May Not Work As Expected

In VB .Net RadioButton.CheckedChanged is fired for the first time on form load. In C#, the same event gets fired only when you programmatically change the value or user selects a radio button. Difference in the way CheckedChanged event is fired is caused by the way VB and C# compiler generates code.

Post Conversion Issue Fixes for WinForms Application

In WinForms application, the UI code itself is a VB .Net code so the conversion process correctly converts the UI code to C#. You may still find some scenario where the converted code is not correct.

ResourceManager error

Even though the ResourceManager had correct namespace, it was throwing an error.

This error stem from MyResource.Designer.cs file. Here ResourceManager is using the wrong key to retrieve resources due to resource namespace change. For example, MyResource class would look like this:

namespace Apps.NS1
{
	namespace Properties
	{
		[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0"), System.Diagnostics.DebuggerNonUserCodeAttribute(), System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
		internal class MyResource
		{
		// all class code
		}
	}
}

But the ResourceManager property is trying to get resource using a wrong namespace. Make sure your resource namespace is correct.

	[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
	internal static global::System.Resources.ResourceManager ResourceManager {
		get {
			if (object.ReferenceEquals(resourceMan, null)) {
				global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WRONG_NAMESPACE", typeof(MyResource).Assembly);
				resourceMan = temp;
			}
			return resourceMan;
		}
	}

Event Handlers

Instant C# either converts design time event handlers to match C# coding practice or creates a function SubscribeToEvents and automatically adds this function call at the end of constructor.

Sometimes the event handlers do not have needed parameters. VB .Net does not complain but C# does. So you will need to properly define event handlers with appropriate parameters.

If the event handlers are added to SubscribeToEvents and if the controls are not initialized, it will throw most beloved “Object reference not set to an instance of an object” exception. The root cause of this error is due to calling SubscribeToEvents function within the constructor which is too early and some UI controls are not created. To solve this error, move this function call from constructor to any other place where you know that all the UI controls are properly initialized.

App.Config Changes

If your app.config file contains key value pair which requires CInt, CBool or any C* functions for strongly typed values, make sure the values are valid as C# is more strict to parse or cast values.

Verify Winform UI in Design Mode

Open every single form in a design mode. This will help quickly identify hidden underlying issues. But before you do that, you must have your code compiled without any error.

Post Conversion Issue Fixes for WebForms Application

Instant C# has limitation that it cannot convert any aspx page inline code. You need to manually make a change within code.

Change Language within aspx Pages

Search for Language="vb" string and replace it with Language="C#" within entire solution.

Search for .aspx.vb string and replace it with .aspx.cs within entire solution.

Replace Inline Code Block

Search for <script RunAt="server"> within your solution. This is a custom code block written within the page. You need to manually convert this code block to C#.

Validate Code Within <% %> block

Search the entire solution for <% and for each line, make sure your code is correct.

  • Replace string concatenation character “&” with “+”.
  • Function name is using correct case. For example, replace “bind”, “eval” function call with “Bind” and “Eval” respectively.
  • Any data type conversion and casting (particularly string to any other data type like Integer, Decimal, Boolean etc) must be explicitly performed.
  • If-Else conditions must be replaced with either inline equivalent code or a separate function within aspx.cs file.

Event Handlers

Instant C# creates OnInit function within .aspx.cs file and adds all the event handler wireups. It is also possible that you already have similar event handle already available within aspx file. For example aspx code will have :

<asp:Button ID="btnUpload" runat="server" OnClick="btnUpload_Click" Text="Upload" />

and within aspx.cs file, you will have:

override protected void OnInit(EventArgs e)
{
//INSTANT C# NOTE: Converted event handler wireups:
	this.Load += Page_Load;
	btnUpload.Click += btnUpload_Click;
	base.OnInit(e);
}

So you may want to remove the event handler wireup within OnInit function.

Web.Config Changes

If your web.config file contains key value pair which requires CInt, CBool or any C* functions for strongly typed values, make sure the values are valid as C# is more strict to parse or cast values.

Verify Webform UI in Design Mode

Open every single webform in a design mode. This will help identify hidden underlying issues. But before you do that, you must have your code compiled without any error.

Testing

I cannot stress enough how much important this step is. You will need run the application in DEV environment, open each and every form/page and click on each and every button to ensure that your application is working fine without any issue.

Share

0 comments

Your email address will not be published.