File Iteration Problem

Hey, I'm currently writing a file backup/synch program and I'm currently writing the most important function for it. They synch function. Ignoring the ultimate purpose of the function, I seem to be inherently missing a fundamental concept for file iteration using FindNextFile(). Here's my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void synch(const string sTarget, const string sDest)
{
	if (dirExists(sDest) && dirExists(sTarget))
	{
		WIN32_FIND_DATA search_data;
		HANDLE handle = FindFirstFile(sTarget.c_str(), &search_data);
		if (handle != INVALID_HANDLE_VALUE)
		{
			do 
			{
				if (GetFileAttributes(search_data.cFileName) == FILE_ATTRIBUTE_DIRECTORY)
				{
					CreateDirectory((sDest + "\\" + search_data.cFileName).c_str(), NULL);
					synch(search_data.cFileName, sDest + "\\" + search_data.cFileName);
				}
				else
				{
					if (!CopyFile((sTarget + "\\" + search_data.cFileName).c_str(), (sDest + "\\" + search_data.cFileName).c_str(), false))
					{
						MessageBox(NULL, ("ERROR backing up \"" + (string)search_data.cFileName + "\"").c_str(), "MySynch Message", MB_OK | MB_ICONERROR);
					}
				}
			} while (FindNextFile(handle, &search_data));
		}
		FindClose(handle);
	}
	else
	{
		MessageBox(NULL, ("ERROR finding directory \"" + sTarget + "\" or \"" + sDest + "\"").c_str(), "MySynch Message", MB_OK | MB_ICONERROR);
	}
}


Before my problems inside the do-while loop, I only iterate through the directory itself. Say I have a folder named "bob" with three files in it and another directory. Only "bob" gets checked and the do-while ends after the first run. Please assume dirExists() returns true if a directory with the file path that I pass it exists. sTarget and sDest are both file paths.

I HAVE seen the MSDN rendition of this. As helpful as it is, I can't seem to understand how mine differs fundamentally from it. Thanks so much in advance!
closed account (Dy7SLyTq)
are you sure that CreateDirectory is working?

edit: test it with a bool and cout<<
Last edited on
1. You need to use a wildcard with FindFirstFile if you want to find everything in a folder; if you give just a folder path, then all you're going to find is the folder.

2. You don't need to use GetFileAttributes on line 11 as the dwFileAttributes member of the WIN32_FIND_DATA struct already provides this data.

3. The file attibutes are a set of bits, so you need to test using bit-wise and (&).

4. You also need to handle (ignore?) the current dir (".") and parent dir ("..") entries or you will end up in an infinite loop.

5. FindClose(handle); should be inside the if (handle != INVALID_HANDLE_VALUE) block, not after it.

6. If dirExists is your own function, you should replace it with PathIsDirectory from the Windows Path API

PathIsDirectory
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773621%28v=vs.85%29.aspx

PathFileExists
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773584%28v=vs.85%29.aspx

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#define WIN32_LEAN_AND_MEAN 
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <shlwapi.h>
#include <iostream>
#include <string>
#pragma comment(lib, "shlwapi.lib")
using namespace std;

#define LETS_PRETEND

inline bool IsDots(const char* path)
{
    return ((0 == strcmp(path, ".")) || (0 == strcmp(path, "..")));
}

void synch(const string sTarget, const string sDest)
{
    // using Path API PathIsDirectory rather than custom function
#ifndef LETS_PRETEND
    if (PathIsDirectory(sDest.c_str()) && PathIsDirectory(sTarget.c_str()))
#endif
    {
        WIN32_FIND_DATA search_data;
        // now using wildcard
        HANDLE handle = FindFirstFile((sTarget + "\\*.*").c_str() , &search_data);
        if (handle != INVALID_HANDLE_VALUE)
        {
            do 
            {
                //if (GetFileAttributes(search_data.cFileName) == FILE_ATTRIBUTE_DIRECTORY)
                if (search_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    // guard against processing this folder again, or the parent
                    if( !IsDots(search_data.cFileName) )
                    {
#ifdef LETS_PRETEND
                        std::cout << "CreateDirectory(" << (sDest + "\\" + search_data.cFileName) << ", NULL);" << std::endl;
#else
                        CreateDirectory((sDest + "\\" + search_data.cFileName).c_str(), NULL);
#endif
                        // to be consistent with other calls, add sTarger + "\\" prefix
                        //synch(search_data.cFileName, sDest + "\\" + search_data.cFileName);
                        synch(sTarget + "\\" + search_data.cFileName, sDest + "\\" + search_data.cFileName);
                    }
                }
                else
                {
#ifdef LETS_PRETEND
                    std::cout << "CopyFile(" << (sTarget + "\\" + search_data.cFileName) << ", " << (sDest + "\\" + search_data.cFileName) << ", FALSE))" << endl;
#else
                    if (!CopyFile((sTarget + "\\" + search_data.cFileName).c_str(), (sDest + "\\" + search_data.cFileName).c_str(), FALSE))
                    {
                        MessageBox(NULL, ("ERROR backing up \"" + (string)search_data.cFileName + "\"").c_str(), "MySynch Message", MB_OK | MB_ICONERROR);
                    }
#endif
                }
            } while (FindNextFile(handle, &search_data));

            FindClose(handle);
        }
    }
#ifndef LETS_PRETEND
    else
    {
        MessageBox(NULL, ("ERROR finding directory \"" + sTarget + "\" or \"" + sDest + "\"").c_str(), "MySynch Message", MB_OK | MB_ICONERROR);
    }
#endif
}

int main()
{
    synch("C:\\test", "C:\\temp");

    return 0;
}
Last edited on
Quick response here. Thanks for responding.

DTS: I haven't tested inside the do-while loop, and I certainly will after I manage to get inside it.

Andy: 1. I wasn't sure what a wildcard was until I saw your example with *.* but I take it that wildcard refers to any file with any extension inside the directory i specified before.
2. Oh, I didn't know that. I'll see how to access that tomorrow. Seems fairly simple.
3. I'm fairly new to programming and I've frequently used the && operator but was warned against using a single & as it was never a focus of our classes. I saw recently that someone had used a & (perhaps it was the MSDN example) but I had thought that it was a typo. Would you mind a brief explanation?
4. I understand what you mean by ignoring them, but how would I go about it? (Ignore this if you explained in the example, I didn't have time to go through it all right now)
5. Ah, you're right!
6. Thank you very much!
1. The wildcard refers to any directory entry (file or subdirectory) and behaves the same way as it does on the command line with dir and other commands. You can use * and ?

2. See MSDN !

3. You need to be careful to use && when you mean a logical and, but & is also used. It ands the bits. e.g. for one char with bits 11110000 and another with 01010101, the bit-wise and ends up as 01010000 (i.e. only the bits which are set in both are set in the result.)

In the file attibut case I know that FILE_ATTRIBUTE_DIRECTORY is a single bit, so the result of bit-wise and-ing it with the attributes will be non-zero only if the directory attribute is set.

4. See code

...

Andy
Last edited on
Worked like a charm. Thanks so much!
:-)

Andy

PS BTW...

I would code this

if (!CopyFile((sTarget + "\\" + search_data.cFileName).c_str(), (sDest + "\\" + search_data.cFileName).c_str(), FALSE))

like this

1
2
3
    string pathFrom = sTarget + "\\" + search_data.cFileName;
    string pathTo   = sDest + "\\" + search_data.cFileName;
    if (!CopyFile(pathFrom.c_str(), pathTo.c_str(), FALSE))


as it's more debugger friendly.

When you don't provide a variable names, the compiler will use anonymous objects which are no more efficient but are harder to get at when debugging.

(I also find the more long-winded form more readable...)
Last edited on
Also, you're using the wrong character type.

http://www.cplusplus.com/forum/windows/106683/

If using char-based strings you should be using CopyFileA, WIN32_FIND_DATAA, FindFirstFileA, etc.

The non-A versions are for TCHAR-based strings.
When you don't provide a variable names, the compiler will use anonymous objects which are no more efficient but are harder to get at when debugging.

Ah... I see. I didn't know that. I've always just tried not to allocate more memory than was needed, but I don't really know how compilers work.
And, yes, I would agree that it's more readable.

Disch: I think I'm beginning to understand WinAPI more than I was a month ago. Back then I was told something similar (very probably by you) after another one of my posts, but, as I didn't really get it and my program worked, I didn't bother too much.
What benefits would a program gain from using the correct type? Is it just less prone to error? Is it more memory efficient? Is it faster? Regardless, as a grammar stickler, I'd rather do what's "correct" regardless, so I'm going to go ahead and change this tomorrow.

Thanks again!
Ah, I may see what's going on with the typing. I just noticed the #define TextOut TextOutA upon hovering over the function, so I guess it's trying to autocorrect me with settings perhaps.
Topic archived. No new replies allowed.