关于c ++:使用OPEN_ALWAYS的CreateFile Win32 API调用以奇怪的方式失败

CreateFile Win32 API Call with OPEN_ALWAYS failed in an Odd Way

我们有一行代码

1
2
3
4
5
6
7
8
9
10
    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;
    }

这段代码已经运行了很多年。几周前,我们有一个有问题的客户。原来,问题可以追溯到以下代码行,该函数将返回INVALID_HANDLE_VALUE句柄,而GetLastError()返回ERROR_FILE_NOT_FOUND(2)。

现在,这使我们感到非常困惑。 OPEN_ALWAYS应该指示要创建的文件(如果不存在)。那么,为什么我们会收到ERROR_FILE_NOT_FOUND?

更令人困惑:对于该客户,这仅发生在一个网络共享点上(我们使用的是UNC路径)。该客户使用其他UNC到其他计算机的路径。本地路径有效。我们所有其他客户(超过10000次安装)完全没有问题。

客户使用XP作为客户端OS,并且服务器运行的似乎是标准Windows Server 2003(我认为是Small Business Server版本)。我们无法使用相同的操作系统在我们的测试实验室中复制他们的错误。他们可以在多个XP客户端上重复该问题,但问题仅在一台服务器上(其他Server 2003服务器没有出现此问题)。

我们通过嵌套两个CreateFile调用解决了该问题,第一个调用是OPEN_EXISTING,第二个是CREATE_ALWAYS,如果OPEN_EXISTING失败。因此,我们无需立即进行修复。

我的问题:有谁知道为什么此API调用会以这种特定方式失败?我们感到困惑。

附录:

上面的CreateFile函数是Windows API函数的包装。这是代码:

1
2
3
4
5
6
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)
}


首先,您应始终像下面这样检查CreateFile()API是否成功:

1
2
3
4
if (CreateFile(...) == INVALID_HANDLE_VALUE)
{
   // handle the error
}

因为在成功的情况下,CreateFile()不会返回!= 0,而是INVALID_HANDLE_VALUE(它是-1)以外的任何值。

然后,如果您要在其中创建/打开文件的目录不存在,则CreateFile()可能会在您描述的情况下失败,或者如果用户有权在该目录中打开和写入文件,则CreateFile()可能会失败,但无权创建新文件。


当UNC共享访问文件时,我们也看到了此问题。在我们的案例中没有适用的任何想法和建议-目录始终存在,CreateFile的参数正确,我们正在正确检查返回值。

我们认为这是股票发行。我们通过一个简单的重试循环解决了这个问题:

如果带有OPEN_ALWAYSCreateFile不能出现ERROR_FILE_NOT_FOUND,我们只需休眠几毫秒然后重试。它总是第二次工作(如果第一次失败)。


可能您要在其中创建文件的目录不存在?

您确定通过使用2个CreateFile调用修复了它吗?还是您只是不复制它?


如果CreateFile失败,它将返回INVALID_HANDLE_VALUE,该值不为零(我认为它为负1)。因此,CreateFile可能会成功执行并返回零作为句柄。

只有在函数失败后才检查GetLastError()是安全的,但是当CreateFile成功(返回零)时,您似乎正在检查最后一个错误。

根据本文的介绍,某些函数如果成功将设置"最后的错误":


我的猜测是与服务器相关的某种东西,例如该服务器未正确实现对该文件系统操作的支持。与Windows服务器相比,也许是linux服务器?

您创建文件的参数不正确,因此我认为这是某种CreateFile辅助函数。

对CreateFile的调用应如下所示:

1
2
m_hFile = CreateFile( szFile, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 );
if(m_hFile == INVALID_HANDLE_VALUE)