1

Тема: FileInfo(получение информации о файлах\папках)

Данный пример показывает получение информации о файлах\папках, например, такой как дата и время создания, размер.
Путь к файлу задаётся в командной строке, например:

FileInfo.kex /sys/example.asm

Если путь не задан, то по умолчанию показывается информация о файле "/sys/kernel.mnt"
misc.php?action=pun_attachment&item=137&download=0
В примере используются элементы управления CheckBox и Frame из библиотеки BoxLib.
Но с CheckBox-ами есть проблемы(в которых удалось разобраться):

  • неверная информация о флагах на SVN, а на Wiki конкретные значения вообще не указаны.

Также может вызвать вопросы добавление цифры 2 в конце имён функций: "check_box_draw2" и "init_checkbox2".

Насколько помню, было время, когда было два варианта CheckBox-ов, но один из них не прижился.
Лучше, конечно, было бы переименовать их сразу в "check_box_draw" и "init_checkbox", чтобы не было путаницы.
Но теперь уже они используются не первый год, остаётся надеяться, что никому не придёт в голову их снова переименовывать — в этом случае пример работать перестанет.

Вот полностью исходный код:

program FileInfo;

uses
  KolibriOS;

const
  WINDOW_BORDER_SIZE = 5;

// Frame flags
  FR_CAPTION = 1;
  FR_DOUBLE  = 0;
  FR_RAISED  = 2;
  FR_SUNKEN  = 4;
  FR_ETCHED  = 6;
  FR_RIDGED  = 8;
  FR_FILLED  = 16;

// Frame text position
  FR_TEXT_POS_BOTTOM = 1;
  FR_TEXT_POS_TOP    = 0;

// CheckBox flags
  CH_FLAG_EN     = 2;  // флаг установленного чек бокса.
  CH_FLAG_TOP    = 8;  // флаг расположения текста вверху т.е. 3-й бит
  CH_FLAG_MIDDLE = 16; // флаг расположения текста в центре т.е. 4-й бит.
  CH_FLAG_BOTTOM = 0;  // флаг расположения текста в низу т.е. по умолчанию

type
  TCheckBox = packed record
    width:          Word;      // ширина прямоугольника
    left:           SmallInt;  // положение по х
    height:         Word;      // высота прямоугольника
    top:            SmallInt;  // положение по у
    ch_text_margin: LongWord;  // расстояние от прямоугольника чекбокса до надписи
    color:          LongWord;  // цвет внутри чекбокса
    border_color:   LongWord;  // цвет рамки
    text_color:     LongWord;  // цвет надписи
    text:           PAnsiChar; // адрес в коде программы, где расположен текст
    flags:          LongWord;  // флаги
    ch_text_length: LongWord;  // длина строки с символами
  end;

  TFrame = packed record
    Style:        LongWord;
    Width:        Word;
    Left:         SmallInt;
    Height:       Word;
    Top:          SmallInt;
    OuterColor:   LongWord;
    InnerColor:   LongWord;
    Flags:        LongWord;
    Text:         PAnsiChar;
    TextPosition: LongWord;
    Font:         LongWord;
    FontHeight:   LongWord;
    ForeColor:    LongWord;
    BackColor:    LongWord;
  end;

  TFileAttr = set of (faReadOnly, faHidden, faSystem, faVolumeLabel, faDirectory, faArchive);

var
  BoxLib: Pointer;
  check_box_draw: procedure(var CheckBox: TCheckBox); stdcall;
  init_checkbox:  procedure(var CheckBox: TCheckBox); stdcall;
  frame_draw:     procedure(var Frame: TFrame); stdcall;

  CheckBox1: TCheckBox = (
    width: 16;
    left: 24;
    height: 16;
    top: 212;
    ch_text_margin: 8;
    color: $00FFFFFF;
    border_color: $0000FF00;
    text: 'Только чтение';
    flags: CH_FLAG_MIDDLE;
    ch_text_length: Length('Только чтение'));

  CheckBox2: TCheckBox = (
    width: 16;
    left: 24;
    height: 16;
    top: 236;
    ch_text_margin: 8;
    color: $00FFFFFF;
    border_color: $0000FF00;
    text: 'Архивный';
    flags: CH_FLAG_MIDDLE);

  CheckBox3: TCheckBox = (
    width: 16;
    left: 24;
    height: 16;
    top: 260;
    ch_text_margin: 8;
    color: $00FFFFFF;
    border_color: $0000FF00;
    text: 'Скрытый';
    flags: CH_FLAG_MIDDLE);

  CheckBox4: TCheckBox = (
    width: 16;
    left: 24;
    height: 16;
    top: 284;
    ch_text_margin: 8;
    color: $00FFFFFF;
    border_color: $0000FF00;
    text: 'Системный';
    flags: CH_FLAG_MIDDLE);

  DateTimeFrame: TFrame = (
    Style: 0;
    Width: 297;
    Left: 8;
    Height: 97;
    Top: 84;
    OuterColor: $00FFFFFF;
    InnerColor: $00808080;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Дата\время:';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
    ForeColor: $00000070;
    BackColor: $00D0D0D0;
  );

  AttrFrame: TFrame = (
    Style: 0;
    Width: 297;
    Left: 8;
    Height: 121;
    Top: 196;
    OuterColor: $00FFFFFF;
    InnerColor: $00808080;
    Flags: FR_ETCHED + FR_CAPTION + FR_FILLED;
    Text: 'Атрибуты:';
    TextPosition: FR_TEXT_POS_TOP;
    Font: 1;
    FontHeight: 16;
    ForeColor: $00000070;
    BackColor: $00D0D0D0;
  );

  WndLeft, WndTop: LongInt;
  WndWidth: LongWord;
  WndHeight: LongWord;

  SC: TStandardColors;

  FilePath: PAnsiChar = '/sys/kernel.mnt'; // по умолчанию, если запущена без параметров
  FileAttributes: TFileAttributes;

  IsError: Boolean = False;

function IntToStr(Number: LongInt; Buffer: PAnsiChar): LongWord;
asm
         push   edi
         push   ebx
         xor    ebx, ebx
         mov    edi, Buffer
         test   eax, eax
         jns    @L1
         mov    ebx, '-'
         neg    eax
@L1:
         push   0
         mov    ecx, 10
@L0:
         xor    edx, edx
         div    ecx
         add    edx, 48
         push   edx
         test   eax, eax
         jnz    @L0
         or     edx, -1
         test   ebx, ebx
         jz     @L2
         mov    al, bl
         stosb
         inc    edx
@L2:
         inc    edx
         pop    eax
         stosb
         test   eax, eax
         jnz    @L2
         mov    eax, edx // return count
         pop    ebx
         pop    edi
end;

procedure FillChar(var Dest; Count: LongInt; Value: AnsiChar);
var
  i: LongInt;
begin
  if Count > 0 then
    for i := 0 to Count - 1 do
      PAnsiChar(@Dest)[i] := Value;
end;

procedure FileSize2ReadableString(FileSize: Int64; Buffer: PAnsiChar); stdcall;
asm
          push   ebx
          push   edi
          mov    ecx, 10
          xor    ebx, ebx
          lea    eax, FileSize
          mov    edx, [eax + 4] // hi part
          mov    eax, [eax + 0] // lo part
  // convert size to readable ;
  @next:
          cmp    eax, 1024
          jnb    @calc
          test   edx, edx
          jz     @done
  @calc:
          add    eax, 512            // to avoid
          adc    edx, 0              // rounding errors
          shrd   eax, edx, cl
          shr    edx, cl
          inc    ebx
          jmp    @next
  @units: // size of units = 4 bytes include end of string
          db     '  b',0 // byte
          db     ' Kb',0 // Kilobyte
          db     ' Mb',0 // Megabyte
          db     ' Gb',0 // Gigabyte
          db     ' Tb',0 // Terabyte
          db     ' Pb',0 // Petabyte
          db     ' Eb',0 // Exabyte
  @done:
  // in eax -- size, in ebx -- index of units ;
          mov    edi, Buffer
          mov    [edi], dword ptr $20202020 // "    " // 4 spaces
          add    edi, 4
  // out units ;
          mov    edx, [ebx * 4 + @units]
          mov    [edi], edx
          dec    edi                 // step back one space
  // convert size to string ;
  @next_divide:
          xor    edx, edx
          div    ecx                 // in ecx still 10
          add    edx, 48             // edx = (eax MOD ecx) + 48
          mov    [edi], dl
          dec    edi
          test   eax, eax
          jne    @next_divide
          pop    edi
          pop    ebx
end;

procedure DrawInfo;
var
  InfoBuffer: array [0..20] of AnsiChar;
  FileAttr: TFileAttr;
  FileName: PAnsiChar;
  i: LongInt;

  procedure FileDateTimeToStr(FileDateTime: TFileDateTime; Buffer: PAnsiChar);
  var
    k: LongInt;
  begin
    FillChar(Buffer^, Length('00.00.0000 00:00:00'), '0');
    k := 0;
    // пояснение по поводу кода ниже "Inc(k, IntToStr(..." — функция IntToStr возвращает длину строки
    with FileDateTime.Date do
    begin
      if Day < 10 then Inc(k);
      Inc(k, IntToStr(Day, PAnsiChar(@Buffer[k])));   Buffer[k] := '.'; Inc(k);
      if Month < 10 then Inc(k);
      Inc(k, IntToStr(Month, PAnsiChar(@Buffer[k]))); Buffer[k] := '.'; Inc(k);
      if Year = 0 then Inc(k, 3); // обычно год — четырёхзначное число, но если даты обнулены, то год будет равен нулю, в этом случае получим "0000"
      Inc(k, IntToStr(Year, PAnsiChar(@Buffer[k])));  Buffer[k] := ' '; Inc(k);
    end;
    with FileDateTime.Time do
    begin
      if Hours < 10 then Inc(k);
      Inc(k, IntToStr(Hours, PAnsiChar(@Buffer[k])));   Buffer[k] := ':'; Inc(k);
      if Minutes < 10 then Inc(k);
      Inc(k, IntToStr(Minutes, PAnsiChar(@Buffer[k]))); Buffer[k] := ':'; Inc(k);
      if Seconds < 10 then Inc(k);
      Inc(k, IntToStr(Seconds, PAnsiChar(@Buffer[k]))); Buffer[k] := #0{конец строки};
    end;
  end;

begin
  InfoBuffer[SizeOf(InfoBuffer) - 1] := #0;

  // если путь не абсолютный
  if FilePath[0] <> '/' then
    FileName := FilePath
  else
  begin
    i := 0;
    while FilePath[i] <> #0 do Inc(i);
    while FilePath[i] <> '/' do Dec(i);
    FileName := PAnsiChar(@FilePath[i + 1]);
  end;
  DrawText(8, 8, 'Имя: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  DrawText(8 + Length('Имя: ') * 8, 8, FileName, SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  DrawText(8, 30, 'Тип: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  if not (faDirectory in TFileAttr(Byte(FileAttributes.Attributes))) then
    DrawText(8 + Length('Тип: ') * 8, 30, 'Файл ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0)
  else
    DrawText(8 + Length('Тип: ') * 8, 30, 'Папка', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  DrawText(8, 52, 'Размер: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  FileSize2ReadableString(FileAttributes.Size, InfoBuffer);
  DrawText(8 + Length('Размер: ') * 8, 52, InfoBuffer, SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  DrawText(24, 100, 'Создания: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  FileDateTimeToStr(FileAttributes.Creation, InfoBuffer);
  DrawText(24 + Length('Создания: ') * 8, 100, InfoBuffer, SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  DrawText(24, 124, 'Изменения: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  FileDateTimeToStr(FileAttributes.Modify, InfoBuffer);
  DrawText(24 + Length('Изменения: ') * 8, 124, InfoBuffer, SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  DrawText(24, 148, 'Доступа: ', SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);
  FileDateTimeToStr(FileAttributes.Access, InfoBuffer);
  DrawText(24 + Length('Доступа: ') * 8, 148, InfoBuffer, SC.WorkText, SC.Work, DT_ZSTRING + DT_CP866_8x16 + DT_FILL_OPAQUE, 0);

  FileAttr := TFileAttr(Byte(FileAttributes.Attributes));

  if faReadOnly in FileAttr then
    CheckBox1.Flags := CheckBox1.Flags or CH_FLAG_EN
  else
    CheckBox1.Flags := CheckBox1.Flags and not CH_FLAG_EN;

  if faArchive in FileAttr then
    CheckBox2.Flags := CheckBox2.Flags or CH_FLAG_EN
  else
    CheckBox2.Flags := CheckBox2.Flags and not CH_FLAG_EN;

  if faHidden in FileAttr then
    CheckBox3.Flags := CheckBox3.Flags or CH_FLAG_EN
  else
    CheckBox3.Flags := CheckBox3.Flags and not CH_FLAG_EN;

  if faSystem in FileAttr then
    CheckBox4.Flags := CheckBox4.Flags or CH_FLAG_EN
  else
    CheckBox4.Flags := CheckBox4.Flags and not CH_FLAG_EN;
end;

procedure On_Redraw;
begin
  BeginDraw;
  GetStandardColors(SC, SizeOf(TStandardColors));
  DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'FileInfo', SC.Work,
    WS_SKINNED_FIXED + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
  CheckBox1.text_color := SC.WorkText + DT_CP866_8x16;
  CheckBox2.text_color := SC.WorkText + DT_CP866_8x16;
  CheckBox3.text_color := SC.WorkText + DT_CP866_8x16;
  CheckBox4.text_color := SC.WorkText + DT_CP866_8x16;
  CheckBox1.border_color := SC.WorkGraph;
  CheckBox2.border_color := SC.WorkGraph;
  CheckBox3.border_color := SC.WorkGraph;
  CheckBox4.border_color := SC.WorkGraph;
  DateTimeFrame.BackColor := SC.Work;
  AttrFrame.BackColor := SC.Work;
  DateTimeFrame.ForeColor := SC.WorkText;
  AttrFrame.ForeColor := SC.WorkText;
  DateTimeFrame.OuterColor := SC.Work3DLight;
  AttrFrame.OuterColor := SC.Work3DLight;
  DateTimeFrame.InnerColor := SC.Work3DDark;
  AttrFrame.InnerColor := SC.Work3DDark;

  frame_draw(DateTimeFrame);
  frame_draw(AttrFrame);

  if not IsError then
    DrawInfo;

  init_checkbox(CheckBox1);
  init_checkbox(CheckBox2);
  init_checkbox(CheckBox3);
  init_checkbox(CheckBox4);

  check_box_draw(CheckBox1);
  check_box_draw(CheckBox2);
  check_box_draw(CheckBox3);
  check_box_draw(CheckBox4);

  EndDraw;
end;

begin
  BoxLib           := LoadLibrary('/sys/lib/box_lib.obj');
  check_box_draw   := GetProcAddress(BoxLib, 'check_box_draw2');
  init_checkbox    := GetProcAddress(BoxLib, 'init_checkbox2');
  frame_draw       := GetProcAddress(BoxLib, 'frame_draw');

  with GetScreenSize do
  begin
    WndWidth := 312 + WINDOW_BORDER_SIZE * 2;
    WndHeight := 325 + WINDOW_BORDER_SIZE + GetSkinHeight;
    WndLeft := (Width - WndWidth) div 2;
    WndTop := (Height - WndHeight) div 2;
  end;

  SetEventMask(EM_REDRAW + EM_BUTTON);

  // если в командной строке есть парметры
  if CmdLine[0] <> #0 then
    FilePath := CmdLine;

  // если информация об атрибутах файла доступна
  if GetFileAttributes(FilePath, FileAttributes) <> 0 then
    IsError := True;

  while True do
    case WaitEvent of
      REDRAW_EVENT:
        On_Redraw;
      BUTTON_EVENT:
        Exit;
    end;
end.

Прикладываю скомпилированный пример FileInfo.kex

Post's attachments

Иконка вложений FileInfo.kex 1.58 Кб, 59 скачиваний с 2022-01-11 

FileInfo.PNG, 7.82 Кб, 323 x 355
FileInfo.PNG 7.82 Кб, 57 скачиваний с 2022-01-11