September 21st, 2016

The case of the system() call that returned before finishing

A customer was having trouble with some code that copies a bunch of files and then processes the copy. Here’s the code they shared (suitably redacted):

// copy the files
char command[512] = {NULL};
sprintf_s(command, 512, "copy /Y %s %s >nul 2>&1",
          sourcePath, destPath);
printf("The command is %s\n", command);
Log("The command is %s", command);
system(command);

// process the files
char searchPattern[256] = {NULL};
sprintf_s(searchPattern, 256, "%s\\*", destPath);
printf("The directory is %s.\n", searchPattern);
Log("The directory is %s", searchPattern);

hFile = FindFirstFileA(searchPattern, &FindFileData);

// error when searching files
if (hFile == INVALID_HANDLE_VALUE) {
  DWORD lastError = GetLastError();

  if (lastError == ERROR_FILE_NOT_FOUND) {
    printf("No files under directory %s.\n", searchPattern);
    Log("No files under directory %s", searchPattern);
    return S_OK;
  } else {
    printf("FindFirstFile failed in directory %s with error: %d.\n",
           searchPattern, lastError);
    Log("FindFirstFile failed under directory %s with error: %d",
        searchPattern, lastError);
    return E_FAIL;
  }
}

do { 
  printf("The file is %s.\n", FindFileData.cFileName);
  Log("The file is %s", FindFileData.cFileName);
  ...

The customer reported that “It appears that Find­First­FileA does not wait for system() to finish copying the files. Here’s a sample log file:

The command is copy /Y \\server\path\to\data\* D:\data >nul 2>&1
The directory is D:\data\*
The file is .

“Observe that Find­First­FileA did not find the files we copied. How can we wait for the system() function to finish copying the files before the program proceeds to the Find­First­FileA?”

In the ensuing discussion, people suggested using Copy­File or SHFile­Operation instead of shelling out to cmd.exe. Issues with spaces and other characters in the directory names. But can you find the reason why Find­First­FileA couldn’t find the files?

Look carefully at the last line of the log: “The file is .” Part of this is confusing because the program both prints its output to the screen as well as to the log file, but prints them differently. The output to the screen includes a period at the end; the output to the log file does not.

And that’s the key. Since the output to the log file does not include a period at the end, it means that the period in the output is the actual contents of FindFileData.cFileName.

The customer misread their log file. The issue isn’t that the Find­First­FileA ran before the files were copied. The issue is that the first thing found by Find­First­FileA was the file whose name consists of a single period.

Recall that every directory has two artificial entries, one for the directory itself (.), and one for the directory’s parent (..). What you found was the first artificial entry, the one that represents the directory itself. Instead of giving up right away, keep looking, and the files you copied will show up later.

(Assuming they were all successfully copied. The program doesn’t actually check.)

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.