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 gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Return from a Class Object returns .T. But before returning it is a string

Status
Not open for further replies.

dkean4

Programmer
Feb 15, 2015
282
US
I was navigating through your archives looking for a "File Search" utility, to avoid CMD and spell everything out.

Eurika! I found something. It was done by blindpete@mail.com. It gave me some problems with locked directories and I had to go and wreck the locks on every directory which had a lock on it. Finally it went all the way through the search, but the result, thought correct, in the class before the RETURN, when it returns it delivers a .T. instead of something like "C:\Flakey\PRGs\start.prg". I fought with it at length and finally had to give up and force transfer the result through a public variable.

This is the call to the Class Object and you can find the return near the end of the code with my comments around it and what I had to do to get it to work.
Maybe someone has an idea why this is taking place...

Code:
LPARAMETERS nKeyCode, nShiftAltCtrl
IF nKeyCode = 13
	ThisForm.srchScreen.ff_result.Value = FindFile(ALLTRIM(This.Value))do init_vfp
ENDIF

And this is the Behemoth Class Object wrapped in my function fondfile:
Code:
FUNCTION findfile
	LPARAMETERS cfilename
	cfilename = ALLTRIM(cfilename)
	*FileSearch Class
	* 7/2000
	* blindpete@mail.com

	* DESCRIPTION
	* This Class will search all drives from  C to Z (that exist)
	* for the filename that you pass it.  It returns a string
	* [Drive]:[Path][Filename w/ extension] and empty() if not
	* found. If wildcards (*/?) are used it returns a cursor.

	* USAGE
	* cPathAndFilename = oFileSearch.Search(cFileName)
	* ObjectName.Search(cFileName,[cBarText],[bShowWindow],[bMoreInfo],[bCmdButtons])
	* cFileName     REQD character filename and extension Wildcards (*?) allowed
	* [cBarText]    OPTN character context to place in progress bar Default empty()
	* [bShowWindow] OPTN T/F display form or not Default .F.
	* [bMoreInfo]   OPTN T/F display path being searched Default .F.
	* [bCmdButtons] OPTN T/F display CANCEL button or not Default .F.

	* RETURNS
	* IF Found:      [Drive]:[Path][Filename w/ extension]
	*  w/ wildcards  cursor FILELIST (FOLDER C(254), FILENAME C(254)
	* IF Not Found:  empty()
	*  w/ wildcards  cursor FILELIST (FOLDER C(254), FILENAME C(254)

	* BEGGING
	* Should you make any significant improvements to this
	* class please send it back to me blindpete@mail.com

	* DISCLAIMER
	* This Class is as is.  There is no warrenty expressed or
	* implied.  Use this code at your own risk.

	* CREDITS
	* Much thanks to Chris Chamberlain and a big thank you to
	* Jon Scott who enlightened me on the beauty and hazards
	* of the DOEVENTS command.

	*EXAMPLE/SAMPLE(s)
	*oFind.Search(cFileName,[cBarText],[bShowWindow],[bMoreInfo],[bCmdButtons])
[COLOR=#CC0000]ofind  = CREATEOBJECT("FileSearch")[/color]

	*This example returns a string
[COLOR=#CC0000]RETURN ofind.search(cfilename,cfilename+" - ",.T.,.T.,.T.)[/color]
	*This example creates a cursor
	*? oFind.Search("*.sys","All System Files - ",.T.,.T.,.T.)

	**********************************************
	DEFINE CLASS "FileSearch" AS "FORM"
		counter = 0
		result = ""
		barcaption = ""
		buttoncancel =.F.
		wildcard = .F.
		ALWAYSONTOP = .T.
		AUTOCENTER = .T.
		SHOWWINDOW = 2 && top level form
		WINDOWTYPE = 1 && Modal
		DRAWMODE = 9
		TITLEBAR = 1
		CONTROLBOX = .F.
		HEIGHT = 83
		WIDTH = 264+24
		CAPTION = "File Search..."
		ADD OBJECT "oTxtMore" AS "TEXTBOX" WITH ;
			HEIGHT = 25, LEFT =12, WIDTH = 264, ALIGNMENT = 0, ;
			TOP = 76, BACKSTYLE = 0, BORDERSTYLE = 0, FONTBOLD = .F.,;
			TABSTOP = .F., ENABLED=.F., DISABLEDFORECOLOR = RGB(0,0,0),;
			FONTSIZE = 8
		ADD OBJECT "oTxt" AS "TEXTBOX" WITH ;
			HEIGHT = 25, LEFT =12, WIDTH = 264, ALIGNMENT = 2, ;
			TOP = 6, BACKCOLOR = RGB(255,255,255), ;
			DISABLEDBACKCOLOR = RGB(255,255,255), ;
			DISABLEDFORECOLOR = RGB(0,0,0), ;
			ENABLED = .F., FONTBOLD = .T.
		ADD OBJECT "oCmd1" AS "COMMANDBUTTON" WITH ;
			CAPTION = "\<Cancel", HEIGHT = 25, LEFT = 96, ;
			WIDTH = 97, TOP = 48
		PROCEDURE ocmd1.CLICK
			nquit = MESSAGEBOX('Are you sure you want to cancel?',4+32,THISFORM.CAPTION)
			IF nquit = 6
				THISFORM.buttoncancel =.T. &&abort search
			ENDIF
		ENDPROC
		ADD OBJECT "oShp" AS "SHAPE" WITH ;
			BORDERSTYLE = 0, DRAWMODE = 14, FILLSTYLE = 0
		PROCEDURE INIT
			THISFORM.oshp.LEFT=THISFORM.otxt.LEFT+1
			THISFORM.oshp.TOP=THISFORM.otxt.TOP +1
			THISFORM.oshp.HEIGHT=THISFORM.otxt.HEIGHT-2
			THISFORM.oshp.VISIBLE = .T.
			THISFORM.BORDERSTYLE = 2
		ENDPROC
		PROCEDURE counter_assign
			LPARAMETERS vnewval
			IF vnewval <> THISFORM.counter
				THISFORM.counter = vnewval
				x = THISFORM.counter
				THISFORM.oshp.WIDTH = x*THISFORM.otxt.WIDTH/100
				THISFORM.otxt.VALUE = THISFORM.barcaption+ALLTRIM(STR(x))+"% Complete"
				THISFORM.REFRESH
	*Check if cancel button has been pushed Alt+C or clicked
				IF MDOWN() OR CHRSAW()
					DOEVENTS
				ENDIF
			ENDIF
		ENDPROC
		PROCEDURE search
			PARAMETER cfilename, cbartext, bshow, bmore, bcmdbuttons
	*cFileName:  FileName w/ extension NO wildcards
	*cBarText:   Caption displayed inside the progress bar
	*bShow:      .T. = show progess bar
	*bMore:      .T. = show more information about the search progress
	*bCmdButtons Displays the toolbar (cancel only at this time... more later)
	*Returns:     FullPath [Drive]:[Path][Filename w/ extension]
	*Validate Parameters
			IF TYPE("cFileName") <> "C" OR LEN(ALLTRIM(cfilename)) = 0
				RETURN
			ENDIF
			IF TYPE("cBarText") <> "C"
				cbartext = ""
			ENDIF
			IF TYPE("bshow") <> "L"
				bshow = .F.
			ENDIF
			IF TYPE("bMore") <> "L"
				bmore = .F.
			ENDIF
			IF TYPE("bCmdButtons") <> "L"
				bcmdbuttons = .T.
			ENDIF
			IF AT("?",cfilename)>0 OR AT("*",cfilename)>0
				THISFORM.wildcard = .T.
			ENDIF
	*Init Display
			IF bshow
				THISFORM.counter = 0
				THISFORM.VISIBLE = .T.
				THISFORM.otxtmore.VISIBLE = bmore
				THISFORM.ocmd1.VISIBLE = bcmdbuttons
				THISFORM.ocmd1.ENABLED = bcmdbuttons
				THISFORM.barcaption = cbartext
				IF bmore
					THISFORM.HEIGHT = 77
					THISFORM.otxtmore.TOP  = THISFORM.HEIGHT -31
				ELSE
					THISFORM.HEIGHT = 46
				ENDIF
				IF bcmdbuttons
					THISFORM.HEIGHT = THISFORM.HEIGHT + 31
					THISFORM.ocmd1.TOP = THISFORM.HEIGHT -31
					THISFORM.ocmd1.SETFOCUS
				ENDIF
			ENDIF
	*Directory List
			IF THISFORM.wildcard
				CREATE CURSOR filelist(folder c(254), filename c(254)) && For Wild Cards
			ENDIF
			CREATE CURSOR dirlist(folder m)
	*CREATE CURSOR DirList(FOLDER C(254))
			SELECT dirlist
			cstartdir = SYS(5)+CURDIR()
			nrecord = 1
			FOR ndrive = 65 TO 90
				cdrive = CHR(ndrive)
				cdir = "\"
				IF DRIVETYPE(cdrive)=3 OR DRIVETYPE(cdrive)=4  &&Hard or Net Drive
					cpath = cdrive+":&cDir"
					SET DEFAULT TO "&cPath"
					APPEND BLANK
					REPLACE folder WITH cpath
					ADIR(adirlist,"","D") && copy all the current dirs into an array
					cpathnow = SYS(5)+CURDIR()
					GO nrecord
					bkeepgoing =.T.
					DO WHILE bkeepgoing=.T.
						nrecord = RECNO()
						cpath = ALLTRIM(dirlist.folder)
						THISFORM.otxtmore.VALUE = cpath
						THISFORM.counter = 100*(nrecord/RECCOUNT())
	*Check if Cancel Command is in effect
						IF THISFORM.buttoncancel =.T.
							bkeepgoing =.F.
							cdrivepathfile = ""
							ndrive = 91 && end drive search FOR LOOP
							EXIT          && end current drive DO LOOP
						ENDIF
	*Look for file
						IF THISFORM.wildcard
							SELECT filelist
							ADIR(afilelist,"&cPath.&cFileName","AHRS") && copy mathching filenames into an array
							cpathnow = SYS(5)+CURDIR()
							IF TYPE("aFileList") <> "U"
								cfoldername = "&cPath"
								FOR ncopy = 1 TO ALEN(afilelist,1)
									IF RIGHT(afilelist[nCopy,5],1)<>"D"
										APPEND BLANK
										REPLACE folder WITH cfoldername
										REPLACE filename WITH afilelist[nCopy,1]
									ENDIF
								ENDFOR
								RELEASE afilelist
							ENDIF
						ELSE
							IF FILE("&cPath.&cFileName")
								ndrive = 91 &&END FOR LOOP
								EXIT  && END DO LOOP
							ENDIF
						ENDIF
	*Add more directories to search
						SELECT dirlist
						SET DEFAULT TO "&cPath"
						ADIR(adirlist,"","D") && copy all the current dirs into an array
						cpathnow = SYS(5)+CURDIR()
						FOR ncopy = 1 TO ALEN(adirlist,1)
							cfoldername = adirlist[nCopy,1]
							IF RIGHT(adirlist[nCopy,5],1)="D" AND cfoldername<>"."
								APPEND BLANK
								REPLACE folder WITH "&cPathNow.&cFolderName.\"
							ENDIF
						ENDFOR
						RELEASE adirlist
						GO nrecord
						bkeepgoing = IIF(nrecord=RECCOUNT(),.F.,.T.)
						SKIP
					ENDDO
				ENDIF
			ENDFOR
	*CleanUp
			SET DEFAULT TO "&cStartDir"  &&reset to previous default
			IF bkeepgoing
				cdrivepathfile = "&cPath.&cFileName"
			ELSE
				cdrivepathfile = ""
			ENDIF
			SELECT dirlist  &&kill cursor
			USE
			IF THISFORM.wildcard
				SELECT filelist && kill cursor if empty
				IF RECCOUNT() = 0
					USE
				ENDIF
			ENDIF
			THISFORM.result = cdrivepathfile
			THISFORM.VISIBLE = .F.
			THISFORM.buttoncancel =.F.
*[COLOR=#EF2929] I introduced this line below to bypass the RETURN below.  And it works just fine.[/color]
			this_form.srchscreen.ff_result.VALUE = cdrivepathfile
* [COLOR=#EF2929]The RETURN below fails.  It returns .T. EVERY TIME!  What gives???[/color]
			RETURN cdrivepathfile
		ENDPROC
	ENDDEFINE
	*EOF
ENDPROC
RETURN
 
Your function FindFile will receive a parameter, do nothing with it, and return .t. exactly as any empty function will do. You have no executable code inside it.

Class definitions do not "live" inside functions.

Create a prg named FileSearch and inside your function do something like this:

loSearch = NewObject("FileSearch","filesearch.prg")
loSearch.Show(1) && modal

I'll leave it to you to figure out how to get your parameter into the right location and then get it back out once the search is done.
 
Dan,

That's weird, because I do find the path of the file with the correct directory.
And I have traced the code going through.
Also, while it is weeding through it, there is progress Bar showing how much it has gone through.

Did you see this line of code? "oFind = createobject("FileSearch")" It instantiates the Object from the Class "FileSearch"
Also, this line makes the call to the instantiated object: "? oFind.Search(cFileName,cFileName+" - ",.T.,.T.,.T.)"

I appreciate your heads up, but it does work. Look at it more carefully.

Here is a test I just ran on it for you. I entered "HeaderButton.png" and I got back this: "C:\JOBS\PIRC\HeaderButton.png"

I am working with VFP version 9.0 Dan!

Dennis

 
Dan,

I figured out the mistake...

The correct RETURN should be at the call into the object:
RETURN oFind.Search(cFileName,cFileName+" - ",.T.,.T.,.T.)

Maybe earlier versions could not be wrapped inside of functions, I only worked for a short time with each 7.0 and 8.0 versions of VFP. But in version 9.0 you can insert a class right into your function.

Unfortunately, this "Find File" is very buggy. It works great up to the second layer. After that it begins to labor and on the 3rd Layer it croaks too often.

I created one a while back. Now, I need to find it. Mine is a lot faster and it can dig up to 128 levels. Maybe I will publish it.


Dennis
 
Dan, you misunderstood this code. The Function findfile creates the FileSearch form object, uses it and returns the result value.

It never was impossible to have a function and a class definition inside the same PRG. It always was impossible to have a class definition within a method.

The form is only shown within the search method, if you pass in the parameter to show it. A bit unusual, as you could do this with a modal form returning the result from its unload event instead. Where to put the search code then is trick, as as the form should be able to show. The form Init has to receive all parameters, but can't do the search itself, as the form isn't yet shown. The search has to be triggered by the first controls GotFocus event. That would make it a less unusual approach of a modal form with return value, though with more unusual trickery on how to let it work automatically after its initialisation.

There is Filer.scx in Home()/Tools/Filer/ and you may also use the Filer.dll directly via CreateObject("filer.fileutil"). Its useage can be seen in the Filer.scx find method.

Bye, Olaf.
 
What's wrong with the code is actually the ENDPROC after the ENDDFINE. VFP does not interpret and compile it this way. The ENDPROC (in this case it had better been ENDFUNC) is optional, so the DEFINE CLASS already dfines the end of the function. The code after ENDDEFINE is simply disregarded.

Try it out:

Code:
FUNCTION myFunc()
   DEFINE CLASS myClass a Custom
   ENDDFINE
   RETURN "Hi"
ENDPROC

This will not return "Hi", it returns .T., as any function ending without a RETURN returns .T. and the function already ends at the DEFINE CLASS. The code after ENDDEFINE is simply disregarded. So what the compiler sees actually is:

Code:
FUNCTION myFunc()
   RETURN .T.
ENDFUNC
DEFINE CLASS myClass a Custom
ENDDFINE
* rubbish:
RETURN "Hi"
ENDPROC

Bye, Olaf.
 
Olaf,

What sounded interesting on that Class "file search" was the claim that it will search on all drives. I assumed "Sequentially". But it can't do more than 3 level deep.

I did not want everything Filer.scx offers and search is done on one drive at a time. But at least it works for deeply embedded files.
Thanks... Some cosmetics and in 5 minutes It was in. I will bastardize it later.

The line "? oFind.Search(cFileName,cFileName+" - ",.T.,.T.,.T.)" in the original code threw me. My sloppy...

The fix was to change it to: "RETURN oFind.Search(cFileName,cFileName+" - ",.T.,.T.,.T.)"

Dennis
 
The fix works.

It was never the RETURN cdrivepathfile, which returned .T., though. It was the end of the function after the ? oFnd.search...
And the ENDPROC should really be an ENDFUNC and put before the DEFINE CLASS.
*EOF shoudl be the last line of that PRG, then it will not only work but be correctly and fully nested code.

Bye, Olaf.


 
Dan, you misunderstood this code. The Function findfile creates the FileSearch form object, uses it and returns the result value.

Yep, I misread the code. Good reason not to structure it this way. [bigsmile]
 
Here is a program that I wrote a long time ago to find files given the file name. It was written to use Foxtools.fll; it can be updated to not use that library anymore.

Code:
************************************************************************************************
* Written by:  Gregory A. Green
*
* Copyright ©1998-2000 Gregory A. Green
*                      Released into the Public Domain
*
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ALL
* IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE ARE HEREBY DISCLAIMED.
*
************************************************************************************************
*   Function to locate the full path to a file on any hard or network drive attached to
*   the PC. Returns the full path and file name if found; otherwise, empty string is
*   returned.  Uses recursion to search the sub-directories.  Requires FoxTools.fll for version
*   5.0 or higher of Visual FoxPro.
*
************************************************************************************************
*FUNCTION FindFile
PARAMETERS pcFileName
	PRIVATE ALL LIKE l*
	IF ATC("FOXTOOLS",SET('LIBRARY'))=0                               && Load external library if needed
		SET LIBRARY TO SYS(5) + SYS(2003) + "\foxtools.fll" ADDITIVE
	ENDIF
	lcFindFile = ""
	DIMENSION lcDriveList[1]
	lcDriveList[1] = ""                                               && Initialize the available disk drive list
	=fGetDrives(@lcDriveList)                                         && Get the available disk drives to this PC
	IF !EMPTY(lcDriveList[1])                                         && Check if any drives found
		lnNumDrives = ALEN(lcDriveList)                                && Set number of drives to search
		FOR lnNdx=1 TO lnNumDrives                                     && Search in root directory of each drive
			lcFindDrive = lcDriveList[lnNdx] + ":\"
			lcFindFile  = lcFindDrive + pcFileName
			IF FILE(lcFindFile)                                         && Check for file
				EXIT
			ENDIF
			lcFindFile = fSearchDirs(lcFindDrive,pcFileName)            && Search in first level of sub-directories
			IF !EMPTY(lcFindFile)                                       && Check if file found
				EXIT
			ENDIF
		ENDFOR
	ENDIF
	RETURN lcFindFile
ENDFUNC

************************************************************************************************
*
*   Function to search all the directories for a given path for the file to locate
*
FUNCTION fSearchDirs
PARAMETER pcSearchDir,pcFileName
	PRIVATE ALL LIKE l*
	llFound    = .F.
	lcFindFile = ""
	lnNumDirs  = ADIR(lcDirList,pcSearchDir+"*","D")                  && Get directories to search
	FOR lnNdx=1 TO lnNumDirs                                          && Search in the each sub-directory
		IF !INLIST(lcDirList[lnNdx,1],".","..")                        && Skip these directory names
			lcFindFile = pcSearchDir + lcDirList[lnNdx,1] + "\" + pcFileName
			IF FILE(lcFindFile)                                         && Check for file
				llFound = .T.
				EXIT
			ENDIF
		ENDIF
	ENDFOR
	IF !llFound
		FOR lnNdx=1 TO lnNumDirs                                       && Search this level of sub-directories
			IF !INLIST(lcDirList[lnNdx,1],".","..")                     && Skip these directory names
				lcSearchDir = pcSearchDir + lcDirList[lnNdx,1] + "\"
				lcFindFile  = fSearchDirs(lcSearchDir,pcFileName)        && Search next level of subdir (by recursion)
				IF !EMPTY(lcFindFile)                                    && Check if file found
					EXIT
				ENDIF
			ENDIF
		ENDFOR
	ENDIF
	RETURN lcFindFile
ENDFUNC

************************************************************************************************
*
*   Function to get the attached hard and network drives to this PC
*
PROCEDURE fGetDrives
PARAMETER pcDriveList
	PRIVATE ALL LIKE l*
	EXTERNAL ARRAY pcDriveList
	#DEFINE DRIVE_REMOVABLE  2
	#DEFINE DRIVE_FIXED      3
	#DEFINE DRIVE_NETWORK    4
	#DEFINE DRIVE_CDROM      5
	lnNumDrives = 0                                                   && Initialize number of drives
	FOR lnDriveID=2 TO 25                                             && Loop to check for drives; start at drive C
		lcDrive = CHR(lnDriveID+65) + ":"
		lnDriveType = DRIVETYPE(lcDrive)                               && Check for drive ID attached to PC
		IF INLIST(lnDriveType,DRIVE_NETWORK,DRIVE_FIXED)               && Check to add to drive list
			lnNumDrives = lnNumDrives + 1
			DIMENSION pcDriveList[lnNumDrives]
			pcDriveList[lnNumDrives] = CHR(lnDriveID+65)
		ENDIF
	ENDFOR
	#UNDEF DRIVE_REMOVABLE
	#UNDEF DRIVE_FIXED
	#UNDEF DRIVE_NETWORK
	#UNDEF DRIVE_CDROM
	RETURN
ENDPROC
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top