views:

741

answers:

8

Is there a way in Delphi to get the currect application's exe size in one or two lines of code?

+2  A: 

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:

  1. 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.
  2. 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.
  3. In Delphi.NET you can use the System.IO.FileInfo, like this: FileInfo.Create(filename).Length (one-liner)
  4. 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)

DR
I don't know why this is the top-voted answer. This post contains several examples of how to do this using only native Delphi VCL commands.
JosephStyons
+2  A: 

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í

Neftalí
Try wrapping the first two lines in a "try..finally", and putting the FindClose in the "finally" part. That's more reliable.
onnodb
+6  A: 

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;
skamradt
Nicely done. I think your answer is more readable than that of Mohammed Nasman or myself.
JosephStyons
+2  A: 

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 }
gabr
+3  A: 

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;
The Wicked Flea
Why should that be better than other approaches? There is no shortage of handles in Windows ...
gabr
Simple. The handle is already allocated, point 1, and even better this tells you the size of the application even if you have data appended behind the application's data. This can be extremely important to know!
The Wicked Flea
The second point is valid, I agree.
gabr
+3  A: 

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);

Jozz
A: 

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

Mohammed Nasman
A: 

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.

JosephStyons
Not sure why this justified a negative vote; can someone point to a specific problem with this code?
JosephStyons