DOCUMENT:Q253879 13-MAY-2002 [foxpro] TITLE :BUG: Forms Cannot Find Class Library When Calling .Exe File PRODUCT :Microsoft FoxPro PROD/VER:WINDOWS:6.0 OPER/SYS: KEYWORDS:kbProjManager kbvfp600 kbvfp600bug kbXBase kbGrpDSFox kbDSupport kbCodeSnippet ====================================================================== ------------------------------------------------------------------------------- The information in this article applies to: - Microsoft Visual FoxPro for Windows, version 6.0 ------------------------------------------------------------------------------- SYMPTOMS ======== When attempting to run a form contained in an application (.app) file that is called from an executable (.exe) file, an error message is generated: Error instantiating class. Cannot find ... CAUSE ===== Visual FoxPro cannot find the class libraries for the form even though they may already be in memory. Because the libraries were excluded from the .app file, the form is looking for them on disk, and they cannot be found. Visual FoxPro does not search memory to find these missing classes. RESOLUTION ========== The resolution to the problem is to clear the ClassLoc field for all records in the form.scx file and load the class libraries that are in the main .exe file. To clear the Classloc field in the form, issue the following code. NOTE: Ensure that you make a backup of the form or forms that are being updated. UPDATE MyForm.SCX SET ClassLoc = "" Microsoft provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose. This article assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. Microsoft support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. If you have limited programming experience, you may want to contact a Microsoft Certified Partner or the Microsoft fee-based consulting line at (800) 936-5200. For more information about Microsoft Certified Partners, please visit the following Microsoft Web site: http://www.microsoft.com/partner/referral/ For more information about the support options that are available and about how to contact Microsoft, visit the following Microsoft Web site: http://support.microsoft.com/default.aspx?scid=fh;EN-US;CNTACTMS Copy the following code to a program (.prg) file called "SetFormClasses" (without the quotation marks): *!* Forms in VFP6 cannot find the classlibs used in the Form Designer if they *!* are excluded from the APP project but are loaded in the calling EXE. This *!* program (SetFormClass) will load necessary classlibs in an APP called *!* from an EXE and is dependant upon the classlibs in the EXE because they *!* are excluded in the APP. *!* *!* One important aspect to note is that the classlib field in the SCX file *!* must be cleared. Be certain to make a copy of the form before clearing *!* the classloc field, or you may not be able to open the form in the Form *!* Designer. The classlibs needed for the form will be loaded in in this *!* program if they are passed as parameters. *!* *!* Parameters: *!* tcClasslib1 *!* tcClasslib2 *!* tcClasslib3 *!* ... *!* tcClasslibN *!* *!* Syntax: *!* DO SetFormClasses WITH "MyClass1", "MyClass2", "MyClass3", ... "MyClassN" *!* *!* This program must be run from the main program in the APP. *!* The variable lcCurrentAPP is assuming that this program is called *!* from the main program. *!* If an APP has a classlib loaded and it is unloaded here, then *!* returning to that APP will cause errors. Need to release and reload the *!* classes in MAIN, but only if they are not loaded with the IN clause. #DEFINE SFC_ADDITIVE .T. *!* Add as many parameters as necessary to cover all necessary classlibs *!* used in this module LPARAMETERS tcClassLib1, tcClassLib2, tcClassLib3, tcClassLib4, tcClassLib5, ; tcClassLib6, tcClassLib7 && Etc. LOCAL lcClass, lnClass, lnIndex, nPos1, nPos2, nChars, nChar0, nChar1, lnParams LOCAL ARRAY laClass[1], laParams[1] LOCAL lcMainEXE, lcCurrentAPP lnParams = PCOUNT() *!* Load the parameters into an array for quick reference DIMENSION laParams[lnParams] FOR lnIndex = 1 TO lnParams laParams[lnIndex] = UPPER(EVAL("tcClassLib" + TRANSFORM(lnIndex))) ENDFOR lcMainEXE = SYS(16, 1) && Main EXE lcCurrentAPP = SYS(16, PROGRAM(-1) - 1) lcClass = SET("CLASSLIB") IF EMPTY(lcClass) *!* This is the first time in. In addition, no class libraries have been *!* set anywhere. Set them all here. All classes will come from params = ACOPY(laParams, laClass) lnClass = lnParams FOR lnIndex = 1 TO lnClass laClass[lnIndex] = laParams[lnIndex] DO SetClassLib WITH laClass, lnIndex, lcMainEXE, SFC_ADDITIVE ENDFOR ELSE *!* Class libraries have been set at some point in time. We don't know *!* where they have been set, or if they are needed. So, go through and *!* try to add each class library back into the the system. Need to check *!* if it is already loaded "IN" somewhere. If it is, then do not touch it. *!* If it is not, compare it with the required classlibs passed in by *!* parameters. If it is not in the parameter list, leave it alone. If it *!* is in the list and loaded "IN" already, then don't process them again. *!* Also need to check for new class libraries being added by this module. *!* If they are not found, add them in. lnClass = OCCURS(",", lcClass) + 1 && Number of libs in memory DIMENSION laClass[lnClass, 3] laClass = .T. *!* laClass[1,1] is the class *!* laClass[1,2] is the class with full path *!* laClass[1,3] is a flag indicating this needs to be loaded nPos1 = 1 *!* Place the stem (filename only) into an array for resetting later FOR lnIndex = 1 TO lnClass nPos2 = AT(",", lcClass, lnIndex) && Locate the end of the class string nChars0 = IIF(nPos2 < 1, LEN(lcClass), nPos2 - nPos1) *!* Need to make sure that you don't include any IN clauses in the substring, *!* 1) they will throw off the JUSTSTEM function *!* 2) FoxPro already knows where to find them. nChars1 = AT(".VCX IN ", SUBSTR(lcClass, nPos1, nChars0)) IF nChars1 > 0 *!* VFP already knows where to find this classlib. Do not reload this classlib laClass[lnIndex,3] = .F. ENDIF nChars = IIF(nChars1 = 0, nChars0, nChars1) laClass[lnIndex,1] = JUSTSTEM(SUBSTR(lcClass, nPos1, nChars)) laClass[lnIndex,2] = SUBSTR(lcClass, nPos1, nChars0) nPos1 = nPos2 + 2 && New starting position ENDFOR *!* If the loaded classlibs are also parameters, *!* mark them for release is not already marked for release FOR lnIndex = 1 TO lnClass IF ( laClass[lnIndex,3] = .T. ) *!* Still marked for reload. Passed the "IN" clause IF ( ASCAN(laParams, laClass[lnIndex,1]) = 0 ) *!* This module does not know about this class, don't unload it laClass[lnIndex,3] = .F. ELSE *!* This will be reloaded later RELEASE CLASSLIB &laClass[lnIndex,2] ENDIF ENDIF ENDFOR *!* Load all eligible classlibs FOR lnIndex = 1 TO lnClass IF laClass[lnIndex,3] = .T. *!* Classlib was previously released, add it back in DO SetClassLib WITH laClass, lnIndex, lcMainEXE, SFC_ADDITIVE ENDIF ENDFOR *!* Now try to load all the classes that were passed in as parameters. *!* Those that don't load here will be loaded next = ACOPY(laParams, laClass1) lnClass = lnParams FOR lnIndex = 1 TO lnClass laClass1[lnIndex] = laParams[lnIndex] DO SetClassLib WITH laClass1, lnIndex, lcMainEXE, SFC_ADDITIVE ENDFOR ENDIF *!* Check the parameters array for any classes that need to be loaded *!* for this module. Add them if necessary. FOR lnIndex = 1 TO lnParams IF ASCAN(laClass, laParams[lnIndex]) = 0 AND ; ASCAN(laClass1, laParams[lnIndex]) = 0 && Add the new library SET CLASSLIB TO (laParams[lnIndex]) IN (lcCurrentAPP) ADDITIVE ENDIF ENDFOR PROCEDURE ClearElement *!* If there is an error setting the classlib, assume that it is because *!* it cannot be found. Clear the reference to that element so the *!* local classes will not be messed up when loading. LPARAMETERS taClass, tnElement taClass[tnElement] = CHR(255) RETURN PROCEDURE SetClassLib LPARAMETERS taClass, tnElement, tcMainEXE, tlAdditive EXTERNAL ARRAY taClass && Avoid compile time errors LOCAL lcOnError lcOnError = ON('ERROR') ON ERROR DO ClearElement WITH taClass, tnElement IF tlAdditive SET CLASSLIB TO (taClass[tnElement]) IN (tcMainEXE) ADDITIVE ELSE SET CLASSLIB TO (taClass[tnElement]) IN (tcMainEXE) ENDIF ON ERROR &lcOnError RETURN In the main program of the .app file, place a call to SetFormClasses with the following code: DO SetFormClasses WITH "MyClass1", "MyClass2", ... "MyClassN" STATUS ====== Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. MORE INFORMATION ================ There may be situations when an application (.app) with multiple modules is created. The modules may be updated, but the base .exe is not updated at the same time. The functionality of the base classes in the .exe files is still needed until the .exe is updated, but not any new functionality in the classes in development. The only way to do this is to exclude the classes from the .app. However, by doing so, Visual FoxPro is no longer able to find the class. Steps to Reproduce Behavior --------------------------- 1. Create a project and save it as "MAIN" (without the quotation marks). - Create a program (.prg) file and save it as "Main" (without the quotation marks). Add the following code: DO FORM Main READ EVENTS CLEAR EVENTS SET CLASSLIB TO - Create a new class named MyForm based on Form and store it in MyClass. - Add a class named MyCommandButton based on CommandButton and store it in MyClass. - Create a form based on the new class MyForm. Remove the original form and the form set. Place the following code in the Destroy event of the form: CLEAR EVENTS Save the form as "Main" (without the quotation marks). - Drag and drop two command buttons based on MyCommandButton onto the form. - In the click event of the first command button, add the following code. Give this button the caption of Module1Form. DO module1.APP WITH "Module1Form" - In the click event of the second command button, add the following code. Give this button the caption of Cancel. THISFORM.RELEASE() - Build an .exe file. 2. Create a second project and save it as "Module1" (without the quotation marks). - Create a .prg file and call it "Module1Main" (without the quotation marks). Add the following code: LPARAMETERS cForm *!* Uncomment this to see the workaround in action *!*DO setformclasses WITH "MyClass" DO FORM Module1Form - Add the class library MyClass to the project. - Create a new form based on the class MyForm and save it as Module1Form. Remove the original form and the form set. - Drag and drop one command button based on MyCommandButton from MyClass onto the form. Add the following code to the click event: THISFORM.RELEASE() - Build an APP. 3. Copy the .app file and the .exe file to a new folder. 4. Run the .exe and click on Module1Form. Note that the following error message appears: Error instantiating class. Cannot find ... Additional query words: ====================================================================== Keywords : kbProjManager kbvfp600 kbvfp600bug kbXBase kbGrpDSFox kbDSupport kbCodeSnippet Technology : kbVFPsearch kbAudDeveloper kbVFP600 Version : WINDOWS:6.0 Issue type : kbbug Solution Type : kbpending ============================================================================= THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY. Copyright Microsoft Corporation 2002.