How to fix error with SHFileOperationA when I am trying to copy a directory from a remote drive to a new directory on that same drive. WinSysErr 183

Viewed 55

When I am using this method on my local drive it will work as intended and copy my intended directory(With two files inside) to another location on my local drive.

However, when trying to do the same thing on a remote drive, I receive an error code from SHFileOperationA( &directory ); Specifically 183, which looking at Windows System Errors I find this:

ERROR_ALREADY_EXISTS 183 (0xB7) Cannot create a file when that file already exists.

Any ideas on what could be causing this issue? I know that there is no file/folder restriction on this Q: drive because I am able to create directories, and because of this, I am sure that the path names are correct also.

void Utilities::CopyDirectory(CString from, CString to)
{
SHFILEOPSTRUCTA directory;
/////////////////////////////////test strings
////////////////////////////////from = "Q:\\TestFolder"
////////////////////////////////to = "Q:\\A\\B"
char fromDir[100];
char toDir[100];

strcpy(fromDir, from);
strcpy(toDir, to);

//Last file name is terminated with a double NULL character to indicate end of buffer
//add null character after the last single NULL
fromDir[strlen(fromDir) +1] = 0;
toDir[strlen(toDir) +1] = 0; 

directory.hwnd = NULL; 
directory.wFunc = FO_COPY;
directory.pFrom = fromDir;
directory.pTo = toDir;
directory.fFlags = FOF_RENAMEONCOLLISION | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_SILENT;
directory.fAnyOperationsAborted = 0;
directory.hNameMappings = NULL;
directory.lpszProgressTitle = NULL;

int i = SHFileOperationA( &directory );
//i = 0 if success. If not Windows System Error Code
if(i != 0)
    {
    sprintf(gBuf, "Error Occured in copying temp directory to: \n%s\n\n WinSysErrCode: %d", to, i);
    ::MessageBox(NULL, gBuf, "Directory Copy To Outbox Error", MB_OK);
    DeleteDirectory(from);
    return;
    }

//Delete temporary outbox folder after it was sent to Outbox
DeleteDirectory(from);
if(from.Find("QPPR_") >= 0)
    {
    sprintf(gBuf, "Receipt has been sent to the email server outbox.\n\nSent to: %s", to);
    ::MessageBox(NULL, gBuf, "Email Sent", MB_OK);
    }
else if(from.Find("QPP_") >= 0)
    {
    sprintf(gBuf, "Pay Now Request has been sent to the email server outbox.\n\nSent to: %s", to);
    ::MessageBox(NULL, gBuf, "Email Sent", MB_OK);
    }
}
1 Answers

I receive an error code from SHFileOperationA( &directory ); Specifically 183, which looking at Windows System Errors I find this: ERROR_ALREADY_EXISTS 183 (0xB7) Cannot create a file when that file already exists.

That is not what error code 183 means in this situation. The SHFileOperation() documentation has the following to say about error 183 (hex 0xB7):

Return value

Type: int

Returns zero if successful; otherwise nonzero. Applications normally should simply check for zero or nonzero.

...

Do not use GetLastError with the return values of this function.

To examine the nonzero values for troubleshooting purposes, they largely map to those defined in Winerror.h. However, several of its possible return values are based on pre-Win32 error codes, which in some cases overlap the later Winerror.h values without matching their meaning. Those particular values are detailed here, and for these specific values only these meanings should be accepted over the Winerror.h codes. However, these values are provided with these warnings:

These are pre-Win32 error codes and are no longer supported or defined in any public header file. To use them, you must either define them yourself or compare against the numerical value.

These error codes are subject to change and have historically done so.

These values are provided only as an aid in debugging. They should not be regarded as definitive.

Error Code Value Meaning
... ... ...
DE_ERROR_MAX 0xB7 MAX_PATH was exceeded during the operation.
... ... ...

And, FWIW, your fromDir and toDir buffers are potential buffer overflows waiting to happen. MAX_PATH is 260 characters, but your buffers can only hold 100 characters each, and strcpy() does not perform any bounds checking. At the very least, your buffers should be MAX_PATH in size, and you should be using strcpy_s() to protect them from overflows, eg:

char fromDir[MAX_PATH+2]{};
char toDir[MAX_PATH+2]{};

if (strcpy_s(fromDir, from) != 0)
{
    // error...
}

if (strcpy_s(toDir, to) != 0)
{
    // error...
}

...

Or better, allocate the buffers dynamically to appropriate sizes, eg:

int iFromLen = strlen(from);
int iToLen = strlen(to);

char *fromDir = new char[iFromLen+2];
char *toDir = new char[iToLen+2];

strcpy(fromDir, from);
strcpy(toDir, to);

fromDir[iFromLen+1] = '\0';
toDir[iToLen+1] = '\0'; 

...

delete[] fromDir;
delete[] toDir;

Alternatively, since you are passing in the CString objects by value, they are already copies anyway, so just modify them directly and then use them as-is, eg:

...
from += _T('\0');
to += _T('\0');

directory.pFrom = from.c_str();
directory.pTo = to.c_str();
...
Related