How to use copyfile in vc6?

titanium0203
titanium0203 used Ask the Experts™
on
Hi,

Currently I can not copy files from one folder to others. I'm using copyfile but the process failed without an error.
check code snippet as below :

	LPCTSTR lpExistingFileName = "C:\\\\Product\\New\\\\product1.txt";
	LPCTSTR lpNewFileName = "C:\\\\Product\\\\Beta\\\\";

	CString msgbuf;
	CopyFile(lpExistingFileName,lpNewFileName,false);
	printf(msgbuf, "Cannot open file %s [err=%ld]\n",GetLastError());
                OutputDebugString(msgbuf);

No Error when I try to output getlasterror.

I'm using vc6.


Thanks
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
AndyAinscowFreelance programmer / Consultant
Commented:
LPCTSTR lpNewFileName = "C:\\\\Product\\\\Beta\\\\";

That is a directory name you specify as the new name for the file.  I suspect you wanted:
 LPCTSTR lpNewFileName = "C:\\\\Product\\\\Beta\\\\product1.txt";

Author

Commented:
Hi,

Actually I want to copy product1.txt to folder c:\product\beta.

thanks.
AndyAinscowFreelance programmer / Consultant

Commented:
You need to specify the FILE NAME to copy to.
Why Diversity in Tech Matters

Kesha Williams, certified professional and software developer, explores the imbalance of diversity in the world of technology -- especially when it comes to hiring women. She showcases ways she's making a difference through the Colors of STEM program.

Todd GerbertIT Consultant
Top Expert 2010

Commented:
>> PCTSTR lpNewFileName = "C:\\\\Product\\\\Beta\\\\product1.txt";

In C, C#, C++, etc, a back-slash in a hard-coded string (i.e. a string literal) has special meaning - the backslash means "this is the start of a special code", for example to separate two words with a tab you would write "hello\tworld". If you just want a plain backslash in your string you need a way to tell the compiler "this is not the start of a special code, just put a backslash in my string" and the way you do that is to use two consecutive backslashes.  The point of all this is that your file names are being interpreted by the compiler as C:\\Product\\Beta\\product1.txt, but in Windows we normally separate path components with just a single backslash.

Your string should be C:\Product\Beta\product1.txt, and to make that string you'd write in your code: LPCTSTR lpNewFileName = "C:\\Product\\Beta\\product1.txt.

LPCTSTR lpString = "Hello\tWorld";    is    Hello    World
LPCTSTR lpString = "Hello\\World";    is    Hello\World
LPCTSTR lpString = "Hello\\\\World";  is    Hello\\World

That's probably not affecting your FileCopy, just thought I'd mention it since it seems like it may be applicable to your other question (and your other question seems like it's related to this one).
Todd GerbertIT Consultant
Top Expert 2010
Commented:
printf(msgbuf, "Cannot open file %s [err=%ld]\n",GetLastError());
                OutputDebugString(msgbuf);

I think you meant sprintf.

Why don't you try this:
void ShowErrorMessageDebug(DWORD err)
{
	TCHAR errorMessage[256];
	FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			err,
			NULL,
			errorMessage,
			256,
			NULL);
	OutputDebugString(errorMessage);
}

LPCSTR lpExistingFileName = "C:\\Product\\New\\product1.txt";
LPCSTR lpNewFileName = "C:\\Product\\Beta\\product1.txt";

if ( CopyFile(lpExistingFileName, lpNewFileName, false) == 0 )
	ShowErrorMessageDebug( GetLastError() );
else
	OutputDebugString( "Copy Succeeded" );

Open in new window

Todd GerbertIT Consultant
Top Expert 2010

Commented:
>> printf(msgbuf, "Cannot open file %s [err=%ld]\n",GetLastError());

Plus, your format string has a "%s" and then a "%ld", which means you need to provide two additional arguments to (s)printf - the first one should be a string and the second one a number:

sprintf(msgbuf, "Cannot open file %s [err=%ld]\n", lpExistingFileName, GetLastError());

Open in new window

Author

Commented:
Hi tgerbert,

I got error below :

1. The system cannot find the path specified.

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
Did you do as I initially said and supply the file name?

Author

Commented:
Hi,

Yes, and now I got this error :

The process cannot access the file because it is being used by another process.

Do I need to clear any buffer or close the Copyfile?

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
You shouldn't need to close it, unless you have it opened in an editor elsewhere for example that is.

Simplest - do a reboot and try to run your code.

Author

Commented:
Hi,

I think My program cannot copy file in real time mode. The Code as below:
...
while (TRUE)
{

CString FileName = CString(Buffer[0].FileName);
CString AbsPathFileName = m_dir + "\\"+ CString(Buffer[0].FileName).Left(Buffer[0].FileNameLength / 2);

switch(Buffer[0].Action)
 {
   case FILE_ACTION_ADDED:
      printf("%s\n+: %s\n","Triggered by add", AbsPathFileName);
      ProcessFile(AbsPathFileName,FileName);
      break;
}

}//while
CloseHandle( hDir );
return 0;
}

char ProcessFile(CString AbsFileName,CString FileNm)
{
char ADir[100];
char Inipath2[255];
CString CADir = ADir;

      LPCTSTR lpExistingFileName = AbsFileName;
      strcat (ADir,FileNm);
      LPCTSTR lpNewFileName = ADir;

                                 if(CopyFile(lpExistingFileName, lpNewFileName, false) == 0)
            {
                  ShowErrorMessageDebug( GetLastError() );
            }
            else
            {
                  OutputDebugString("Copy Succeeded");
            }


      return 1;

}

When I pumped in one file to the monitored folder it return me with :
"The process cannot access the file because it is being used by another process."

Do I need to refresh the directory list, if yes could someone enhance the query above.

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
CloseHandle( hDir );

Erm, what is that there for ?  Should it be before the while loop ?

Author

Commented:
Hi,

Sorry,

miss this line :

int ret = ReadDirectoryChangesW(hDir,&Buffer,sizeof(Buffer),TRUE,FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE,&dwBytes,NULL,NULL);

It should be before :
CString FileName = CString(Buffer[0].FileName);

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
Look at what you are doing and look at the error message:
"The process cannot access the file because it is being used by another process."

Now take a wild guess.  ;-)

Author

Commented:
Hi,

No Idea. How could I fix it?

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
Weel you now inform us that there is some processing of directories going in in your app.  Don't perform any processing of the directories you are copying from/to.
Todd GerbertIT Consultant
Top Expert 2010

Commented:
Can you post your complete code (copy/paste it so you don't miss any lines accidentally)?

Author

Commented:
Hi,

Please check below. Main purpose of this code is to
1. check any new/existing file pump in to the targeted folder.
2. automatically copy the new/existing files to new folder.

Regards,
titanium
#include "stdafx.h"
#include "stdio.h"
#include "WinBase.h"
#include "Windows.h"



int CheckFiles()
{
	DWORD	dwWaitStatus;
	HANDLE	dwChangeHandle;
	BOOL	fFinished = FALSE;
	char	SourceDir[100];
	int		Times=0,FailureCount=0;
	char	Inipath2[255];

	TCHAR	szBuffer[640] = {0}; 
	DWORD	dwOffset = 0;
	FILE_NOTIFY_INFORMATION* pInfo = NULL;
	DWORD	dwBytes;
	

	//Configuration file
	strcpy(Inipath2, "f:\\tracsdata\\admin.ini");



	//Source Directory
	GetPrivateProfileString("LOAD","MAINDIR","f:\\product\\beta\\",SourceDir,sizeof(SourceDir),Inipath2);

	OutputDebugString("* 2");
	OutputDebugString(SourceDir);


	FILE_NOTIFY_INFORMATION Buffer[1024];
	DWORD BytesReturned;
	

	CString m_dir("F:\\PRODUCT\\FILES");
	HANDLE hDir = CreateFile( CString(SourceDir),		// pointer to the file path 
     FILE_LIST_DIRECTORY,								// access (read/write) mode
     FILE_SHARE_READ|FILE_SHARE_WRITE,					// share mode
     NULL,												// security descriptor
     OPEN_EXISTING,										// how to create
     FILE_FLAG_BACKUP_SEMANTICS,						//|FILE_FLAG_OVERLAPPED,         // file attributes
     NULL												// file with attributes to copy
	);




	//endless loop
	while (TRUE)
	{
	
		OutputDebugString("Watch Targeted Directory");
	
			int ret = ReadDirectoryChangesW(hDir,&Buffer,sizeof(Buffer),TRUE,FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE,&dwBytes,NULL,NULL);
	
			CString FileName = CString(Buffer[0].FileName);
			CString AbsPathFileName = m_dir + "\\"+ CString(Buffer[0].FileName).Left(Buffer[0].FileNameLength / 2);
		 
				 switch(Buffer[0].Action)
				 {
				   case FILE_ACTION_ADDED: 
					printf("%s\n+: %s\n","Triggered by add", AbsPathFileName);
					OutputDebugString("A");
					OutputDebugString(AbsPathFileName);
					HChange.ProcessFile(AbsPathFileName,FileName);
					break; 
				   case FILE_ACTION_REMOVED: 
					printf("%s\nx: %s\n","Triggered by removed", AbsPathFileName);
					OutputDebugString("B");
					OutputDebugString(AbsPathFileName);
					break; 
				   case FILE_ACTION_MODIFIED: 
					printf("%s\nm: %s\n","Triggered by modified", AbsPathFileName);
					OutputDebugString("C");
					OutputDebugString(AbsPathFileName);
				//	ProcessFile(filename);
					break; 
				   case FILE_ACTION_RENAMED_OLD_NAME: 
					printf("%s\n%s -> %s\n","Triggered by rename", AbsPathFileName);
					//filename = CString(Buffer[1].FileName).Left(Buffer[1].FileNameLength / 2));
					OutputDebugString(AbsPathFileName);
					break; 
				   case FILE_ACTION_RENAMED_NEW_NAME: 
					printf("%s\n -> %s\n","Triggered by rename 2", AbsPathFileName);
					OutputDebugString("E");
					OutputDebugString(AbsPathFileName);
					break;
				 }
		 
	
	}//while
	CloseHandle( hDir );
	return 0;
}

char HandleChange::ProcessFile(CString AbsPath,CString FileNm)
{
	
	char TarDir[100];
	char Inipath2[255];
	CString CLcsDir = LcsDir;
	
	strcpy(Inipath2, "f:\\tracsdata\\admin.ini");
	GetPrivateProfileString("BETA","MAINDIR","f:\\product\\release\\",TarDir,sizeof(TarDir),Inipath2);


		if(!strcmp(TarDir,"DEFAULT"))
		{
			LogFile("TarDir Not Found in INI File");
			return 0;
		}
	

		if(CLcsDir.Find("\\\\0")<0)
			CLcsDir += "\\";

	
		LPCTSTR lpExistingFileName = AbsPath;
		strcat (TarDir,FileNm);
		LPCTSTR lpNewFileName = TarDir;
		
			
		OutputDebugString("Process File");
		OutputDebugString(lpExistingFileName);
		OutputDebugString(lpNewFileName);
		OutputDebugString(FileNm);



		if(CopyFile(lpExistingFileName, lpNewFileName, false) == 0)
		{
			OutputDebugString("NOT OK");
			ShowErrorMessageDebug( GetLastError() );
		}
		else
		{
			OutputDebugString("OK");
			OutputDebugString("Copy Succeeded"); 
		}


	return 1;
}


void HandleChange::ShowErrorMessageDebug(DWORD err)
{
	TCHAR errorMessage[256];
	FormatMessage(
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			err,
			NULL,
			errorMessage,
			256,
			NULL);
	OutputDebugString(errorMessage);
}

Open in new window

Author

Commented:
Increase Point to 300
AndyAinscowFreelance programmer / Consultant

Commented:
I suggested earlier to not perform processing of the directories as you copy.  Have you tried it yet?
Top Expert 2016

Commented:
The statement

   if(CLcsDir.Find("\\\\0")<0)

probably doesn't do what you expect.

it would search for string \\0 cause two of the backslashes were interpreted as escape characters.

if you intended to search for a final backslash (closed by right-hand zero character), you can't use a literal like "\\\0"  (note, it is 3 backslashes), cause any literal is looked from left to right for the first occurrence of a zero character '\0'. so the '\0' in the literal wasn't used for searchstring which only has one backslash and nothing else.

but the error you got has different reason: as far as i understand your code you are trying to watch a directory for file changes made by (an)other application(s). if you got such an event you take the filename and pass it as output-filename to HandleChange::ProcessFile. then you read inifile for output temp directory. the code inside the ProcessFile function is not very good. you were defining local string variables CLcsDir which wasn't used later. you use temporary LPCTSTR pointer where you assign a CString object (what could work cause CString has appropriate cast operator) but is not needed and not good code. but what you didn't consider is that the file you got from ReadDirectoryChangesW most likely exclusively is opened by the application it belongs to. if so, your CopyFile fails because of that cause you need at least read access to the file. another reason for the error you got can be that your output directory doesn't exist and will not be created from CopyFile. last possibility zoppo already mentioned is that you opened the output file yourself with some editor or similar.

Sara  

Author

Commented:
AndyAinscow,

How can I do that? If I disable processing of directories so many application is not in real time anymore.

sarabande,

Could you please enhance the code above?

Thanks
AndyAinscowFreelance programmer / Consultant

Commented:
>>How can I do that? If I disable processing of directories so many application is not in real time anymore.

Consider.  Your current approach does NOT work as you have found out.  

Why can't the file be copied?  
Because your code is stopping it
Because another application/process is stopping it

If you tried to turn off your code when attempting the copy then
It copies - your code is at fault
It fails - another process it stopping it BUT your code still might be stopping it

Why do we try this.  Because when one finds the problem one can start solving it.  Just guessing what is the problem then making a solution might be lucky, or it might just waste a lot of time and effort.
Top Expert 2016
Commented:
first, you shouldn't read from inifile only once. so to get the target directory you should do that in the calling function and not in ProcessFile

CString TarDir;
GetPrivateProfileString("BETA","MAINDIR","f:\\product\\release\\",TarDir.GetBuffer(MAX_PATH),MAX_PATH,"f:\\tracsdata\\admin.ini");
TarDir.ReleaseBuffer(-1);
   
if(TarDir == "DEFAULT")
{
    LogFile("TarDir Not Found in INI File");
    return 0;
}

Open in new window



then you would pass the target directory to ProcessFile:

char HandleChange::ProcessFile(const CString & AbsPath,const CString & TarDir, const CString & FileNm)
{
    
    CString TarFile = TarDir;
    TarFile.TrimRight();
    if (TarFile.ReverseFind(_T('\\')) == -1 && TarFile.ReverseFind(_T('/')) == -1)
        TarFile += "\\";
    TarFile += FileNm;

    OutputDebugString("Process File");
    OutputDebugString(AbsPath);
    OutputDebugString(TarFile);
    OutputDebugString(FileNm);
   
    
    if(CopyFile(AbsPath, TarFile, FALSE) == 0)
    {
        OutputDebugString("NOT OK");
        ShowErrorMessageDebug( GetLastError() );
    }
    else
    {
        OutputDebugString("OK");
        OutputDebugString("Copy Succeeded"); 
    }

Open in new window


note, the above will not solve the principal problems of your approach:

- the files you copy may be exclusively opened by another application and then you can't copy it,
- you'll get a change for every write another app does for a file. if you do a full copy of the file after each write your total performance will decrease dramatically.
- it also is likely that the copyfile isn't fully done by the filesystem if you have two changes very close. then the error you currently get is because of your own deferred write.

to solve the issues in my opinion you have to do a redesign. you could check the readability of files using _access before copying. you shouldn't do a copyfile if the another program continuouusly writes to a file. you could try to do the copy deferred when the changes for a file stopped for a while. that way you also would avoid to copy a file multiple times.

Sara

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial