tags:

views:

59

answers:

2

I have an application that modifies an XML file by:

(a) opening it,

(b) creating a temporary file and writing a modified version to it,

(c) closing both files, and

(d) replacing the original file with the temporary file.

When I test it on my laptop running Vista, everything works just as it should. On an embedded PC running XP Professional SP2 with a flash drive instead of a hard disk (which may or may not be relevant), it fails at step (d) with an access violation (error code 5).

If I insert code between steps (c) and (d) to verify that the files are closed, it confirms that they are; if I put in code between steps (c) and (d) to try to delete the original file, it fails with a sharing violation (code 32). If I pause the program at this point and try to delete the file from the GUI, it fails with a sharing violation. If I use systinternals "Process Explorer" at this point, it shows the application still has a handle to the file.

Here is some of the code:

// Open the file which is to be updated:
_wfopen_s(&inStream, m_fileName, L"r, ccs=UTF-8"); 

// Obtain a suitable temporary filename from the operating system:
TCHAR lpTempPathBuffer[MAX_PATH];       // Buffer to hold temporary file path
TCHAR szTempFileName[MAX_PATH];         // Buffer to hold temporary file name
GetTempPath(MAX_PATH, lpTempPathBuffer);
GetTempFileName(lpTempPathBuffer,
                TEXT("TMP"),
                0,
                szTempFileName);

// Now open a temporary file to hold the updates:
errno_t err = _wfopen_s(&outStream, szTempFileName, L"w, ccs=UTF-8"); 
if (err == 0) 
    printf ("Temporary file opened successfully\r\n");
else
    printf ("Temporary file not opened; error code %d\r\n", err);

Then the gubbins that modifies the file, and then ...

// Finally, we must close both files and copy the temporary file to
// overwrite the original input file:
int closerr = fclose(inStream);
if (closerr == 0)
    printf("Original file closed properly\r\n");
else
    printf("Original file not closed properly\r\n");

closerr = fclose(outStream);
if (closerr == 0)
    printf("Temp file closed properly\r\n");
else
    printf("Temp file not closed properly\r\n");

int numclosed = _fcloseall();
printf("Number of files closed = %d\r\n", numclosed); 
       // Should be zero, as we've already closed everything manually

if (!DeleteFile(m_fileName))
{
    int err = GetLastError();
    printf ("Delete file failed, error code was %d\r\n", err);
}
else
    printf ("Delete file succeeded\r\n");


if (!MoveFileEx(szTempFileName, m_fileName, 
           MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH))
{
    int err = GetLastError();
    printf ("Move file failed, error code was %d\r\n", err);
}
else
    printf ("Move file succeeded\r\n");

The output log shows:

"Temporary file opened successfully

Original file closed properly

Temp file closed properly

Number of files closed = 0

Delete file failed, error code was 32

Move file failed, error code was 5"

This makes no sense... Why am I getting a sharing violation on a file which the operating system insists is closed? And is there a reason why this works in Vista but not in XP?

Many thanks for any advice, Stephen.

+2  A: 

One thing to keep in mind with Flash media is that the access times can be much longer, and the file actions are frequently handled asynchronously. Windows may be returning to your code saying that the file is closed before the device driver has actually released it. You go to delete it, and it's really still in use, according to the device driver.

I'd suggest putting a delay in there for testing purposes (say 5-10 seconds), after the file closes and before you try to delete the file. If it works, then what you need to do is to loop on the delete action a couple of times (with a short delay in the loop) and exit the loop when the delete succeeds, or you hit a max # of attempts (say 4-5).

If you still have the same problem, even with a 30 second delay, then the problem probably lies somewhere else.

Craig Trader
A: 

It seems to be a problem with file permissions. After trying various other things which didn't work, I decided to try opening the file with read/write permissions (i.e with the "r+" attribute rather than "r"), and then overwriting the original file contents with the contents of the temporary file. This time, the "_wfopen_s" command itself failed with error code 13 ("Permission denied"), indicating that the operating system was clearly not willing to let the program tamper with this file under any circumstances.

So I guess I need to frame the question slightly differently: why, when

(a) my application works perfectly as-is in Vista, and

(b) I can freely edit the file when I am using the GUI in XP, and all the file permissions look to be set correctly, and

(c) the program that is trying to modify the file is running from a session that is 'logged in' as the file owner

...can the file not be modified by the program?

Is this a curiosity of XP, or of the fact that it's running on an embedded computer with flash memory? If it were the latter, I'd expect there to be problems when creating brand new temporary files as well, but this seems to work just fine.

I'd once again value any suggestions.

Stephen

Eos Pengwern
To cut a long story short, it turns out to have been an issue with IXMLReader. Earlier in the application, the file was opened using IXMLReader but I had failed to close it explicitly. When running under Vista, the system somehow accommodated this (presumably closing the file automatically at some point) whereas in XP the file was left open so naturally could not subsequently be re-opened with write access.
Eos Pengwern
Re-writing the section of code that invoked IXMLReader, to make really sure that the file was closed at the end of the process, solved the problem and the application now runs well both in XP and Vista.
Eos Pengwern