tags:

views:

201

answers:

4

I have a VB6 program which I've been maintaining for ten years. There is a subroutine in the program called "Prepare Copy", which looks like this (Edited to add changes):

Public Sub PrepareCopy()

On Local Error GoTo PrepareCopyError

MsgBox "in modeldescription preparecopy"

Set CopiedShapes = New Collection

MsgBox "leaving preparecopy"
Exit Sub
PrepareCopyError:
    MsgBox Err.Description, , Err.Number
    Resume Next
End Sub

Where CopiedShapes is dimmed out as a VB6 Collection.

That code is now kicking out a Runtime Error 5 -- Invalid Procedure Call or Argument. It appears from the interstitial debugging code that the error arises between the MsgBox "in modeldescription preparecopy" and the MsgBox "leaving preparecopy" lines, and that the On Error code path never executes.

Once the Error 5 dialog box is cleared, then the MsgBox "leaving preparecopy" dialog appears, after which the program closes out.

It's behaving this way on my development machine and two client computers.

It is only happening in runtime code, and does not appear to make a difference whether I compile it or use P-Code

What I'm asking for here is speculation as to what causes this sort of thing to happen.

A: 

Does the error occur only on machine/s different than where the code was compiled? It may be that some DLLs are missing in the runtime environment (like MSVBRT.dll or the like).
Easiest way to figure it out: run on the build machine.

If it doesn't happen there, you can create a deployable version (an installer) through VB6) that will include all that's needed. OR, use SysInternals' Procmon to monitor the process as it runs and see which resource (file, DLL, reg key) it's attempting to access at the time of the error.

Traveling Tech Guy
It has been reported on three or four very disparate machines worldwide. The dev machine is the build machine. The problem manifests only outside the VB6 debugger.
Rob Perkins
Oh, and: Doh. I didn't think to use Procmon. thanks for the reminder
Rob Perkins
There are a few other tools to catch a problem that occurs "in the wild" only. But start with ProcMon.
Traveling Tech Guy
ProcMon revealed nothing germane, unfortunately. See the edits to my problem description.
Rob Perkins
+2  A: 

Your recent comment says that the variable is Public CopiedShapes as New Collection.

  1. Could you try removing the new in the declaration?

  2. Are there any instances in the Collection with a Sub Class_Terminate() which are being called when the old Collection is garbage collected along with its contents?

quamrana
That declaration "As New" looks like the culprit. It's always a good idea to avoid that declaration, unless there is a very, very good reason. Of course, this could have been cleared up quicker if Rob could have posted a bit more code.
Mark Bertenshaw
VB6 doesn't perform garbage collection, that I can recall. It's a COM monster; it uses reference counting. The code is part of a VB6 class which has worked for ten years, only exhibiting the behavior I'm describing here very recently.
Rob Perkins
removing "As new" did not change the behavior of the code.
Rob Perkins
Mark's comment is germane, but I'm not sure which additional code would be helpful. The application itself is now a complex mixture of an MDI VB6 program, a significantly large COM-interop .NET dll, and some very old code which taps Direct 3D 3 for graphics display. The whole application is 10 years old, modified piecemeal over time, and the problem has manifested itself only in the last month in a part of the code that literally hadn't been modified in ten years.
Rob Perkins
@Rob: The point I'm trying to make is that VB6 **does** collect garbage, yes its reference counting, so it could be collecting some garbage when it gets rid of the contents of the old collection. Perhaps you could log the old contents beforehand.
quamrana
We did some intensive troubleshooting today, and were pretty much able to determine that the error is kicking back from inside the msvbvm60.dll's "MsgBox" function call. When we started removing calls to `MsgBox` from the control flow, the error and the crash happened at different and later locations. One colleague suggested replacing all calls to `MsgBox` with calls into a similarly functioned VB form, designed to behave like a MessageBox, which would be a way to avoid hitting the failure condition, but would not identify a root cause. At this point, MS has a crash dump and is evaluating.
Rob Perkins
A: 

What happens if you add a new variable in this function and then assign a new collection to it? Does this create the error too?

dim x as collection
msgbox "After Dim X"

Set X = new collection
msgbox "After Set X"

Perhaps the problem lies outside of this function ... ?

AlexS
+1  A: 

Procmon was not helpful. Microsoft took a deep-dive image of the system at the point the problem was occurring.

They found a bad HRESULT failure code. Something deep in the VB6 runtime was attempting to access a late-bound but undeclared object through an IDispatch interface. They swiftly scheduled a call with me. I asked which object was being called through IDispatch; there are no late-bound calls in the VB6 portions of my project. ("option explicit")

In the call, they told me to go away. They refused to help identify further root causes, because VB6 code was involved and the support department is forbidden to troubleshoot VB6 problems. I countered that this was clearly a problem in the supported runtime, to no avail.

Instead, I got a 20 minute lecture on the economies of supporting something when there was no money to be made. I replied with a war story about a thrice-virtualized application still running after 35 years at a phone company.

They were good guys; they wanted to help me solve the problem but their policies forbade it. I'm no closer to a root cause today than I was when I posted the question.

However, if you call MsgBox from the VB6 runtimes, it sends WM_USER messages to other VB6 forms in the project. That, in turn, in my case, triggered MDIForm_Activate in which there was this code:

Me.TreeView1.SetFocus

And that, in spite of the fact that TreeView1 was by definition declared explicitly, was what they declared as the cause of the failed late-bind IDispatch call, all the while refusing to explain how that could be. The guy on the phone even went so far as to say that he absolutely could figure it out, but Microsoft policy forbade it, because that's VB6, there.

Removing the SetFocus call removed the circumstance which produced the error.

Rob Perkins
Rob - That sounded like a real PITA - Microsoft, whilst I really rate them highly on development tools, have really screwed everyone over on support, including themselves. Good catch on the solution, though!
Mark Bertenshaw
It's a side effect of hiring legions of 22 year olds, I think, whilst also burning them out after between five and ten years. I was just as insufferable at that age as the VB6 policy is. Thanks for the kudos.
Rob Perkins