Using WinZip from FoxPro

We've had a FoxPro database using WinZip without problem for many years but it's recently started to misbehave. The application was originally written in Visual FoxPro 3 and we upgraded it to VFP 9 when we took over the maintenance about five years ago. We've never changed the unzip routine but when the users installed new Windows 7 PCs earlier this year they started to get intermittent failures.

The routine is nothing special, all that it does is extract a zipped table from a branch office and append the data to the head office files. We know we ought to be sharing the head office database with the branch but the import only runs once every month or so and there's no budget for a rewrite. WinZip has been doing the import nicely for a a long time but we've now had a couple of months of intermittent failures.

What happens is that the user will start the import and will then get an Error 1705 - "File access is denied". If they try again then the import might succeed or it might fail again. Eventually it will work. The zip file is on the user's desktop and files are being extracted to the user's local temp folder so there can't be any conflict with other users. We came to the conclusion that there must be some sort of timing problem.

Calling WinZip from FoxPro

The existing code is simple. First it calls WinZip:

lcCommand = [! /N WINZIP32 -e -o "] + lcZipFile + [" "] + lcAucFolder + ["] &lcCommand

Then it goes into a loop waiting for a file to appear:

Do While .T.
   If File(lcTempFile)
     Exit
   Endif
Enddo

The line calling WinZip is very old code. It uses ! to call the old DOS command processor and uses square brackets as delimiters around the text of the command string. It's also very wasteful in that the program continues to execute whilst WinZip is running. This turns out to be part of the problem.

Testing WinZip

The fault was intermittent which eventually suggested that it might be a timing problem. We modified the loop and added a simple test using ADIR() to read the contents of the destination folder whilst the archive was being extracted. We didn't have the user's data so used a large archive from our own data as a test. The first run showed us a possible problem:

Time Size    
08:48:21.85 0 FoxPro was running fast enough to be reading the directory every few milliseconds. Only the rows where the data has changed are shown.
08:48:22.30 45,875,200
08:48:22.57 68,452,352
08:48:22.82 91,619,328
08:48:23.11 114,589,696
08:48:23.36 137,396,224
08:48:23.61 160,366,592
08:48:23.90 183,500,800
08:48:24.15 206,340,096

WinZip seems to be creating an empty file and then extracting the data in chunks. After the first step the size changes by about 20 Mb every quarter of a second. With our big test file it's inevitable that FoxPro would detect the partially empty file whilst it's still being extracted and would try to USE it. The user's files are smaller than our test so there's a chance that the extraction would complete before Fox detected the file. Once in a while though there's a chance that Fox would try to open a file before it was fully extracted and this seems a plausible cause of their intermittent failures.

Fixing the problem

We made two changes to the loop.

First we added an API call to put Fox to sleep for a second in the middle of the loop. This meant that we'd only be checking the folder once a second rather than several hundred times a second and there'd be less chance of us finding a partial file. It would also free some resources to let WinZip run more quickly and this too would reduce the chance of us seeing bad files.

The second change was to check the size of the file before leaving the loop. If we see the same non-zero size on two successive scans then we'll know that the size hasn't changed in the past second. WinZip was updating the file size four times a second on our XP machine whilst sharing the processor with the FoxPro loop. It'll run at least as fast as that on the users' new machines so if we see the file size stable for a whole second then that should be a safe signal that the extraction is complete.

Did it work?

We've not been able to make it fail under tests but we'll have to wait for the next import in January to see how it behaves with live files.