【问题标题】:How do you check if a directory exists on Windows in C?如何检查 C 中 Windows 上是否存在目录?
【发布时间】:2011-09-07 06:44:25
【问题描述】:

问题

在 Windows C 应用程序中,我想验证传递给函数的参数以确保指定的路径存在。*

如何在 C 中检查 Windows 上是否存在目录?

*我知道在检查是否存在到使用不再存在的路径之间,您可能会陷入竞争状态,但我可以处理。

附加背景

当权限发挥作用时,明确地知道一个目录存在或不存在可能会变得很棘手。在尝试确定目录是否存在时,该进程可能没有访问该目录或父目录的权限。 这可以满足我的需要。如果该目录不存在或者我无法访问它,则在我的应用程序中两者都被视为无效路径故障,因此我不需要区分。如果您的解决方案提供这种区别,则(虚拟)奖励积分。

C 语言、C 运行时库或 Win32 API 中的任何解决方案都可以,但理想情况下,我想坚持使用通常加载的库(例如 kernel32、user32 等)并避免涉及加载非-标准库(如 Shlwapi.dll 中的 PathFileExists)。同样,如果您的解决方案是跨平台的,则(虚拟)奖励积分。

相关

How can we check if a file Exists or not using Win32 program?

【问题讨论】:

  • “我无法访问它”是什么意思?读取权限?写权限?删除文件访问权限?
  • 好问题。为此,读取访问权限。我会假设(读取:这可能很愚蠢)检查读取访问就足够了,因为然后尝试在该目录中执行任何文件访问(RWD)将导致该 API 调用(例如 CreateFile、WriteFile)的相应失败。但是,如果您甚至无法访问要读取的目录(因为它不存在或您没有权限),那么在调用文件访问函数时使用它将导致您无法区分路径问题的失败。
  • 在 Windows API 中,我认为您也可以使用 FindFirstFile() 来测试是否存在。 msdn.microsoft.com/en-us/library/windows/desktop/…
  • “常用”和“非标准”库并不如上一段所建议的那样相互排斥。

标签: c windows winapi


【解决方案1】:

这是一个完全与平台无关的解决方案(使用标准 C 库)

编辑:要在 Linux 中编译,请将 <io.h> 替换为 <unistd.h> 并将 _access 替换为 access。对于真正的平台无关解决方案,请使用 Boost FileSystem 库。

#include <io.h>     // For access().
#include <sys/types.h>  // For stat().
#include <sys/stat.h>   // For stat().

bool DirectoryExists( const char* absolutePath ){

    if( _access( absolutePath, 0 ) == 0 ){

        struct stat status;
        stat( absolutePath, &status );

        return (status.st_mode & S_IFDIR) != 0;
    }
    return false;
}

支持 MBCS 和 UNICODE 构建的特定于 Windows 的实现:

#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <tchar.h>

BOOL directory_exists( LPCTSTR absolutePath )
{
  if( _taccess_s( absolutePath, 0 ) == 0 )
  {
    struct _stat status;
    _tstat( absolutePath, &status );
    return (status.st_mode & S_IFDIR) != 0;
  }

  return FALSE;
}

【讨论】:

  • 这不是平台无关的或标准的,这是微软对 POSIX 的模仿(实际上你会从&lt;unistd.h&gt; 得到access,它不会以下划线开头。)
  • 糟糕,我的错。没错,如果我寻找#included .h,它们位于 Visual Studio 文件夹中。一件事:当您说“模仿”时,您的意思是它实际上不符合 POSIX 标准?如...这不会在 Linux 中编译?
  • 正确,不是POSIX,其他平台上没有io.h_access(带下划线)之类的东西。
  • 我在使用此解决方案的 Windows 上遇到了正斜杠问题,尽管每个人都认为 Win32 接受 / 而不是到处都是 ``。我对此感到困惑,但看起来,要小心 Win32 上的 POSIX 路径和 POSIX libc 函数。
  • 是的,就像 asveikau 所说,Windows 的 POSIX 实现并不完美。如果您想要可移植的文件系统访问,请使用 Boost(这就是我现在正在做的)。
【解决方案2】:

做这样的事情:

BOOL DirectoryExists(LPCTSTR szPath)
{
  DWORD dwAttrib = GetFileAttributes(szPath);

  return (dwAttrib != INVALID_FILE_ATTRIBUTES && 
         (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

GetFileAttributes() 方法包含在 Kernel32.dll 中。

【讨论】:

  • 如果 szPath 是 "C:\\",GetFileAttributes、PathIsDirectory 和 PathFileExists 将不起作用。
  • 我刚刚发现 GetFileAttributes() 看不到某些虚拟 COM 端口,否则 CreateFile() 完全可以使用这些端口。尽管 FILE_ATTRIBUTE_DEVICE 存在于头文件中。哼哼。
  • @zwcloud 你在哪里测试的? GetFileAttributes 对我来说工作正常。
【解决方案3】:

如果您可以链接到 Shell 轻量级 API (shlwapi.dll),您可以使用 PathIsDirectory function

【讨论】:

    【解决方案4】:

    另一个选项是外壳函数 PathFileExists()

    PathFileExists() documentation

    此函数“确定文件系统对象(如文件或目录)的路径是否有效。”

    【讨论】:

      【解决方案5】:

      所以,这个问题充满了边缘情况。真正的答案是这样的:

      BOOL DirectoryExists(LPCTSTR szPath, BOOL *exists)
      {
        *exists = FALSE;
        size_t szLen = _tcslen(szPath);
        if (szLen > 0 && szPath[szLen - 1] == '\\') --szLen;
        HANDLE heap = GetProcessHeap();
        LPCTSTR szPath2 = HeapAlloc(heap, 0, (szlen + 3) * sizeof(TCHAR));
        if (!szPath2) return FALSE;
        CopyMemory(szPath2, szPath, szLen * sizeof(TCHAR));
        szPath2[szLen] = '\\';
        szPath2[szLen + 1] = '.';
        szPath2[szLen + 2] = 0;
        DWORD dwAttrib = GetFileAttributes(szPath2);
        HeapFree(heap, 0, szPath2);
      
        if (dwAttrib != INVALID_FILE_ATTRIBUTES) {
          *exists = TRUE; /* no point checking FILE_ATTRIBUTE_DIRECTORY on "." */
          return TRUE;
        }
        /*
         * If we get anything other than ERROR_PATH_NOT_FOUND then something's wrong.
         * Could be hardware IO, lack of permissions, a symbolic link pointing to somewhere
         * you don't have access, etc.
         */
        return GetLastError() != ERROR_PATH_NOT_FOUND;
      }
      

      DirectoryExists 的正确结果是三态。共有三种情况,您需要处理所有这些情况。要么存在,要么不存在,或者您无法检查。我在生产中丢失了数据,因为 SAN 向 check 函数返回 NO,然后让我在下一行代码中创建一些破坏它的东西。不要犯微软在 C# 中设计 File.Exists() API 时犯的同样错误。

      但是,我看到很多支票都是超值的。不要写想if (目录不存在) CreateDirectory();只需写CreateDirectory() if (GetLastError() != 0 &amp;&amp; GetLastError() != ERROR_ALREADY_EXISTS。或者与RemoveDirectory()相反;只需将其删除并检查是否没有错误或找不到路径。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-24
        • 1970-01-01
        • 2013-05-19
        • 2012-09-12
        • 1970-01-01
        • 2017-09-11
        • 1970-01-01
        相关资源
        最近更新 更多