?
Solved

Copy file without changing creation date

Posted on 2011-05-06
26
Medium Priority
?
1,426 Views
Last Modified: 2012-05-11
I want to be able to copy files without changing the creation date. With pictures, I want to retain the date they were taken. Is there a direct way to do this. Or can I get the creation date before copying and then put it back afterward ? How do I do it ?
0
Comment
Question by:ChLa
  • 15
  • 5
  • 3
  • +3
26 Comments
 
LVL 14

Expert Comment

by:systan
ID: 35711049
the code;
function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  AT,CT,WT : TSystemTime;
  A,C,W : TFileTime;
begin
    Fhandle:=createfile(Pchar(fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetFileTime(FHandle,@C,@A,@W) then
      begin
        FileTimeToSystemTime(C,CT);
        result := SystemTimeToDateTime(CT);
      end;
    FileClose(FHandle);
end;



procedure SetCreationDate(fn: string; NewDateTime: TDateTime);
var
  FileHandle: Integer;
  FileTime: TFileTime;
  LFT: TFileTime;
  LST: TSystemTime;
begin
    DecodeDate(NewDateTime, LST.wYear, LST.wMonth, LST.wDay);
    DecodeTime(NewDateTime, LST.wHour, LST.wMinute, LST.wSecond, LST.wMilliSeconds);
    if SystemTimeToFileTime(LST, LFT) then
    begin
      if LocalFileTimeToFileTime(LFT, FileTime) then
      begin
        FileHandle := FileOpen(fn, fmOpenReadWrite or fmShareExclusive);
        SetFileTime(FileHandle, @FileTime, @FileTime, nil);
      end;
    end;
    FileClose(FileHandle);
end;




procedure TForm1.Button1Click(Sender: TObject);
var getdate: tdatetime;
begin
getdate := GetCreationDate('c:\autoexec.bat');
copyfile('c:\autoexec.bat','d:\autoexec.dat',false);
SetCreationDate('d:\autoexec.dat', getdate);
end;

Open in new window

0
 
LVL 9

Expert Comment

by:Orcbighter
ID: 35711203
All you need is the FileInfo and File classes
Below is a simple function to retrieve all the files from a directory and copy them to a new location.
it then modifies the dates of the file to the values they originally had.
Note, I wrote this as a recursive function to loop down through all the sub-directories of my origibnal starting directory. If you want to process the files one at a time, then just rip out the functionality inside the recursive function and use that.

I hope it helps

// inside main program
string rootdir = "c:\\temp";
DirSearch( rootdir);

//----


private void DirSearch( string searchDir )
{
    bool bAll_OK = true;
    string[] dirFileList = null;

    try
    {
        // find all files first
        dirFileList = Directory.GetFiles( searchDir );
    }
    catch ( UnauthorizedAccessException ex )
    {
        // NTFS file systems can contain reparse points in the form of junction points, symbolic links, 
        // and hard links. 
        // The .NET Framework methods such as GetFiles and GetDirectories will not return any subdirectories 
        // under a reparse point. This behavior guards against the risk of entering into an infinite loop when 
        // two reparse points refer to each other.
        bAll_OK = false;
        Console.WriteLine( "Unauthorised access exception: {0}", ex.Message );
    }
    catch ( Exception ex )
    {
        bAll_OK = false;
        Console.WriteLine( "Unexpected Exception in Directory.GetFiles(): {0}", ex.Message );
    }

    if ( bAll_OK )
    {
        // we have some files to check in this directory
        string fileItem = "";

        try
        {
            foreach ( string item in dirFileList )
            {
                fileItem = item;
                FileAttributes attr = File.GetAttributes( item );
                string ModificationDateAsString = "";
                string CreationDateAsString = "";
                string LastAccessedDateAsString = "";
                string filename = "";

                //make sure its a file and not a sub-directory
                if ( ( attr & FileAttributes.Directory ) != FileAttributes.Directory )
                {
                    FileInfo fileInfo = new FileInfo( item );
                  
                    filename = item;
                    ModificationDateAsString = fileInfo.LastWriteTime.ToString();
                    CreationDateAsString = fileInfo.CreationTime.ToString();
                    LastAccessedDateAsString = fileInfo.LastAccessTime.ToString();

                    //...
                    // copy this file to your new destination
                    //...

                    // ... Need Newfilername to hold path of new location and filename
                    string newFilename = ""; // extract filename from old path, append new path to name
                    //...

                    // now change the date back to original
                    File.SetCreationTime( newFilename, DateTime.Parse( CreationDateAsString ) );
                    File.SetLastAccessTime( newFilename, DateTime.Parse( ModificationDateAsString ) );
                    File.SetLastWriteTime( newFilename, DateTime.Parse( LastAccessedDateAsString ) );

                }
            } // end-foreach
        }
        catch ( Exception ex )
        {
            Console.WriteLine( "DirSearch: Unexpected error for file {0} in dir {1} - {2}",
                               fileItem, searchDir, ex.Message );
        }


        try
        {
            // find all the subdirectories in this directory
            string[] subDirs = Directory.GetDirectories( searchDir );

            foreach ( string folder in subDirs )
            {
                DirSearch( folder );
            }
        }
        catch ( Exception ex )
        {
            Console.WriteLine( "DirSearch: Unexpected exception for {0} - {1}", searchDir, ex.Message );
        }
    } // end-if
} // end-function

Open in new window

0
 
LVL 1

Expert Comment

by:bakry99
ID: 35711703
listening
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 32

Expert Comment

by:Ephraim Wangoya
ID: 35717132

Hi Systan
You know there is an easier way to do it

  var
     SourceFileAge: Integer;
  begin
     SourceFileAge := FileAge('c:\source.dat');

     FileSetDate('c:\destination.dat', SourceFileAge);
0
 
LVL 14

Expert Comment

by:systan
ID: 35718973
thanks, didn't think of it.  Good.
0
 

Author Comment

by:ChLa
ID: 35723633
How does that work, Ewangoya ? Sorry for my slow response. I have had a busy last couple of days.How does it save the time and date as an integer ?
0
 
LVL 32

Expert Comment

by:Ephraim Wangoya
ID: 35723791

The function FileAge returns an integer which represents the system time

The method FileSetDate() takes in an integer value which it then converts to the corresponding FileTime internally

Since both functions work with integers, you dont have to do any conversions yourself which make the two of them convenient to use

0
 
LVL 14

Accepted Solution

by:
systan earned 2000 total points
ID: 35726151
Does NOT work; ? (file created date_time remains the same)
var
     SourceFileAge: Integer;
  begin
     SourceFileAge := FileAge('c:\bdlog.txt');
     CopyFile('c:\bdlog.txt','d:\bdlog.txt',false);
     FileSetDate('d:\bdlog.txt', SourceFileAge);
 end;

Open in new window


This works; (corrected)
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  A,C,W : TFileTime;
  Bias : double;
  SysTimeStruct: SYSTEMTIME;
  TimeZoneInfo: TTimeZoneInformation;
begin
    Bias :=0;
    Fhandle:=createfile(PChar(Fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetTimeZoneInformation(TimeZoneInfo) <> $FFFFFFFF then
    Bias := TimeZoneInfo.Bias / 1440;
    if GetFileTime(FHandle,@C, @A, @W) then
      begin
        FileTimeToSystemTime(C, SysTimeStruct);
        result := SystemTimeToDateTime(SysTimeStruct) - Bias;
      end;
    FileClose(FHandle);
end;

function SetCreationDate(FileName: string; dtCreation: TDateTime): Boolean;
var
  hHandle: THandle;
  ftCreation: TFiletime;

  function DTtoFT(dt: TDateTime): TFiletime;
  var
    dwft: DWORD;
    ft: TFiletime;
  begin
    dwft := DateTimeToFileDate(dt);
    DosDateTimeToFileTime(LongRec(dwft).Hi, LongRec(dwft).Lo, ft);
    LocalFileTimeToFileTime(ft, Result);
  end;

begin
  hhandle := CreateFile(PChar(FileName),
  GENERIC_READ or GENERIC_WRITE,0,nil,
  OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS, 0);

  if hHandle <> INVALID_HANDLE_VALUE then
  begin
    try
      ftCreation       := DTtoFT(dtCreation);
      Result := SetFileTime(hHandle, @ftCreation, nil, nil);
    finally
      CloseHandle(hHandle);
    end;
  end
  else
    Result := False;
end;


procedure TForm1.Button1Click(Sender: TObject);
var getdate: tdatetime;
sor,des: string;
begin
sor :='c:\bdlog.txt';
des := 'd:\bdlog.txt';
getdate := GetCreationDate(sor);
CopyFile(PChar(sor),PChar(des),false);
SetCreationDate(des, getdate);
ShowMessage(DateTimeToStr(getdate));
end;

end.

Open in new window

0
 

Author Comment

by:ChLa
ID: 35782543
Hi everyone...sorry for the delay...couldn't work on this yester
day.
I just tried the last thing above from systan
It doesn't seem to be working. The copied files still have the current time/date.

I copied and pasted the two procedures get and set. Here is my implementation of the third procedure:

See code box

The procedure in the code box does successfully copy the selected files.

Did I leave something out ?
procedure TMainForm.CopyLtoR(Sender: TObject);
var
I: Integer;
FileName: String;
SourceFilePath: String;
DestFilePath: String;
GetDate: tDateTime;
begin
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do       
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];
    GetDate := GetCreationDate(SourceFilePath);
    //  add something to check that the file soesn't already exist...if file 
    //doesn't exist then
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath),False);
    SetCreationDate(SourceFilePath, GetDate);
    I := I + 1;
  End;;
end;

Open in new window

0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 35782621
wouldn't you want to change the destination file date ?

 
procedure TMainForm.CopyLtoR(Sender: TObject);
var
I: Integer;
FileName: String;
SourceFilePath: String;
DestFilePath: String;
GetDate: tDateTime;
begin
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do       
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];
    GetDate := GetCreationDate(SourceFilePath);
    //  add something to check that the file soesn't already exist...if file 
    //doesn't exist then
    // if not FileExists(DestFilePath) then 
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath),False);
    SetCreationDate(DestFilePath, GetDate);
    I := I + 1;
  End;;
end;

Open in new window

0
 

Author Comment

by:ChLa
ID: 35782982
I'm trying to keep the original creation date so it continues to reflect the date an image was taken. This allows a collection of images to later be put into the order they were taken, even though they may have been copied instead of moved. Doesn't the proc above get the existing creation date with GetCreationDate, put it into GetDate, before copying the file, and then put it back after copying it, with SetCreationDate ? That's what I was hoping, but the creation date is still changing to the date of the copy, like windows.
0
 
LVL 14

Expert Comment

by:systan
ID: 35793023
I don't know how you test it, but here's the complete project,
I just test it, it really copies all the datetime properties from datecreated, datemodified, dateaccessed.
please try it too.

setdatetime.zip
0
 

Author Comment

by:ChLa
ID: 35796338
You are right...stupid mistake on my part. I had put the source file path into setdate instead of the destination file path. I don't know how I missed it when I re-checked it after Geert Gruwez's comment. Sorry.
0
 

Author Closing Comment

by:ChLa
ID: 35796350
Thank you
0
 

Author Comment

by:ChLa
ID: 35808703
Sorry for this late comment. Now that I have had time to more fully test the solution from Systan, There is an anomaly. It seems the copied files all have a new time which is 1 hour earlier that the original time. Does this have something to do with that insanity Daylight Savings Time ? Is there a fix ?
It's also not changing every file. Some copied files appear in the destination directory with the current time/date in Creation Date. Can't understand why. I'll attach my code.

If this case is reopened, I request that the last six comments before this, and this line, be removed.
procedure TMainForm.CopyLtoR(Sender: TObject); // this is the procedure that copies files from left list to right
var
  I: Integer;
  FileName: String;
  SourceFilePath: String;
  DestFilePath: String;
  GetDate: TDateTime;

begin
  Memo1.Color := clYellow;
  Memo1.Lines.Add('Copying Left to Right..Please Stand By');
  Application.ProcessMessages;
  I := 0;
  While I <= JamShellList1.SelectedFiles.Count - 1 do // the actual copying loop
  Begin
    SourceFilePath := JamShellList1.Path + JamShellList1.SelectedFiles[I];
    DestFilePath := JamShellList2.Path + JamShellList1.SelectedFiles[I];

    GetDate := GetCreationDate(SourceFilePath);

    if FileExists(DestFilePath) then
      Memo1.Lines.Add('File Exists ' + DestFilePath + ' ...Not Copied')
    else
      CopyFile(PWideChar(SourceFilePath), PWideChar(DestFilePath), False);

    SetCreationDate(DestFilePath, GetDate);
    
    I := I + 1;
  End;
  Stage := 5;
  Memo1.Color := clCream;
  Memo1.Lines.Add('Done ' + IntToStr(I) + ' Files Copied');
  JamShellList2.FullRefresh;
end;

Open in new window

0
 

Author Comment

by:ChLa
ID: 35808736
Does the time information saved with a file include seconds ? This would be a great thing when many digital stills are taken in a minute.
0
 
LVL 32

Expert Comment

by:Ephraim Wangoya
ID: 35811465

Thats because she adjusted the time using TimeBias, remove that piece of code, you don't need to adjust it yourself, windows will compensate for that
function GetCreationDate(fn:string):TDateTime;
var
  FHandle : THandle;
  A,C,W : TFileTime;
  SysTimeStruct: SYSTEMTIME;
begin
    Fhandle:=createfile(PChar(Fn), 0,FILE_SHARE_READ, nil, OPEN_EXISTING, SECURITY_ANONYMOUS, 0);
    if GetTimeZoneInformation(TimeZoneInfo) <> $FFFFFFFF then
      if GetFileTime(FHandle,@C, @A, @W) then
      begin
        FileTimeToSystemTime(C, SysTimeStruct);
        Result := SystemTimeToDateTime(SysTimeStruct);
      end;
    FileClose(FHandle);
end;

Open in new window

0
 

Author Comment

by:ChLa
ID: 35811691
I just copied and tested your new set creation date procedure above, I tested it by copying 17 MIDI files. All these files have 12-14-2010 4:58PM as the creation date/time. The first of the copied files has the same date/time as the source. The next three have todays date/time for creation date. The next four have the correct date, but the time is 8:58 PM, four hours later than it should be. The next four have todays date/time. The next two have the correct date, but the time is 8:58. The next one has todays date/time. The last two have the correct date, but 8:58PM. It doesn't make any sense to me. My procedure copies all the selected files correctly. Why did it reset the date for the first file correctly and then set the time 4 hours later for the others. And why are some files being skipped ? You can see my copy procedure is simple. The only option is it doesn't copy if the file already exists.
0
 

Author Comment

by:ChLa
ID: 35811802
I copied your file above except had to add  this variable declaration:   TimeZoneInfo: TTimeZoneInformation;

I just ran another test of the new GetCreationDate function. I copied seven image files of various types. The first original file creation date/time was 5-17-2011 9:10 PM. the copy has 5-18-2011 1:10 AM
The second file should be 12:08PM, but is 4:08 PM.
The third file 11:08AM is 3:08 PM
The fourth  12:08PM is 4:08 PM
The fifth  11:08 AM is 3:08 PM
The sixth 12:08 PM is 4:08 PM
and  finally the seventh file 3-14-2011 8:27PM turned into 3-15-2011 12"27 AM
0
 

Author Comment

by:ChLa
ID: 35811889
I went back to the last version of Get Creation Date and tested. It is perfect except for the hours being one hour early. Dates not changed. I removed " minus Bias ". It got worse again. Some dates 1 day earlier. Times four hours earlier..
0
 

Author Comment

by:ChLa
ID: 35811898
oops, I meant times four hours later, at the end of the last comment
0
 
LVL 14

Expert Comment

by:systan
ID: 35812058
hi
please really check the code carefully.

in my test here, it does really change all the data and time, but 1 thing i recognize that in the seconds it change plus 1

i think because of the time bias
0
 

Author Comment

by:ChLa
ID: 35812349
JamShellList doesn't show me seconds. I have been copying and pasting your functions. I don't see how I screwed it up. But I will look more closely. It seems it only skips redating files when I am copying tiny MIDI files. Why would it skip some ?
0
 

Author Comment

by:ChLa
ID: 35812352
Can this be a Windows 7 or Delphi 2010 anomaly ?
0
 

Author Comment

by:ChLa
ID: 35812367
I should mentio nI tried the code from Orcbighter and had troubles with it such as unrecognized commands or properties.
0
 

Author Comment

by:ChLa
ID: 35828716
Hi everyone,
I had asked another question about how to also get the last modified date. Ewangoya give me some code for that that also seems to work perfectly to solve this question. So I am now using that solution and now consider this question closed. Thank you everyone.
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This video shows how to quickly and easily deploy an email signature for all users in Office 365 and prevent it from being added to replies and forwards. (the resulting signature is applied on the server level in Exchange Online) The email signat…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses
Course of the Month15 days, 15 hours left to enroll

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question