Tricky problem:
I'm creating a framework. One of its components is a form manager
class, based on the Custom class. My form manager class has an
OpenForm() method. The OpenForm() method can accept a form name and
some optional parameters, and it will open the named form, whether
it's a .scx requiring "DO FORM..." or a .vcx or .prg class requiring
"CREATEOBJECT()". All forms in the framework are based on a form class
that contains code in the Init and Destroy methods to register the
form with the form manager when the form is created and unregister it
when it's destroyed.
All of this works great while my program is running. I can create and
destroy forms with no problem. And the program shuts down fine,
leaving no visible trace. Until...
If I run my uncompiled program in the VFP IDE, and my form manager
opens a form using CREATEOBJECT(), then after my program shuts down
I'll get a "Remove classes from memory?" dialog when I try to open my
base form .vcx library in the Project Manager to edit a class.
If I run the same program as a compiled .exe, and my form manager
opens a form using CREATEOBJECT(), I don't have any problem opening
the .vcx afterwards.
The only way that I can stop this from happening in the IDE is to
issue an explicit CLEAR CLASS [class name] command, for the class that
was opened using CREATEOBJECT(), at the very end of the program, just
before it shuts down. Obviously, in a production environment this is
not good, because I'd have no sane way of knowing which classes had
been instantiated with CREATEOBJECT() and I'd have to loop through all
the records in all the .vcx files, and parse all the .prg files, to
get all the class names and issue CLEAR CLASS for them, before
shutting down.
Please note the following:
1. This problem does not occur when the framework invokes DO FORM to
open a .scx that is based on a .vcx class.
2. This problem occurs whether the class being instantiated is in a
.vcx or .prg file, and whether it's based on my base form class or a
subclass of that class. It is not dependent on any particular class;
it occurs for any use of CREATEOBJECT() to open a form.
3. I've tried SET CLASSLIB TO and SET PROCEDURE TO at the end of my
program and this doesn't solve the problem. SET("CLASSLIB") and
SET("PROCEDURE") both return an empty string, but the problem doesn't
go away.
4. RELEASE ALL at the end of the program doesn't help either. On the
other hand, RELEASE ALL doesn't actually seem to release all--the
handful of PUBLIC variables that my main program sets up are still
there in a DISPLAY MEMORY even after issuing RELEASE ALL.
5. CLEAR MEMORY also doesn't do it.
6. Issuing CLEAR CLASS [class name] from the Destroy() or Unload()
method of the problematic class does not work. It doesn't cause an
error, but it doesn't CLEAR the CLASS either. For the CLEAR CLASS to
work, it has to be issued at the very end of the program.
7. My form manager STOREs .NULL. to all form reference variables when
the forms are destroyed.
8. And the program shuts down fine--nothing left in the Task List, and
the class library that contains the form manager class can be opened
with no problem after shutdown. To me, this implies that there are no
dangling object references.
I think CLEAR ALL would solve the problem (it does in the Command
window), but I can't issue CLEAR ALL anywhere inside my program, not
even as the second-to-last line, just before QUIT. If I do, then, due
to VFP's perplexing event sequence (which doesn't actually release
objects at the moment when their Release() methods are called or when
a RELEASE command is issued, nor does it actually destroy objects at
the time when their Destroy events occur), something or other that is
dependent on some memory variable can't actually finish releasing, and
I get a cascading series of errors. (Yes, I've read the help on that.
Since my program works fine as long as I don't issue CLEAR ALL, do I
really need to care about this? Should I really have to expect to
issue CLEAR ALL in order to ensure that what SET CLASSLIB TO is
supposed to do actually gets done?)
I've deliberately kept a lot of detail out of this post. I CANNOT
reproduce this problem in a simple scenario outside of my framework,
which leads me to believe I've made a mistake somewhere, but I can't
find it for the life of me. On the other hand, since the problem
doesn't occur in a compiled .exe, do I really have anything to worry
about?
I'd be glad to post more code--even the whole project in a zip
file--if someone would like to take a look at it.
I'd appreciate the benefit of your experience. Thanks in advance.
Ken Dibble
Southern Tier Independence Center