Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Does VB6 limit the number of COM reference's an application can hold ? 1

Status
Not open for further replies.

Nimroduk

Programmer
Aug 10, 2006
77
GB
Hi all,

Ok, first off all I need to clarify that this has been working fine for a few years, the problem appears to be with the work load and not with the code...

I have a standard .exe application that utilises an ActiveX EXE server. The application is used to ping various resources and display on screen the status of the resource dependant upon the result of the ping. The actual pinging of the resource is handled by the ActiveX server.

The ActiveX server provides a single thread-per-object multiuse class interface. Each instance of the class utilises a timer to run in a pseudo-async nature.

The application creates an instance of the ActiveX server's iface per resource ping that is required. The application then passes the target IP, frequency to ping and a timeout to the ActiveX server.
Once all of the needed instances have been created and configured the application calls a "start" procedure on the object that enables the class' internal timer.

When the timer fires in each object, the object pings the resource and then returns a response to the application via a message queue.


Right onto the problem...
I have just added a lot of new resources to monitor, taking the work load from 85 individual pings to around 200.
The problem I have is that when the application attempts to create the 161st instance of the multiuse class, VB spits out an "Out of Memory" error.
I have stepped through the code and the error is thrown when the parent application's thread accesses the 161st instance of the ActiveX's multiuse class. i.e.
Code:
'This works fine 161 times...
Set loObject = new MyServer.Pinger

'On the 161st call the Configure routine returns false
'lsErrorMsg contains the "Out of Memory" Error
lbReturn = loObject.Configure(lsConfXML, lsErrorMsg)

I have tried stepping through the code on the 161st object instance but when I do, the IDE will not step into the ActiveX server, it just steps over; returning false and lsErrorMsg populated. n.b. lsErrorMsg is populated in the ErrorHandler of the Configure function.


Has anyone ever heard of a limit to the number of object references you can hold within VB ? Or does anyone have anyother thoughts on what the problem maybe ?

oh and it makes no difference if I am on my workstation, test bed or server.


 
That's interesting. I worked up this test: I created a small multiuse ActiveX EXE, set thread per object, and ran this code from another instance of the IDE:
Code:
[COLOR=blue]Option Explicit
Dim x() As Test.Class1

Private Sub Command1_Click()
Dim i As Integer

For i = 0 To 200
    ReDim Preserve x(i)
    Set x(i) = New Test.Class1
Next i
MsgBox UBound(x)
End Sub
[/color]
It ran without problems, so there isn't an inherent limitation for the number of references. Now, I can't say why yours is running out of memory, but the main reason that programs run out of memory is due to some sort of recursive procedure call. I'd rule that out in some way. The other thing is that you might go with a thread pool; I don't see off the top of my head why you need to have all of those references open at the same time although you might of course.

HTH

Bob
 
I've had programs with over 3000 active Winsock controls in a control array. No problems like those you describe, but perhaps the limitation is with your thread-per-object Class.
The big drawback to the thread-per-object model is that you have no control over the number of objects (and hence threads) that clients create. Creating too many threads will bog down the operating system in thread-maintenance overhead.

Too many active threads — that is, threads that are actively executing code — will bog down the operating system even more quickly. Generally speaking, you want about the same number of active threads as you have processors, to guarantee that there’s never an idle processor. When you have just one processor, that’s not very many active threads.
This is why VB6 offers a round-robin thread pool option: select the Thread Pool option and specify the number of threads to allow. The Pool size is really a factor of the number of CPUs and the frequency and duration of blocking operations in the threads.

You may be choking Windows' thread dispatcher.
 
Thanks both of you, I'll recompile using a thread pool and see if that fixes my problem.
 
<perhaps the limitation is with your thread-per-object Class.

Nice bit of research, dilettante. From my test I suspect that there isn't a theoretical limit (certainly if there is it isn't 160), but it wouldn't surprise me at all if there were a practical one.
 
I'd be interested to see if the change helps or hurts. I was really just taking a stab, because the symptoms don't make a lot of sense to me.

The real problem is likely to be something else entirely. Perhaps some other resource the Class is exhausting.
 
Ok,

Changing to a thread pool model made no difference to the problem but it was worth a shot !

I have partially sorted the problem out, in as much as I have raised the number of instance before the problem takes hold by reinstalling VS SP6. I am in the process of building a completely fresh XP SP2 test bed to see if the problem occurs on that platform. I have a feeling that a Windows Update could be to blame. I should also mention that I do not have the problem at all on Win2k Workstation or Advanced Server :s
 
Right, got this sorted :)

It was a mixture of poor Windows Update timing and poor design from me :/

First of all a recent MSUpdate rolled out a new version of the common dialog that does not appear to be compatible with VB6. I did write down the name (and version) of the file but I have since lost the post-it note :/

Next up was the huge, non-scaling design flaw :s
Within my ActiveX server I was using two forms to act as containers for the timer control and the winsock control; you can probably guess where this is going...

A single "ping" instance would utilises two timers and a winsock control; three form instances. Multiple this by 200 "ping" instances and you've got the server component attempting to load 600 forms and thus an "Out of Memory" error.

I initially thought (hoped) that solving the problem would be as simple as replacing the form based timer with an API based solution. It certainly helped but it just took longer to hit the "Out of Memory" error; now being caused purely by the 200 winsock container forms.

My final (and apparently stable) solution:

Create an ActiveX EXE Timer component (threadpool=1).
Replace all use of form based timers withinthe server component with the newly created timer component.

Grabbed CSocket from PSC and throughly debugged/updated/tested it. (There are some <known> bugs in the PSC release).
Added CSocket to the server component.
Replaced all use of the winsock control with CSocket.

Finally I did some thorough blackbox and load testing.
I am now confident that I can run upto at least 3000 instances of my "ping" class without running into problems.
 
Also, the unattended execution property is worth investigating, if you're loading a lot of forms with no user interaction. But replacing all of the controls with classes is of course the more robust way to go. Nice work Nimrod, and a star for documenting your solution so well.

Bob
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top