76

Question

In a Windows C application I want to validate a parameter passed into a function to ensure that the specified path exists.*

How do you check if a directory exists on Windows in C?

*I understand that you can get into race conditions where between the time you check for the existance and the time you use the path that it no longer exists, but I can deal with that.

Additional Background

Knowing explicitly that a directory does or does not exist can get tricky when permissions come into play. It's possible that in attempting to determine if the directory exists, the process doesn't have permissions to access the directory or a parent directory. This is OK for my needs. If the directory doesn't exist OR I can't access it, both are treated as an invalid path failure in my application, so I don't need to differentiate. (Virtual) bonus points if your solution provides for this distinction.

Any solution in the C language, C runtime library, or Win32 API is fine, but ideally I'd like to stick to libraries that are commonly loaded (e.g. kernel32, user32, etc.) and avoid solutions that involve loading non-standard libraries (like PathFileExists in Shlwapi.dll). Again, (Virtual) bonus points if your solution is cross-platform.

Related

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

Community
  • 1
  • 1
Zach Burlingame
  • 13,476
  • 14
  • 56
  • 65
  • 1
    What do you mean by "I can't access it"? Read access? Write access? Delete file access? – Jim Mischel Jun 02 '11 at 18:11
  • Good question. For this purpose, read access. I would assume (read:this might be stupid) that checking for read access would be sufficient as then attempting to perform any file access (RWD) in that directory will result in the appropriate failure of that API call (e.g. CreateFile, WriteFile). However if you can't even access the directory for reading (either because it doesn't exist or you don't have perms) then using it in a call to file access functions will result in failures that you cannot distinguish from path problems. – Zach Burlingame Jun 02 '11 at 18:32
  • In Windows API, I think you can also use FindFirstFile() to test for existance. http://msdn.microsoft.com/en-us/library/windows/desktop/aa364418(v=vs.85).aspx – Indinfer Dec 18 '12 at 01:54
  • "Commonly loaded" and "non-standard" libraries are not mutually exclusive as suggested by the last paragraph. – Adrian McCarthy Oct 01 '13 at 16:03
  • possible duplicate of [How can we check if a file Exists or not using Win32 program?](http://stackoverflow.com/questions/3828835/how-can-we-check-if-a-file-exists-or-not-using-win32-program) – Adrian McCarthy Oct 01 '13 at 16:04
  • Not a duplicate, this is about a directory, that one is about a file – Zach Burlingame Oct 29 '13 at 10:26

5 Answers5

103

Do something like this:

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

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

The GetFileAttributes() method is included in Kernel32.dll.

Zach Burlingame
  • 13,476
  • 14
  • 56
  • 65
retrodrone
  • 5,850
  • 9
  • 39
  • 65
  • 5
    If szPath is `"C:\\"`, GetFileAttributes, PathIsDirectory and PathFileExists will not work. – zwcloud Jun 30 '15 at 07:59
  • I've just discovered that GetFileAttributes() cannot see some virtual COM ports, that are otherwise perfectly available to CreateFile(). Despite the fact that FILE_ATTRIBUTE_DEVICE exists in the header files. Ho hum. – frr Oct 26 '17 at 19:29
  • @zwcloud Where did you test that? GetFileAttributes is working fine for me. – Martin Feb 19 '18 at 15:42
26

Here's a totally platform-agnostic solution (using the standard C library)

Edit: For this to compile in Linux, replace <io.h> with <unistd.h> and _access with access. For a real platform agnostic solution, use the Boost FileSystem library.

#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;
}

A Windows-specific implementation that supports both MBCS and UNICODE builds:

#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;
}
dario_ramos
  • 7,118
  • 9
  • 61
  • 108
  • 6
    That's not platform-agnostic or standard, that's Microsoft's imitation of POSIX (in actuality you would get `access` from `` and it would not begin with an underscore.) – asveikau Jun 02 '11 at 18:14
  • Ooops, my bad. You're right, if I seek the #included .h's, they are inside a Visual Studio Folder. One thing: When you say "imitation", you mean it's not actually POSIX compliant? As in... This wouldn't compile in Linux? – dario_ramos Jun 02 '11 at 18:20
  • 2
    Correct, it's not POSIX, there is no such thing as `io.h` or `_access` (with underscore) on other platforms. – asveikau Jun 02 '11 at 18:22
  • 1
    I’ve had trouble with forward slashes on Windows with this solution, even though everyone thinks Win32 accepts `/` instead of `\` everywhere. I’m confused about it, but seemingly, be careful with POSIX paths and POSIX libc functions on Win32. – mxcl May 30 '13 at 11:53
  • Yes, like asveikau said, Windows' implementation of POSIX is not perfect. If you want portable filesystem access, use Boost (that's what I'm doing now). – dario_ramos May 30 '13 at 14:16
  • _stat and its relatives fails for `C:\ ` path on Windows. – Violet Giraffe Apr 05 '17 at 14:00
10

If linking to the Shell Lightweight API (shlwapi.dll) is ok for you, you can use the PathIsDirectory function.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
5

Another option is the shell function PathFileExists()

PathFileExists() documentation

This function "Determines whether a path to a file system object such as a file or directory is valid."

Greg
  • 1,181
  • 13
  • 13
2

So, this question is full of edge cases. A real answer would be like this:

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;
}

The correct result of DirectoryExists is tri-state. There are three cases and you need to handle all of them. Either it exists, it doesn't exist, or you were unable to check. I've lost data in production because a SAN returned NO to the check function and then let me create something clobbering it on the very next line of code. Don't make the same mistake Microsoft did when designing the File.Exists() API in C#.

However I see a lot of checks that are supliferous. Don't write thinks like if (directory doesn't exist) CreateDirectory(); just write CreateDirectory() if (GetLastError() != 0 && GetLastError() != ERROR_ALREADY_EXISTS. Or the converse with RemoveDirectory(); just remove it and check for either no error or path not found.

Joshua
  • 40,822
  • 8
  • 72
  • 132