How can I save a whole folder with it's files and folders using Synopse Big Table? I need to make a backup of my files without compression. I heard that Synopse Big Table is good for this purpose. But I couldn't find info to accomplish that.
Thanks!
Why didn't you write this question in the library forum?
OK, here is some sample code:
function SaveFolderToBigTableFile(const aFolder, aFile: TFileName): boolean;
var SR: TSearchRec;
BT: TSynBigTableString;
aPath: TFileName;
Path: RawUTF8;
begin
DeleteFile(aFile);
result := true;
BT := TSynBigTableString.Create(aFile);
try
aPath := ExtractFilePath(aFolder);
Path := StringToUTF8(aPath);
if FindFirst(aPath+'*.*',faAnyFile,SR)=0 then
try
repeat
if (SR.Name[1]='.') or (faDirectory and SR.Attr<>0) then
Continue;
if BT.Add(StringFromFile(aPath+SR.Name),StringToUTF8(SR.Name))<>0 then
writeln(SR.Name,' added') else begin
result := false;
writeln(SR.Name,' ERROR');
end;
if BT.CurrentInMemoryDataSize>100000000 then
BT.UpdateToFile;
until FindNext(SR)<>0;
finally
FindClose(SR);
end;
finally
BT.Free;
end;
end;
The trick is to use a TSynBigTableString class using the file name as key.
You can add very fast compression just by using our SynLZ library (much faster than zip, but of course with a bit less compression ratio).
Related
I would like to retrieve the file size of a file copied into the clipboard.
I read the documentation of TClipboard but I did not find a solution.
I see that TClipboard.GetAsHandle could be of some help but I was not able to complete the task.
Just from inspecting the clipboard I could see at least 2 useful formats:
FileName (Ansi) and FileNameW (Unicode) which hold the file name copied to the clipboard.
So basically you could register one of then (or both) with RegisterClipboardFormat and then retrieve the information you need. e.g.
uses Clipbrd;
var
CF_FILE: UINT;
procedure TForm1.FormCreate(Sender: TObject);
begin
CF_FILE := RegisterClipboardFormat('FileName');
end;
function ClipboardGetAsFile: string;
var
Data: THandle;
begin
Clipboard.Open;
Data := GetClipboardData(CF_FILE);
try
if Data <> 0 then
Result := PChar(GlobalLock(Data)) else
Result := '';
finally
if Data <> 0 then GlobalUnlock(Data);
Clipboard.Close;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Clipboard.HasFormat(CF_FILE) then
ShowMessage(ClipboardGetAsFile);
end;
Once you have the file name, just get it's size or other properties you want.
Note: The above was tested in Delphi 7. for Unicode versions of Delphi use the FileNameW format.
An alternative and more practical way (also useful for multiple files copied) is to register and handle the CF_HDROP format.
Here is an example in Delphi: How to paste files from Windows Explorer into your application
Have a set of Word-templates (files *.dot) and a little program, which create new files base on that templates. It's works fine, but the goal is to make all in one exe-file.
I see the solution is to move templates files into program resources. But I don't know, how then I will read them from resources. Tell me, please, how to do this.
Maybe you can advise me another solution.
Now, my code is:
procedure TfmMain.CreateDocument0;
var
TempleateFileName: string;
WordApp, Document: OleVariant;
procedure FillBookmark(BookmarkName, bText: string);
var
Range: OleVariant;
begin
if Document.Bookmarks.Exists(BookmarkName) then
begin
Range := Document.Bookmarks.Item(BookmarkName).Range;
Range.Text := bText;
end;
end;
begin
TempleateFileName := ExtractFilePath(Application.ExeName)+'Templates\0.dot';
try
WordApp := GetActiveOleObject('Word.Application');
except
try
WordApp := CreateOleObject('Word.Application');
except
on E: Exception do
begin
MessageBox(Self.Handle, PChar(E.Message), PChar(fmMain.Caption), MB_OK+MB_ICONERROR);
Exit;
end;
end;
end;
try
Document := WordApp.Documents.Add(TempleateFileName, False);
FillBookmark('ObjectType', edt0ObjectType.Text);
...
WordApp.Visible := True;
WordApp.Activate;
finally
WordApp := Unassigned;
end;
end;
That is, I should change this line:
Document := WordApp.Documents.Add(TempleateFileName, False);
Read not from file, but from program resource.
Word cannot open documents from memory. Not only does it not have such a feature, you must also bear in mind that Word executes in a separate process. It cannot see the memory in your process, even if it were able to open documents from memory.
If you do put the documents into linked resources then you will need to extract them to file before asking Word to open them.
how in Delphi could I open binary file in non-text mode?
Like C function fopen(filename,"rb")
There are a few options.
1. Use a file stream
var
Stream: TFileStream;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Stream.ReadBuffer(Value, SizeOf(Value));//read a 4 byte integer
finally
Stream.Free;
end;
2. Use a reader
You would combine the above approach with a TBinaryReader to make the reading of the values simpler:
var
Stream: TFileStream;
Reader: TBinaryReader;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Reader := TBinaryReader.Create(Stream);
try
Value := Reader.ReadInteger;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
The reader class has lots of functions to read other data types. And you can go in the opposite direction with a binary writer.
3. Old style Pascal I/O
You can declare a variable of type File and use AssignFile, BlockRead, etc. to read from the file. I really don't recommend this approach. Modern code and libraries almost invariably prefer the stream idiom and by doing the same yourself you'll make your code easier to fit with other libraries.
You have different options, two of them are:
Use the old school approach, like the C function you pointed out:
var
F: File;
begin
AssignFile(F, 'c:\some\path\to\file');
ReSet(F);
try
//work with the file
finally
CloseFile(F);
end
end;
Use a more modern approach to create a TFileStream based on the file:
var
F: TFileStream;
begin
F := TFileStream.Create('c:\some\path\to\file', fmOpenRead);
try
//work with the file
finally
F.Free;
end;
I'm trying embed an Excel file into my Delphi 5 application, so I can avoid my users just deleting the file accidentally.
Using the embedded file, I create it on disk with a Save dialog, and then open it with the Excel := CreateOleObject('Excel.Application'); method. I've seen examples on how to load a resource, using THandles, but I don't seem to get it working with Excel.WorkBooks.Open(EmbeddedExcelFile);.
Have you had to do something like this before? How would you do it?
Thanks!
You have to include the file as a resource. Say you have a blah.xls
Create a blah.rc file with the following content
blah RCDATA blah.xls
compile it with the resource compiler into blah.res
embed the RES file within the executable
{$R 'blah.res'}
in your application code, extract the file and run it with this code
procedure ExtractAndRun(resourceID:string; resourceFn:string);
var
resourceStream: TResourceStream;
fileStream: TFileStream;
tempPath: string;
fullFileName: string;
begin
tempPath:=GetTempDir;
FullFilename:=TempPath+'\'+resourceFN;
if not FileExists(FullFilename) then
begin
resourceStream := TResourceStream.Create(hInstance, resourceID, RT_RCDATA);
try
fileStream := TFileStream.Create(FullFilename, fmCreate);
try
fileStream.CopyFrom(resourceStream, 0);
finally
fileStream.Free;
end;
finally
resourceStream.Free;
end;
end;
ShellExecute(0,'open', pchar(FullFilename), nil, nil, SW_SHOWNORMAL);
end;
you'll have to add ShellApi in your uses clause
maybe you'll need this GetTempDir function
function GetTempDir: string;
var
Buffer: array[0..MAX_PATH] of char;
begin
GetTempPath(SizeOf(Buffer) - 1, Buffer);
result := StrPas(Buffer);
end;
invoke the function like this
extractAndRun('blah','blah.xls');
I am pretty sure it will not work. You have to save the file in a temp folder, alter it and and then do whatever you want.
Is there a way in Delphi to get the currect application's exe size in one or two lines of code?
Just for grins...you can also do this with streams Just slightly more than 2 lines of code. Generally the application filename including path is also stored into Paramstr(0).
var
fs : tFilestream;
begin
fs := tFilestream.create(paramstr(0),fmOpenRead or fmShareDenyNone);
try
result := fs.size;
finally
fs.free;
end;
end;
It's not as small as you want, but it needs no handles. I use this in all my "SFX" archivers and programs that must know their size. IIRC it requires the Windows unit.
function GetExeSize: cardinal;
var
p: pchar;
i, NumSections: integer;
const
IMAGE_PE_SIGNATURE = $00004550;
begin
result := 0;
p := pointer(hinstance);
inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
NumSections := PImageFileHeader(p).NumberOfSections;
inc(p,sizeof(TImageFileHeader)+ sizeof(TImageOptionalHeader));
for i := 1 to NumSections do
begin
with PImageSectionHeader(p)^ do
if PointerToRawData+SizeOfRawData > result then
result := PointerToRawData+SizeOfRawData;
inc(p, sizeof(TImageSectionHeader));
end;
end;
For the sake of future compatibility, you should choose an implementation that does not require pointers or Windows API functions when possible. The TFileStream based solution provided by skamradt looks good to me.
But... You shouldn't worry too much whether the routine is 1 or 10 lines of code, because you're going to encapsulate it anyway in a function that takes a filename as a parameter and returns an Int64, and put it in your personal library of reusable code. Then you can call it like so:
GetMyFileSize(Application.ExeName);
You can try this:
if FindFirst(ExpandFileName(Application.exename), faAnyFile, SearchRec) = 0 then
MessageDlg(Format('Tamaño: <%d>',[SearchRec.Size]), mtInformation, [mbOK], 0);
FindClose(SearchRec);
===============
Neftalí
Streams can also be used without a TFileStream variable:
with TFilestream.create(paramstr(0), fmOpenRead or fmShareDenyNone) do
aFileSize := Size;
Free;
end;
Ugly, yes.
I prefer using DSiFileSize from DSiWin32. It uses CreateFile internally:
function DSiFileSize(const fileName: string): int64;
var
fHandle: DWORD;
begin
fHandle := CreateFile(PChar(fileName), 0, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if fHandle = INVALID_HANDLE_VALUE then
Result := -1
else try
Int64Rec(Result).Lo := GetFileSize(fHandle, #Int64Rec(Result).Hi);
finally CloseHandle(fHandle); end;
end; { DSiFileSize }
Unfortunatly it is not possible to do that with only one or two lines of code without using some library.
The easy part is getting the application's exe file. You can find it in Application.ExeName
In general there are several possibilities for retrieving the file size:
Open the file and read the size of the stream. This can be accomplished using the 'old' Delphi functions FileOpen and FileSize, or with TFileStream (use the size property) or with Win32 API functions CreateFile and GetFileSize function. (Platform dependend!) Make sure you open the file with read-only access.
In a pure Win32 envinronment you can use FindFirst to get the file size. You can read it from TSearchRec.FindData.nFileSizeLow. If you want to be prepared for files larger than 2 GB (you should be) you have to use also the nFileSizeHigh part.
In Delphi.NET you can use the System.IO.FileInfo, like this: FileInfo.Create(filename).Length (one-liner)
In Linux you can use the lstat64 function (Unit Libc) and get the size from TStatBuf64.st_size. (two-liner if you don't count the variable declaration)
In the JCL library you can find many useful functions, including a simple function which returns the file size of a given file name. (It uses a method which suits the given platform)
uses IdGlobalProtocols;
var
ExeSize: Int64;
begin
ExeSize := FileSizeByName(ParamStr(0));
// or
ExeSize := FileSizeByName(Application.ExeName);
end;
I would like to modify the code provided by skamradt, to make it two lines of code as you requested ;-)
with tFilestream.create(paramstr(0),fmOpenRead or fmShareDenyNone) do
ShowMessage(IntToStr(size));
but I would prefer to use the code as skamradt wrote, because it's more safe
Shortest I could do. Note that the .Size is in bytes, so for kilobytes, divide by 1024.
procedure TForm1.Button1Click(Sender: TObject);
begin
with TFileStream.Create(Application.ExeName,fmShareDenyNone) do
ShowMessage(FloatToStr(Size/1024));
end;
Check out this link.