Metamorphing Machine I rather be this walking metamorphosis
than having that old formed opinion about everything!

Got a VB6 legacy project. Now what? Part 2

This is the second part of this post.

If you've been assigned to a legacy VB6 project, chances are that's because it has some error you need to find and fix.
Finding the precise spot where the error happens usually is a tough task.
Something that can help is a project called HuntERR. It can be found here.

Usually, I grab it, unzip it, and add its frmShowError.frm and HuntERR31.bas to the project I'm working on:
Right-click project's name in Visual Studio's right panel, select AddAdd file..., and browse to the folder where they are.
You will need to do it twice, once for frmShowError.frm and again for HuntERR31.bas, as VS will not allow you to multi-select them.

As none of the projects I worked on were COM+, I needed to set HuntERR's H_NOCOMPLUS conditional compilation variable.
If you do not do such a thing, you'll get an error stating HuntERR could not find GetObjectContext.
Go to Project menu → Project properties...Make tab → Conditional Compilation Arguments field.
Paste H_NOCOMPLUS = 1 there. If there are already other settings there, use a colon to separate them from this new one.

Now comes the hard part. For each Sub, Function, or Property except for one (more about it later), you will need to set an error handler.

At the very first line of each of them, you'll need to add On Error GoTo ErrHandler. (I'm assuming none of them already have an error handler.)
Then, after the very last line inside the subprocedure, you'd write:

Exit Sub 'or Function, or Property

ErrHandler:
ErrorIn "name-and-args", Array(<list-of-args>)

name-and-args should be replaced by the name of the subprocedure and a comma-separated list of its arguments' names inside parenthesis.
<list-of-args> should be replaced by the name of the subprocedure's arguments.
Let's go for an example to make it clearer. Suppose you have the following function signature:

Public Function GetResource(InstanceHandler As Long, Identifier As Variant, Optional ResourceType As Variant = ResourceTypes.StringEntry, Optional LangId As LangIds = LangIds.Neutral) As Variant

Then your last line should be like this:

ErrHandler:
ErrorIn "GetResource(InstanceHandler,Identifier,ResourceType,LangId)", Array(InstanceHandler, Identifier, ResourceType, LangId)

You may want to include form's name too, if the subprocedure is inside one:

ErrHandler:
ErrorIn "frmMain.GetResource(InstanceHandler,Identifier,ResourceType,LangId)", Array(InstanceHandler, Identifier, ResourceType, LangId)

Now, about that "except one" above: In the main subprocedure - that one that calls all others - you should write something like this (new code is in bold):

ErrHandler:
ErrorIn "frmMain.GetResource(InstanceHandler,Identifier,ResourceType,LangId)", Array(InstanceHandler, Identifier, ResourceType, LangId), EA_NORERAISE
frmShowError.ErrorReport = ErrReport
frmShowError.Show

Why all of this? Because when an error occurs, you will get a nice window with lots of information about it:

HuntERR info window

This should help pinpoint what has gone wrong and where.
Another thing you should consider is adding line numbers to subprocedures.
It is really helpful to have that information in the window shown above.

Andrej Biasic
2020-04-08