【问题标题】:CreateFile Win32 API Call with OPEN_ALWAYS failed in an Odd Way带有 OPEN_ALWAYS 的 CreateFile Win32 API 调用以奇怪的方式失败
【发布时间】:2010-10-31 16:33:12
【问题描述】:

我们有一行代码

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

我们通过嵌套两个 CreateFile 调用解决了这个问题,第一个调用 OPEN_EXISTING,如果 OPEN_EXISTING 失败,第二个调用 CREATE_ALWAYS。因此,我们没有立即修复的需要。

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

附录:

上面的 CreateFile 函数是 Windows API 函数的封装。代码如下:

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 在失败时返回一个 NULL 句柄。我忘了(mea culpa)提到上面示例中的 CreateFile 实际上是一个包装函数。我将包装函数添加到我上面的问题中。包装器返回一个布尔值。

标签: c++ winapi


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我的猜测可能与服务器相关,例如服务器没有正确实现对该文件系统操作的支持。与 Windows 服务器相比,Linux 服务器可能是什么?

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

    对 CreateFile 的调用应如下所示:

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

    【讨论】:

    • 优秀的眼睛! :) 你是对的,这是一个 CreateFile 辅助函数。我应该在发布之前简化代码。此外,我们不应该调用辅助函数 CreateFile。
    【解决方案3】:

    首先,您应该始终检查 CreateFile() API 是否成功,如下所示:

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

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

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

    【讨论】:

    • 该目录确实存在,并且用户确实有权创建文件(我们能够使用登录用户在我们的应用程序之外创建文件)。
    【解决方案4】:

    如果 CreateFile 失败,它将返回非零的 INVALID_HANDLE_VALUE(我认为它是负 1)。因此 CreateFile 可能会成功并返回零作为句柄。

    只有在函数失败后检查 GetLastError() 才是安全的,但看起来您可能会在 CreateFile 成功时检查最后一个错误(返回零)。

    根据this article,一些函数如果成功则设置“最后一个错误”:

    【讨论】:

      【解决方案5】:

      也许您要在其中创建文件的目录不存在?

      您确定通过使用 2 个 CreateFile 调用来修复它吗?还是你没有复制它?

      【讨论】:

      • 是的,它已通过添加两个 CreateFile 调用修复。我们给了他们一个新版本,它解决了他们的问题。
      • 目录确实存在。我们与用户进行了 WebEx,并直接观察了情况。
      • 重读代码时,我们可能遇到了时间问题。该目录是在 CreateFile 之前立即创建的,因此尝试在其中创建文件可能会遇到某种延迟问题。我们没有客户可以试用。由于这似乎是一个可能的解释,因此我将此答案标记为已接受。
      猜你喜欢
      • 2016-07-11
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      • 2014-11-07
      • 1970-01-01
      • 1970-01-01
      • 2015-02-26
      • 1970-01-01
      相关资源
      最近更新 更多