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

How to: Ignore a file for deletion if its in use 6

Status
Not open for further replies.

keepingbusy

Programmer
Apr 9, 2000
1,470
GB
Hi all

As part of an app, DBF's are created by simply using:
Code:
tempfile=SYS(3)
USE MYFILE
copy stru to tempfile+'.dbf'
Slight problem is sometimes, the tempfile gets left behind by the user who has either switched off (yes people stll do that) or worse still, a system crash, before the code gets a chance to:
Code:
dele file tempfile+'.dbf'

As SYS(3) generates a random 8 number digit we thought of trying to resolve this using a couple of lines at the start of a PRG such as:
Code:
dele file 1*.*
dele file 2*.*
etc
The only problem with this would be that if one of the sys(3) numbers is already in use by another, you cannot delete it (and obviously wouldn't want to delete it if its in use by another).
This doesn't happen too often, but as I'm sure your aware, over a period of time the 8 digit DBF's build up using valuable disc space.

This app is version 6

Any suggestions guys?

Many thanks
Lee

Visual FoxPro Versions: 6 & 9
Operating System: Windows XP
 
I post this because I didn't see it here yet (certainly other working solutions have been posted...)

Use the Windows API:
Code:
PROCEDURE SafeDel
LPARAMETERS pcFile
DECLARE INTEGER DeleteFile IN kernel32 ;
  STRING lpFileName
res = DeleteFile(pcFile)
RETURN res<>0

Then simply:
Code:
IF not SafeDel('myfile.ext')
  * log the failure, or whatever
ENDIF

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
Hi Bill

Thank you for your post. How would I replace 'myfile.ext' with the actual file name such as 34256793.dbf or 12354366.dbf etc?

Lee

Visual FoxPro Versions: 6 & 9
Operating System: Windows XP
 
keepingbusy,

Now you're just over thinking. [dazed]

Assuming you have a variable with the filename in it you can send it to SafeDel like so...

lcFileName = "34256793.dbf"

If not SafeDel(lcFileName)

boyd.gif

SweetPotato Software Website
My Blog
 
The equivalent of your line above:
dele file 2*.*

could be:
Code:
LOCAL lnCnt, laFiles[1]
lnCnt = adir(laFiles, '2*.*')
for lnI = 1 to lnCnt
  if not SafeDel(laFiles[lnI,1])
    * Log error, or just Ignore error
  endif
  * if just ignoring the errors, you could use:
  * SafeDel(laFiles[lnI,1])
endfor

PROCEDURE SafeDel
LPARAMETERS pcFile
DECLARE INTEGER DeleteFile IN kernel32 ;
  STRING lpFileName
res = DeleteFile(pcFile)
RETURN res<>0

Or, to avoid re-declaring DeleteFile over and over:
Code:
LOCAL lnCnt, laFiles[1]
lnCnt = adir(laFiles, '2*.*')
DECLARE INTEGER DeleteFile IN kernel32 ;
  STRING lpFileName
for lnI = 1 to lnCnt
  if 0=DeleteFile(laFiles[lnI,1])
    * Log error, or ignore error
  endif
  * if just ignoring the errors, you could use:
  * DeleteFile(laFiles[lnI,1])
endfor

- Bill

Get the best answers to your questions -- See FAQ481-4875.
 
There's been a lot of suggestions, so here's one that I find very useful.

First create the following function:

Code:
*********************************
** FILE: func_is_exclusive.prg **
*********************************
*-- Calling Example:
*--		lAvailable = func_is_exclusive(ADDBS(gcDbfDirectory) + "APD.DBF")
*--	For the fastest possible return you should include the path to the file (if known) in your function call, so the system
*-- does not have to search the 'SET PATH' path because the file is not in the default directory.
*---------------------------------------------------------------
*-- Returns .T. if the file is NOT used by any other user/process, otherwise it returns .F.
*---------------------------------------------------------------
*-- Tests if a file is available for exclusive access by attempting to rename the file to itself.  If the file can be
*--	'renamed' then nobody else is using the file and it can be opened for packing, reindexing, and structure changes.
*---------------------------------------------------------------
LPARAMETERS tcfile
LOCAL lcOnError, llExclusive
lcOnError = ON("ERROR")
llExclusive = .T.
ON ERROR llExclusive = .F.
RENAME (tcFile) TO (tcFile) && if we can't rename the file to itself, then it is already in use by another user/process
ON ERROR &lcOnError.
RETURN llExclusive


Next, generate a list of candidate files using ADIR and loop thru them testing to see if they are in use or not. If not, then delete/erase them.
Code:
lnFiles = ADIR(aDbfNames,ADDBS(gcDBFDirectory)+'*.dbf')
FOR x1 = 1 TO lnFiles
	lcFileName = ADDBS(gcDbfDirectory) + aDBFNames[x1,1]
        *- ISDIGIT is presuming that you don't have valid files that begin with a digit....
	IF ISDIGIT(aDBFNames[x1,1]) AND func_is_exclusive(lcFileName)
		ERASE (lcFileName)	
	ENDIF
ENDFOR

Steve


 
Craig
Now you're just over thinking
Thats the story of my life!

Bill
Thank you for the code. As the thread progesses things become easier!

Steve
I'm grateful to you. As with any code, it makes it a lot easier when you can see it and its explained!

Thanks all

Lee....[thumbsup2]

Visual FoxPro Versions: 6 & 9
Operating System: Windows XP
 
Another option available in additon to the ones above to delete files not being used :

Code:
lnFiles = ADIR(aTables,ADDBS(gcDirectory)+'*.dbf')
FOR x1 = 1 TO lnFiles
    lcFileName = ADDBS(gcDirectory) + aTables[x1,1]
    nHandle = FOPEN((lcFileName),12)
    IF nHandle < 0
       *** File is being used. Do not delete.
    ELSE
       FCLOSE(nHandle)   
       ERASE (lcFileName)      
    ENDIF 
ENDFOR

 
Hi all

I wonder why you put so much effort in this thing. After we found out that erase *.* will stop working after the first error the solution was at hand. make it single file deletions and suppress errors.

mm0000: working with fopen() has the disadvantage that you must do fclose before erase. between that two lines the dbf may be opened by someone else. Very unprobable, but not impossible. The simplest soluton is to try to delete and suppress errors.

Code:
* version for VFP7 or lower
Local lcDir, lcFileSkeleton
lcDir = "c:\temp"
lcFileSkeleton = "*.dbf"
Local Array laFiles[1]
Local lnFiles, lnCount
lnFiles = Adir(laFiles,Addbs(lcDir)+lcFileSkeleton) 
Local lcError
lcError = On("Error")
On Error *
For lnCount= 1 to lnFiles
    If File(Addbs(lcDir)+laFiles[lnCount,1])
       Delete File (Addbs(lcDir)+laFiles[lnCount,1])
    Endif
Endfor lnCount
On Error &lcError

Code:
* version for VFP8 or higher
Local lcDir, lcFileSkeleton
lcDir = "c:\temp"
lcFileSkeleton = "*.dbf"
Local Array laFiles[1]
Local lnFiles, lnCount
lnFiles = Adir(laFiles,Addbs(lcDir)+lcFileSkeleton) 
For lnCount= 1 to lnFiles
    If File(Addbs(lcDir)+laFiles[lnCount,1])
       Try
          Delete File (Addbs(lcDir)+laFiles[lnCount,1])
       Catch
          ? laFiles[lnCount,1]+" could not be deleted"
       EndTry
    Endif
Endfor lnCount

Make lcDir and/or lcFileskeleton parameters and you have a function DeleteDirFiles().

Don't try to figure out if a file is in use by another, simply try to get it exclusive. That also goes for using a table or database exclusive. To proove you only get into trouble try to run this little test:

A) create a database on the LAN:
Code:
create database \\myserver\mytest.dbc
leave that database open for the moment

B) Start two other instances of VFP and run this in those two VFP sessions:
Code:
Public lnOpened
Local llExclusive, lnHandle

Do While .T.
   llExclusive = .f.
   Do While !llExclusive 
      lnHandle = FOpen("\\myserver.mytest.dbc",12)
      llExclusive = (lnHandle>0)
   EndDo
   FClose(lnHandle)
   Open Database \\myserver.mytest.dbc Exclusive
   lnOpened = lnOpened+1
   Close Databases All
   If Inkey()#0
      Exit 
   Endif 
EndDo

Now you have one VFP session with the database open and two sessions desperatly trying to open that database. Close the database in the first VFP session and sooner or later one of the two vfp sessions trying to get exclusive access will have it and eventually also successfully open the database, but as there is the another vfp session trying to do the same, it's in risk, that that other vfp session gets the grip on the database in the wrong moment. Therefore sooner or later you'll get an error in OPEN DATABASE...EXCLUSIVE.

The better solution is this:
Code:
Public lnOpened
Private plError
Do While .T.
   plError = .F.
   On Error plError=.T.
   Open Database \\myserver.mytest.dbc Exclusive
   On Error
   lnOpened = lnOpened+iif(plError,0,1)
   Close Databases All
   If Inkey()#0
      Exit 
   Endif 
EndDo
That won't throw errors of course, as I suppress them, but if plError stays .F The database IS opened exclusive, testing beforehand with FOpen() is just wasting time. You may compare how often the two versions get a grip on the database. Both versions can be stopped by pressing any key.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top