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

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?

74166 次浏览

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

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.

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

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."

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.