tags:

views:

901

answers:

4

We had a line of code

    if( !CreateFile( m_hFile, szFile, GENERIC_READ|GENERIC_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL ) )
    {
      DWORD dwErr = GetLastError();

      CString czInfo;
      czInfo.Format ("CMemoryMapFile::OpenAppend SetFilePointer call failed - GetLastError returned %d", dwErr);
      LOG(czInfo);

      return false;
    }

This code worked great for many years. A few weeks ago, we had a customer with a problem. Turns out, the problem could be traced to this line of code, where the function would return a INVALID_HANDLE_VALUE handle and GetLastError() returned ERROR_FILE_NOT_FOUND(2).

Now, this is very confusing to us. OPEN_ALWAYS should direct the file to be created if it does not exist. So, why are we getting a ERROR_FILE_NOT_FOUND?

More confusion: For this customer, this only happened on one network share point (we were using a UNC path). Other UNC paths to other machines for this customer worked. Local paths worked. All our other customers (10000+ installs) have no problem at all.

The customer was using XP as the client OS, and the servers were running what appeared to be standard Windows Server 2003 (I think the Small Business Server version). We could not replicate their errors in our test lab using the same OS's. They could repeat the problem with several XP clients, but the problem was only on one server (other Server 2003 servers did not exhibit the problem).

We fixed the problem by nesting two CreateFile calls, the first with OPEN_EXISTING, and the second with CREATE_ALWAYS if OPEN_EXISTING failed. So, we have no immediate need for a fix.

My question: Does anyone have any idea why this API call would fail in this particular way? We are puzzled.

Addendum:

The CreateFile function above is a wrapper on the Windows API function. Here's the code:

bool CMemoryMapFile::CreateFile( HANDLE & hFile, LPCSTR szFile, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes )
{
  hFile = ::CreateFile (szFile, dwDesiredAccess, dwShareMode, NULL, 
                        dwCreationDisposition, dwFlagsAndAttributes, NULL);
  return (hFile != INVALID_HANDLE_VALUE) 
}
+1  A: 

Maybe the directory you wanted to create the file in did not exist?

Are you sure you fixed it by using 2 CreateFile calls? Or did you just not reproduce it?

Brian R. Bondy
Yes, it was fixed by adding the two CreateFile calls. We gave them a new build and it fixed their problem.
David Coufal
And the directory did exist. We did a WebEx with the user and observed the situation directly.
David Coufal
On rereading the code, it's possible that we had a timimg problem. The directory is created immediately before the CreateFile, so it's possible that trying to create a file in it has some kind of latency problem. We do not have the customer available to try this out. Since this seems like a likely explanation, I'm marking this answer as accepted.
David Coufal
A: 

If CreateFile fails, it will return INVALID_HANDLE_VALUE which is non-zero (I think it's negative 1). So CreateFile might be succeeding and returning zero as the handle.

It's only safe to check GetLastError() after a function fails but it looks like you might be checking the last error when CreateFile has succeeded (returned zero).

According to this article, some functions set the "last error" if they succeed:

Tim Stewart
+3  A: 

First, you should always check for success of the CreateFile() API like this:

if (CreateFile(...) == INVALID_HANDLE_VALUE)
{
   // handle the error
}

because CreateFile() doesn't return !=0 in case of success, but anything other than INVALID_HANDLE_VALUE (which is -1).

Then, the CreateFile() can fail in the situation you described if either the directory doesn't exist where you want to create/open the file in, or it can fail if the user has the rights to open and write files in that directory, but no rights to create new files.

Stefan
The directory did exist, and the user did have permission to create files (we were able to create files outside of our app with the logged in user).
David Coufal
A: 

My guesses would be something server related like the server not implementing support correctly for that filesystem operation. Maybe a linux server compared to a windows server?

Your arguments to create file are incorrect, so I assume that this is some sort of CreateFile helper function.

A call to CreateFile should look like:

m_hFile = CreateFile( szFile, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_hFile == INVALID_HANDLE_VALUE)
Shane Powell
Excellent eyes! :) You are correct, this is a CreateFile helper function. I should have simplified the code before posting. Also, we should not have called the helper function CreateFile.
David Coufal