Tuesday, November 06, 2007

VB Must die

I'm sure there are some excellent VB programmers around it's just that I've never met any. Judging by the quality of the VB code I've been looking at recently, none worked at the place that wrote that code.

There is something about VB that seems to invite cowboys and morons to it. Guys that were flummoxed by C/C++ or Delphi, but still wanted to get on the IT Gravy train. I guess it's easy to get something up and running, and without a lot of forethought or future thinking. Just put all our business rules on the click event of this button, no one will ever need to change the code, or the UI.

The language itself encourages laziness.

On Error Resume Next

WTF!

Sure the program has just come back from catastrophic error, but I'll still try and plug on anyway. What's the worst that could happen?

variables don't need to be defined before using

VB guys counter this by saying, "Always use Option Explicit". Well if you should always use it, why isn't it turned on by default? Or better still why give the choice?! Sugrue rule number 1 states that if you allow a programmer to be lazy, they will be.

The single worst thing about VB to me though is the use of braces when calling a method with parameters. Or not.

myMethod parmeter1, parameter2

is valid, infact that's how you do it. However if you are assigning the return of a method to a variable, put the brackets in:

myVar = myMethod(parameter1, parameter2)

why? What sane person thought of this? WTF is the point. Either use it or don't.

Which brings me to VB.NET. Obviously it is real OO and utilises the .NET framework so is built on the foundation of brilliance, but what is the point? If it's to allow VB coders an easier starting point to .NET, then they are kidding themselves.

Microsoft should doing everyone a favour and send VB for a long walk off a short pier. Either learn C# or flip burgers instead.

Sometimes you have to be cruel to be kind.

When is String.IsNullOrEmpty doesn't work

If you read my previous post you'd know I was involved in some C#->VB COM developing. Well actually VB->C# to be 100% correct.

Anyhoo, in VB you can define a string with a length eg

Dim suckyVBString as String * 9

which defines a string of max length 9 characters. When this string is part of a user defined VB type (or "class") then when the type/class is newed then suckyVBString will be "         " rather than "".

So a little unexpectantly doing a String.IsNullOrEmpty(suckyVBStringFromCOM) returns false

a little browse of the IL shows that IsNullOrEmpty contains this code (not exact code I am blogging on my mac but you get the point):

public bool IsNullOrEmpty(string s)
{
   if (s != null && s.Length > 0)
      return false;
   else
      return true;
}
so while the Marshaled VB string is full of 9 nulls (char /0), it is neither null or empty according to the code.

A quick visit to String.Trim() sorts all this out, but it's a trap for young players if you're not careful.

C#->VB6->C# Sucks

The current project I am working on is erm, interesting. The scope of the project is to replace VB business logic, with C# and SQL Server. The major caveat is that the VB front end MUST stay intact. Not only intact but untouched.

Fun fun.

Most things just work out of the box. Obviously all classes must be exposed with [ComVisible(true)] attributes and given a GUID. Even though Microsoft states that you shouldn't =, we also had to use the AutoDual visibility attribute to get VB to see the public methods.

The VB code we have to interface to uses classes with property method indexers. These are probably called something else in VB, But I call it this. These look something like this (going OTOH so might get syntax wrong)

Public Property Let Reminder(index as Integer) as String
Public Property Set Reminder(index as Integer) as String

instead of being marshaled as

public string[] Reminder
{
get;
set;
}

as you might think, this gets Marshaled as

public string get_Reminder(short __p1);
public void set_Reminder(short __p1, string __p2);

which is of course correct. This is not a major, but the VB code does need changing. Also if the underlying VB array that the properties refer to are not zero based then the C# code then has to take this into consideration when dealing with the index.

oh and any normal VB property that is assigned with ByRef or more to the point, with ByVal not added then the properties get marshaled as above.

Like I said fun fun.

Heres the "killer feature" though. If you are doing Unit testing in C# of the VB Business logic that includes your C# that includes VB arrays that are marked by ref, well it won't work. It falls down in the re-marshaling back into C#. (Don't ask).

But with a bit of reflection foo it is possible.

First you have to get the type using

Type t = Type.GetTypeByProgID("mydll.dll");
object objectInstance = Activator.CreateInstance(t);

then we just simply go InvokeMethod with the Class Name, Method Name and any method arguments in an object array.

I'll post the code - don't have it on hand at the moment.

Anyway, if you can avoid COM and .NET do so. Well VB frontend to C# code anyway...