I have a program that writes to a log file and zips it. I want to set it up so that it will take the log file and zip it after a month and clear the file and reset it to do it again if another month has passed
procedure SendToLog(Const MType : twcMTypes; Const callProgram, callPas, callProssecs, EMessage, zipName : String; AddlStr : String = '' );
Const
MTValues = 'EDS';
var
LogFile : TextFile;
LogName : String;
EString : String;
begin
logName := WebLogPath; // þ for delimeter
EString := MTValues[ Ord( MType )+1] + PC + FormatDateTime( 'mm/dd/yyyy hh:nn:ss.zzz', Now )
+ PC + callProgram + PC + callpas + PC + callProssecs + PC + EMessage;
Assign( LogFile, LogName );
if FileExists(LogName) then
Append( LogFile ) { Open to Append }
else
begin
Rewrite( LogFile ); { Create file }
end;
Writeln( LogFile, EString );
Close( LogFile );
ArchiveFiles('C:', 'mytest.log', 'C:', zipName + '.zip', 'M');
I want to know how I make so that every time the program logs something it checks if the a month has passed then it will zip everything into a new file and reset the log.
You would have to either:
keep track of the last write date somewhere, persistently across app restarts.
query the last write date of the log file itself using the Win32 API GetFileTime() function.
put the current date on each log entry that you write, then you can seek to the end of the log file and read the date from the last log entry that was written.
Each time you want to write a new log entry, compare the month+year of the last known date against the current date and then zip+reset the log file if the current date is higher.
http://docwiki.embarcadero.com/Libraries/XE2/en/System.IOUtils.TFile.GetCreationTime
http://docwiki.embarcadero.com/Libraries/XE2/en/System.IOUtils.TFile.AppendAllText
http://docwiki.embarcadero.com/Libraries/XE2/en/System.IOUtils.TFile.Exists
So you just check for creation date and then you decide if you need a new file or not.
procedure SendToLog(Const MType : twcMTypes; Const callProgram, callPas, callProssecs, EMessage, zipName : String; AddlStr : String = '' );
Const
MTValues = 'EDS';
MaxAgeBeforeNewLogFile = 30; // 30 days, in TDateTime type convention
var
LogFile : TextFile;
LogName : String;
EString : String;
NeedZipLogFile : Boolean; ZipName: String;
begin
logName := WebLogPath; // þ for delimeter
EString := MTValues[ Ord( MType )+1] + PC + FormatDateTime( 'mm/dd/yyyy hh:nn:ss.zzz', Now )
+ PC + callProgram + PC + callpas + PC + callProssecs + PC + EMessage;
NeedZipLogFile := False;
if System.IOUtils.TFile.Exists( LogName ) then
NeedZipLogFile := Now() - System.IOUtils.TFile.GetCreationTime( LogName )
> MaxAgeBeforeNewLogFile;
if NeedZipLogFile then begin
ZipName := _Generate_New_Non_Used_And_Proper_Name_For_Archive();
_Save_Log_Into_Zip( LogName );
If _Secure_Keeping_Of_Logs_Is_Very_Very_Important then begin
_Flush_Windows_File_Cache_To_Disk( ZipName );
_Read_Zipped_Log_Into_Memory( ZipName, ExtractFileName( LogName ), _Temp_Memory_Buffer );
_Compare_With_Old_Log_File_And_Ensure_Nothing_Was_Lost( LogName, _Temp_Memory_Buffer);
end;
DeleteFile( LogFile);
end;
System.IOUtils.TFile.AppendAllText( LogFile, EString );
end;
Related
I am trying to make a application which is based around backing up data. I would like to add a option to copy a folder with its contents while ignoring the subfolders only.
I use TDirectory.Copy('C:\folder','C:\folder2'); to copy folders but that has no additional data requested other than the directory to copy and where to copy.
So, Is there a simple way to achieve this?
A function that can be called would work too.
It's not perfect, but you could make your own routine based on this :
procedure TForm2.Button1Click(Sender: TObject);
var
aSourceDir : String;
aDestDir : String;
aFileList : TStringDynArray;
iFile : Integer;
aSourceFileName : String;
aDestFileName : String;
begin
aSourceDir := 'C:\DEV\GitRepositories\TestProjects\WithStatementSample\';
aDestDir := 'C:\DEV\GitRepositories\TestProjects\WithStatementSample2\';
aFileList := TDirectory.GetFiles( aSourceDir );
if not ( TDirectory.Exists( aDestDir ) ) and
( Length( aFileList ) > 0 ) then
begin
TDirectory.CreateDirectory( aDestDir );
end;
for iFile := 0 to Pred( Length( aFileList ) ) do
begin
aSourceFileName := aFileList[ iFile ];
aDestFileName := IncludeTrailingPathDelimiter( aDestDir ) +
ExtractFileName( aFileList[ iFile ] );
TFile.Copy( aSourceFileName, aDestFileName );
end;
end;
This will simply loop over every file found in the Source directory and copy it to the destination directory. It will not copy folders, nor copy contents of the folders.
Again, it's not 100% fool/bullet proof though, so you will have to adapt it to your needs if necessary.
Dec.26,2022 Update... The example code I submitted used literal values which
actually worked but not for my application. so...the problem with vars was
PAnsiChar not managing strings, so... (rather verbose code)... sSCBHostPath :=
gsAppPath + 'SCBHosts\'; sDirName := 'SCBHost' +
Trim(absqMyGuests.FieldByName('Guest_Name').AsString); sNewHostAppPath :=
sSCBHostPath + sDirName; sPCharString := 'XCopy ' + sAppDeploymentPath + ' ' +
sNewHostAppPath + ' /E /H /C /I'; cPChar {PAnsiChar type} :=
StringToPAnsiChar(sPCharString); //to reader, function code follows TRY
WinExec(cPChar,SW_SHOWNORMAL); EXCEPT ON E:EXCEPTION DO messagedlg('Exception:
' + e.Message,mterror,[mbok],0); END; // This function converts a string to a
PAnsiChar // If the output is not the same, an exception is raised // Author:
nogabel#hotmail.com function
TfInternetServersAndClients.StringToPAnsiChar(stringVar : string) : PAnsiChar;
Var AnsString : AnsiString; InternalError : Boolean; begin InternalError :=
false; Result := ''; try if stringVar <> '' Then begin AnsString :=
AnsiString(StringVar); Result := PAnsiChar(PAnsiString(AnsString)); end;
Except InternalError := true; end; if InternalError or (String(Result) <>
stringVar) then begin Raise Exception.Create('Conversion from string to
PAnsiChar failed!'); end; end; Dec.25,2022 Delphi XE2 Pro...
This opens Command.com and copies an entire folder and its subfolders and all files to a different drive, then disappears.
WinExec(PAnsiChar('XCopy C:\Dir1 E:\Dir1 /E /H /C /I'),SW_SHOWNORMAL);
Next.
This opens Command.com and copies an entire folder and its subfolders and all files to an existing folder on the same drive named C:\NewDir. The resulting C:\NewDir will contain a folder named Dir1 with all of its subfolders and files.
WinExec(PAnsiChar('XCopy C:\Dir1 C:\NewDir /E /H /C /I'),SW_SHOWNORMAL);
I made a simple program that adds ones information (Name, surname, ID ect) to a .txt file. When ever I make new details in the program, and click on a button to save the information, it rewrites it in the .txt file.
Here's my code:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
InfoFile : TStringList;
Name, Surname, ExtraInfo : String;
PhoneNumber,ID : Integer;
Date : TDateTime;
begin
InfoFile := TStringList.Create;
Name := edtName.text;
Surname := edtSurname.Text;
ID := StrToInt64(edtID.Text);
PhoneNumber := StrToInt64(edtPhone.Text);
Date := StrToDate(edtJoinDate.Text);
try
InfoFile.Add('NAME: '+Name);
InfoFile.Add('SURNAME: '+Surname);
InfoFile.Add('ID NUMBER: '+IntToStr(ID));
InfoFile.Add('PHONE NUMBER: '+IntToStr(PhoneNumber));
InfoFile.Add('DATE JOINED :'+DateToStr(Date));
InfoFile.Add(''); // Spacers to separate next set of details
InfoFile.Add('');
InfoFile.SaveToFile('C:\Users\GrassMan\Desktop\InfoSaver\imfofile.txt');
finally
InfoFile.Free;
end;
So instead of ADDING new details to the .txt file, its rewriting it. I know im doing something, if someone wouldn't mind giving me a hand.
Thanks
Either load the file at the beginning (via LoadFromFile), before adding to it and writing it back; or else forget about TStringList, and just use WriteLn, after opening the file with Append.
Should look like this:
begin
InfoFile := TStringList.Create;
Name := edtName.text;
Surname := edtSurname.Text;
ID := (edtID.Text);
PhoneNumber :=(edtPhone.Text);
try
InfoFile.LoadFromFile('C:\Users\grassman\Desktop\infofile.txt');
InfoFile.Add('NAME: '+Name);
InfoFile.Add('SURNAME: '+Surname);
InfoFile.Add('ID NUMBER: '+ ID);
InfoFile.Add('PHONE NUMBER: '+(PhoneNumber));
InfoFile.Add('Time of registration: ' + TimeToStr(time));
InfoFile.Add('Date of registration: ' + DateToStr(date));
InfoFile.Add(''); // Spacers to separate next set of details
InfoFile.Add('');
InfoFile.SaveToFile('C:\Users\grassman\Desktop\infofile.txt');
finally
InfoFile.Free;
procedure TForm1.BitBtn1Click(Sender: TObject);
var
InfoFile : TStringList;
Name, Surname, ExtraInfo : String;
PhoneNumber,ID : Integer;
Date : TDateTime;
FS : TFileStream;
begin
Name := edtName.text;
Surname := edtSurname.Text;
ID := StrToInt64(edtID.Text);
PhoneNumber := StrToInt64(edtPhone.Text);
Date := StrToDate(edtJoinDate.Text);
InfoFile := TStringList.Create;
try
InfoFile.Add('NAME: '+Name);
InfoFile.Add('SURNAME: '+Surname);
InfoFile.Add('ID NUMBER: '+IntToStr(ID));
InfoFile.Add('PHONE NUMBER: '+IntToStr(PhoneNumber));
InfoFile.Add('DATE JOINED :'+DateToStr(Date));
InfoFile.Add(''); // Spacers to separate next set of details
InfoFile.Add('');
FS := TFileStream.Create('C:\Users\GrassMan\Desktop\InfoSaver\imfofile.txt', fmOpenWrite);
try
FS.Seek(0, soEnd);
InfoFile.SaveToStream(FS);
finally
FS.Free;
end;
finally
InfoFile.Free;
end;
end;
You should use TFileStream:
var
recordStr: string;
fs: TFileStream;
fsFlags: Word;
filePath: string;
begin
filePath := 'C:\Users\GrassMan\Desktop\InfoSaver\imfofile.txt';
recordStr := 'NAME: '+ Name + #13#10 +
'SURNAME: '+ Surname + #13#10 +
'ID NUMBER: '+ IntToStr(ID) + #13#10 +
'PHONE NUMBER: '+ IntToStr(PhoneNumber) + #13#10 +
'DATE JOINED :' + DateToStr(Date) + #13#10 +
#13#10#13#10; // Spaces to separate next set of details
// open if exists, create if not
fsFlags := fmOpenWrite;
if (not FileExists(filePath)) then
fsFlags := fsFlags or fmCreate;
try
fs := TFileStream.Create(filePath);
try
fs.Seek(0, soEnd); // go to the end of the file
fs.Write(recordStr[1], Length(recordStr));
finally
fs.Free;
end;
except on ex: Exception do
begin
ShowMessage('Error while writing to the file: ' + ex.Message);
end;
end;
end;
I have the following function with parameters
aFile = a full filename
aFolder = a foldername to copy/move to
aGuid = the guid that the document is assigned
aAction = what to do with the fil (move or copy)
I would guess the line if Trim(NewFile) = Trim(aFile) then Exit should stop the code from doing anything if the old file is the same as the new. But it doesn't. The line if FileExists(NewFile) is executed even if the files are the same.
In my debug log I have
30-05-2013 08:10:34:840 # New file: C:_Delphi_Compiled\HomeSuite\Debug\indbo\computerfladskaerm\968ED02C-21B5-4582-8A49-8463E01ADCB3.pdf
30-05-2013 08:10:34:841 # Old file: C:_Delphi_Compiled\HomeSuite\Debug\Indbo\computerfladskaerm\968ED02C-21B5-4582-8A49-8463E01ADCB3.pdf
and as far as I can tell these names are the same
function DocumentHandle(aFile, aFolder, aGuid: string; aAction: TDocumentAction): string;
const
CopyMsg = 'Der findes allerede en fil med det navn!' + sLineBreak +
'Filen omdøbes derfor til et unikt navn';
var
NewFile: string;
begin
Result := aFile;
try
NewFile := ExtractFileName(aFile);
NewFile := aFolder + NewFile;
if Trim(NewFile) = Trim(aFile) then
Exit;
if FileExists(NewFile) then
begin
NewFile := ExtractFileExt(aFile);
NewFile := aFolder + CleanGuid(aGuid) + NewFile;
MessageDlg(CopyMsg, mtWarning, [mbOk], 0);
end;
case aAction of
daCopy:
begin
if CopyFile(PwideChar(aFile), PwideChar(NewFile), False) then
Result := NewFile;
end;
daMove:
begin
if MoveFile(PwideChar(aFile), PwideChar(NewFile)) then
Result := NewFile;
end;
end;
except
on E: exception do
Logfile.Error('U_Documents.DocumentHandle: ' + E.Message);
end;
end;
Comparison is CaseSensitive you have indbo vs. Indbo in your filenames.
You could compare e.g.
UpperCase(f1)=UpperCase(f2)
or
if SameText(f1,f2) then ...
Rather than comparing strings, which can lead to false positives, you could alternatively convert the file paths to PIDLs using SHParseDisplayName() or IShellFolder.ParseDisplayName(), and then compare those using IShellFolder.CompareIDs(). That would allow you to not only compare files of mixed cases, but also compare short vs long file names, etc.
It looks like you're keeping garbage data in your wide string after the meaningful part, can you try Length(aMessage) on both the string and find out if length is same..
I am using a Delphi procedure to Compact Access Database
Code sniphet of procedure is:
procedure CompactDatabase(pFullDatabasePathName : string; pLoginName : string = ''; pPassword : string = ''; pSystemDb : string = '');
var
JE : TJetEngine;
sdbTemp : String;
sdbTempConn : String;
sdbSrcConn : String;
loginString : String;
systemDbString: String;
compactDone : Boolean;
const
SProviderAccess2007 = 'Provider=Microsoft.ACE.OLEDB.12.0;Data Source=';
SEngine = ';Jet OLEDB:Engine Type=';
Access2007EngineType = '5';
begin
loginString := '';
if (pLoginName <> '') then
loginString := ';User Id= ' + pLoginName + '; Password = ' + pPassword;
if ((pLoginName = '') and (pPassword <> '')) then
loginString := ';Jet OLEDB:Database Password = ' + pPassword;
systemDbString := '';
if (pSystemDb <> '') then
systemDbString := ';Jet OLEDB:System Database = ' + pSystemDb;
try
compactDone := False;
JE := TJetEngine.Create(Application);
sdbTemp := ExtractFileDir(pFullDatabasePathName) + '\TEMP' + ExtractFileName(pFullDatabasePathName);
if FileExists(sdbTemp) then
DeleteFile(sdbTemp);
//Access 2007
if not compactDone then
begin
try
sdbSrcConn := SProviderAccess2007 + pFullDatabasePathName + loginString + systemDbString + SEngine + Access2007EngineType;
sdbTempConn := SProviderAccess2007 + sdbTemp + SEngine + Access2007EngineType;
JE.CompactDatabase(sdbSrcConn, sdbTempConn);
compactDone := True;
except
end;
end;
if not compactDone then
Raise Exception.Create('Compacting of database: ' + pFullDatabasePathName + 'failed!');
if (not DeleteFile(pFullDatabasePathName)) then
Raise Exception.Create('Compacting failed because cannot delete database: ' + pFullDatabasePathName);
if (not RenameFile(sdbTemp, pFullDatabasePathName)) then
Raise Exception.Create('Compacting failed because cannot overwrite database: ' + pFullDatabasePathName + ' by temporary compacted database: ' + sdbTemp);
if FileExists(sdbTemp) then
DeleteFile(sdbTemp);
finally
JE.FreeOnRelease;
end;
end;
The outcome of this procedure is the Compact Access Database, but in Access 2002-2003 format.
I am unable to get the problem area. Is some dll or regsitry settings needs to be renewed?
Please help...
Thanks 4 ur support, I found a working solution to this problem and found it worth sharing with you.
Now instead of using Jet 4, I am now using 'DAO.DBEngine.120'.
//Access 2007
if not compactDone then
begin
try
// DaoAccess2007 is OleVariant Type
DaoAccess2007 := CreateOleObject('DAO.DBEngine.120');
DaoAccess2007.CompactDatabase(pFullDatabasePathName,sdbTemp);
compactDone := True;
except
end;
end;
The outcome is the Compacted Databsae in Access 2007 Format.
Cheers!
I have an Delphi 4 application, that extracts data from XLS 2003 sheets (filled Forms ) and inserts into SQL2005 DB .
i have a group of fields in XSL and SQL2005 called.In the Delphi code it is correspondingly called 133, 167 etc.The words around "smrBgm133GallonsGross, .." i.e "smrBgm" and "GrossGallons" are concatinated accordingly in the Delphi files.
SQL/XLS Delphi
smrBgm133GallonsGross... 133
smrBgm167GallonsGross ... 167
For the above I added a new field called in XSL/SQL called smrBgm167GallonsGrossDA
But the PROBLEM is in the Delphi it should be NAMED AS 229, NOT as 'smrBgm167GallonsGrossDA' (as per some biz rules;coz the Delphi appl, processes both EDI/XLS and EDI accepts 229)Hence getting an error while inserting and updating data via the EXCEL sheets ."saying 229 not found in DB".
(Excel sheets it is named as 'smrBgm167GallonsGrossDA' where as in Delphi it is named as '229').
How to tell the Delphi application....
"if it is " smrBgm167GallonsGrossDA" then consider it as "229"?????????????
Not entirely sure what you need, I can't make head nor tail from what you specificly are asking but perhaps this gets you on the right path.
function ExtractNumber(const Value: string): Integer;
begin
if Value = 'smrBgm167GallonsGrossDA' then
Result := 229
else
Result := YourNormalFunctionToExtractTheNumber(Value);
end;
if copy(fieldname, Length(fieldname) - 2, 2) = 'DA' then
begin
delphiField = 229
end
???
You can create a lookup table. Which can be used to lookup the name.
For example:
const
cSize = 2;
cNames : array[0..cSize-1] of string = (
'Name1', 'Name2'
);
CNumbers : array[0..cSize-1] of Integer = (
99, 123
);
function Convert(const AName: string): Integer;
var
i : Integer;
begin
i := 0;
while (i<cSize) do begin
if cNames[i] = AName then begin
Result := cNumbers[i];
Exit;
end;
Inc(i);
end;
Result := NormalConvert(AName);
end;
Note you can also use one array of records:
type
TLookupRec = record
name : string;
number : Integer;
end;
const
cSize = 2;
cLookup : array[0..cSize-1] of TLookupRec = (
( name : 'Name1'; number : 99; ),
( name : 'Name2'; number : 123; )
);
function Convert(const AName: string): Integer;
var
i : Integer;
begin
i := 0;
while (i<cSize) do begin
if cLookUp[i].name = AName then begin
Result := cLookUp[i].number;
Exit;
end;
Inc(i);
end;
Result := NormalConvert(AName);
end;