Checking a file using GetFileAttributes(szPath) and GetLastError()

2.9k Views Asked by At

this is a followup on 'LPCTSTR typecast for use with GetFileAttributes'

i extended my method by GetLastError();.

I cast DWORD into a unsigned int and try to display it with a MessageBox.

the code does work, but i fail to visualize the value of DWORD dw or Unsigned int test from the GetLastError(); method using a MessageBox and figuring how to proceed.

this a win32 project and i trying to build a method to see if a file exist on the harddrive.

there are over 15.000 error codes and it´s imposible to hardcode them all. http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx

BOOL FileExists(LPCTSTR szPath)
{
    //MessageBox(NULL,szPath,L"File Error",MB_OK);
    DWORD dwAttrib = GetFileAttributes(szPath);

    unsigned int test;
    DWORD dw = GetLastError();
    test =(unsigned int)dw;
    if(test == 0){
        MessageBox(NULL,(LPCWSTR)test, L"Target == 0",MB_OK);
    }
    else
    {
        MessageBox(NULL,(LPCWSTR)test, L"Target != 0",MB_OK);
    }

    switch(dw)
    {
    case ERROR_SUCCESS:
        MessageBox(NULL,L"ERROR_SUCCESS", L"File Error",MB_OK);
        break;
    case ERROR_PATH_NOT_FOUND:
        MessageBox(NULL,L"ERROR_PATH_NOT_FOUND", L"File Error",MB_OK);
        break;
    default:
        MessageBox(NULL,(LPCWSTR)dw, L"File Error",MB_OK);
        break;
    }

    switch(dwAttrib)
    {

        case FILE_ATTRIBUTE_DIRECTORY:
            MessageBox(NULL,L"FILE_ATTRIBUTE_DIRECTORY", L"File Error",MB_OK);
            break;
        case FILE_ATTRIBUTE_ARCHIVE:
            MessageBox(NULL,L"FILE_ATTRIBUTE_ARCHIVE", L"File Error",MB_OK);
            break;
        case FILE_READ_ONLY_VOLUME:
            MessageBox(NULL,L"FILE_READ_ONLY_VOLUME", L"File Error",MB_OK);
            break;
        case FILE_INVALID_FILE_ID:
            MessageBox(NULL,L"FILE_INVALID_FILE_ID", L"File Error",MB_OK);
            break;
        //case INVALID_FILE_ATTRIBUTES:
        //  MessageBox(NULL,L"INVALID_FILE_ATTRIBUTES",L"File Error",MB_OK);
        //  break;
        //case FILE_INVALID_FILE_ID:
        //  MessageBox(NULL,L"Failed to get image file,\\please check game folder",L"File Error",MB_OK);
        //  break;
        default:
            MessageBox(NULL,(LPCWSTR)dwAttrib,L"File Error",MB_OK);
            break;
    }

  return true; // testing phase
}
2

There are 2 best solutions below

4
On BEST ANSWER

You cannot type-cast a DWORD into a string. You have to format it.

Your error checking is wrong.

On success, GetFileAttributes() can (and frequently does) return multiple attributes at a time, but you are not handling that possibility. And some of the values you are checking for are not even value attributes to begin with.

Try this instead:

BOOL FileExists(LPCTSTR szPath)
{
    DWORD dwAttrib = GetFileAttributes(szPath);
    if (dwAttrib == INVALID_FILE_ATTRIBUTES)
    {
        DWORD dw = GetLastError();
        switch (dw)
        {
            case ERROR_PATH_NOT_FOUND:
                MessageBoxW(NULL, L"ERROR_PATH_NOT_FOUND", L"File Error", MB_OK);
                break;

            case ERROR_FILE_NOT_FOUND:
                MessageBoxW(NULL, L"ERROR_FILE_NOT_FOUND", L"File Error", MB_OK);
                break;

            default:
            {
                std::wstringstream msg;
                msg << L"Error Code: " << dw;

                LPWSTR lpMsg = NULL;
                if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, dw, 0, (LPWSTR)&lpMsg, 0, NULL))
                {
                    msg << std::endl << lpMsg;
                    LocalFree(lpMsg);
                }

                MessageBoxW(NULL, msg.str().c_str(), L"File Error", MB_OK);
                break;
            }
        }

        return false;
    }
    else
    {
        std::wstringstream attribs;

        if (dwAttrib & FILE_ATTRIBUTE_READONLY)
        {
            attribs << L"FILE_ATTRIBUTE_READONLY" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_READONLY;
        }

        if (dwAttrib & FILE_ATTRIBUTE_HIDDEN)
        {
            attribs << L"FILE_ATTRIBUTE_HIDDEN" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_HIDDEN;
        }

        if (dwAttrib & FILE_ATTRIBUTE_SYSTEM)
        {
            attribs << L"FILE_ATTRIBUTE_SYSTEM" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_SYSTEM;
        }

        if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
        {
            attribs << L"FILE_ATTRIBUTE_DIRECTORY" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_DIRECTORY;
        }

        if (dwAttrib & FILE_ATTRIBUTE_ARCHIVE)
        {
            attribs << L"FILE_ATTRIBUTE_ARCHIVE" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_ARCHIVE;
        }

        if (dwAttrib & FILE_ATTRIBUTE_DEVICE)
        {
            attribs << L"FILE_ATTRIBUTE_DEVICE" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_DEVICE;
        }

        if (dwAttrib & FILE_ATTRIBUTE_NORMAL)
        {
            attribs << L"FILE_ATTRIBUTE_NORMAL" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_NORMAL;
        }

        if (dwAttrib & FILE_ATTRIBUTE_TEMPORARY)
        {
            attribs << L"FILE_ATTRIBUTE_TEMPORARY" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_TEMPORARY;
        }

        if (dwAttrib & FILE_ATTRIBUTE_SPARSE_FILE)
        {
            attribs << L"FILE_ATTRIBUTE_SPARSE_FILE" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_SPARSE_FILE;
        }

        if (dwAttrib & FILE_ATTRIBUTE_REPARSE_POINT)
        {
            attribs << L"FILE_ATTRIBUTE_REPARSE_POINT" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_REPARSE_POINT;
        }

        if (dwAttrib & FILE_ATTRIBUTE_COMPRESSED)
        {
            attribs << L"FILE_ATTRIBUTE_COMPRESSED" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_COMPRESSED;
        }

        if (dwAttrib & FILE_ATTRIBUTE_OFFLINE)
        {
            attribs << L"FILE_ATTRIBUTE_OFFLINE" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_OFFLINE;
        }

        if (dwAttrib & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
        {
            attribs << L"FILE_ATTRIBUTE_NOT_CONTENT_INDEXED" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
        }

        if (dwAttrib & FILE_ATTRIBUTE_ENCRYPTED)
        {
            attribs << L"FILE_ATTRIBUTE_ENCRYPTED" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_ENCRYPTED;
        }

        if (dwAttrib & FILE_ATTRIBUTE_VIRTUAL)
        {
            attribs << L"FILE_ATTRIBUTE_VIRTUAL" << std::endl;
            dwAttrib &= ~FILE_ATTRIBUTE_VIRTUAL;
        }

        if (dwAttrib != 0)
            attribs << L"Other: " << std::hex << std::showbase << std::setw(8) << std::setfill(L'0') << dwAttribs << std::endl;

        MessageBoxW(NULL, attribs.str().c_str(), L"File Attributes", MB_OK);

        return true;
    }
1
On

The fundamental problem you have is that your casts are incorrect. When the compiler tells you that the parameters you pass have the wrong type, the correct approach is to find a way to pass something with the right type. Instead you cast the value which simply says to the compiler, "I know better than you, please ignore the type of the variable and pretend it is what I said it is."

So, instead of writing

(LPCWSTR)test

you need to convert the integer test to a null-terminated wide string. There are lots of ways to do that. For instance, you might use a string stream:

std::wstringstream sstream;
sstream << test;
std::wstring str = test.str();

Then you can pass str.c_str() to the MessageBox API.

In C++11 you could use std::to_wstring() like this:

std::wstring str = std::to_wstring();

You are interpreting the values returned by GetFileAttributes incorrectly. These are combinations of bit flags. In other words, multiple file attribute flags may be set. You need to use the bitwise and operator, & to test for the presence of the flags.


You also get the error checking badly wrong. The documentation says:

If the function fails, the return value is INVALID_FILE_ATTRIBUTES. To get extended error information, call GetLastError.

In other words, you should only call GetLastError when GetFileAttributes returns a value of INVALID_FILE_ATTRIBUTES. This is an incredibly common mistake, that of thinking that errors are indicated by GetLastError returning a non-zero value. You must read the documentation for each and every API function to be clear on how it signals errors.


Finally, if you do successfully obtain a valid Win32 error code, use FormatMessage to get a textual description of the error.