Как извлечь "Сводку" свойств файла?

Материал из DRKB

Как извлечь "Сводку" свойств файла?[править | править код]

Ниже показан пример получения набора свойств файла (которые отображаются на вкладке "Сводка" диалогового окна "Свойства:"). В файловой системе NTFS читаются свойства любого файла (если они вообще есть, конечно). В других ФС читаются свойства только compound-файлов (они же structured storage, т.е. файлы хранения структурированной информации, такие как файлы MS Office, пакеты MSI, etc.), т.к. в этом случае вся информация о свойствах находится в самих файлах (опять же, она может там и отсутствовать). Ну и, естественно, не читаются нестандартные свойства, т.к. они нигде не хранятся, а динамически добавляются на вкладку соответствующим файлу COM-сервером - обработчиком вкладок св-в (PropertySheetHandlers).В этом примере считывается несколько св-в из набора SummaryInformation.

unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses ActiveX, ComObj;

function StgOpenStorageEx(const pwcsName: POleStr; grfMode: LongInt;
  stgfmt: DWORD; grfAttrs: DWORD;
  pStgOptions: Pointer; reserved2: Pointer; riid: PGUID;
  out stgOpen: IStorage): HResult; stdcall; external 'ole32.dll';


const
  // GUID'ы для набора св-в SummaryInformation
  FMTID_SummaryInformation: TGUID = '{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';
  IID_IPropertySetStorage: TGUID = '{0000013A-0000-0000-C000-000000000046}';

  STGFMT_FILE = 3;
  STGFMT_ANY = 4;

type
  PPropSpecArray = ^TPropSpecArray;  // массив спецификаций св-в
  TPropSpecArray = array[0..999] of TPropSpec;

  PPropVariantArray = ^TPropVariantArray;
  // массив - приемник, куда будут помещены значения нужных св-в
  TPropVariantArray = array[0..999] of TPropVariant;


// вспомогательные ф-ции  *******

function IsNTFS(AFileName: string): boolean;
var
  fso, drv: olevariant;
begin
  fso := CreateOleObject('Scripting.FileSystemObject');
  drv := fso.GetDrive(fso.GetDriveName(AFileName));
  Result := drv.FileSystem = 'NTFS';
end;

function FileTimeToLocalSystemTime(ft: FILETIME): TSystemTime;
var
  t: FILETIME;
begin
  FileTimeToLocalFileTime(ft, t);
  FileTimeToSystemTime(t, Result);
end;

function SysTimeToStr(ST: TSystemTime): string;
begin
  SetLength(Result, 15);
  GetTimeFormat(LOCALE_USER_DEFAULT, 0, @st, nil, @Result[1], 15);
  SetLength(Result, StrLen(@Result[1]));
end;

function SysDateToStr(ST: TSystemTime): string;
begin
  SetLength(Result, 255);
  GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, @ST, nil, @Result[1], 255);
  SetLength(Result, LStrLen(@Result[1]));
end;

function SysDateTimeToStr(ST: TSystemTime): string;
begin
  Result := SysDateToStr(ST) + ' ' + SysTimeToStr(ST);
end;

// *******

procedure TForm1.Button1Click(Sender: TObject);
var
  stgRoot: IStorage;
  stgPS: IPropertySetStorage;
  stgP: IPropertyStorage;
  ps: PPropSpecArray;
  pv: PPropVariantArray;
  lit: TListItem;  // значения св-в записываем в ListView
begin

  if (not OpenDialog1.Execute) then
    exit;  // выбираем файл

  // Проверяем: если это не compound file и система - не NTFS, тогда выходим.
  // (для compound файлов FS не важна)
  if (StgIsStorageFile(StringToOleStr(OpenDialog1.FileName)) <> S_OK) then
    if (not IsNTFS(OpenDialog1.FileName)) then
    begin
      MessageBox(Handle, 'NTFS needed for non-compound files', 'Error', MB_ICONERROR);
      exit;
    end;


  ListView1.Clear;

  if (StgOpenStorageEx(StringToOleStr(OpenDialog1.FileName), STGM_READ or
    STGM_SHARE_DENY_WRITE, STGFMT_ANY, 0, nil,
    nil, @IID_IPropertySetStorage, stgRoot) <> S_OK) then
    // открываем файл
  begin
    MessageBox(Handle, 'Can not open file', 'Error', MB_ICONERROR);
    exit;
  end;

  stgPS := stgRoot as IPropertySetStorage;
  // ссылаемся на нужный интерфейс
  // открываем набор св-в
  if (stgPS.Open(FMTID_SummaryInformation, STGM_READ or
    STGM_SHARE_EXCLUSIVE, stgP) <> S_OK) then
  begin
    MessageBox(Handle, 'Can not open property set', 'Error', MB_ICONERROR);
    exit;
  end;

  ps := nil;
  pv := nil;
  try
    GetMem(ps, SizeOf(TPropSpec));
    GetMem(pv, SizeOf(TPropVariant));

    ps[0].ulKind := PRSPEC_PROPID;
    // считываем св-ва по их идентификатору (см. ActiveX.pas)

    // Прочитаем несколько св-в файла.
    // В этом примере - для наглядности - читаем по одному св-ву из потока (можно за один раз прочитать и больше),
    // поэтому каждый раз значение будет лежать в pv[0].

    ps[0].propid := PIDSI_TITLE;     // заголовок
    lit := ListView1.Items.Add;
    lit.Caption := 'Title';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then  // читаем св-во
      lit.SubItems.Add(pv[0].pszVal) // добавляем в ListView
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_SUBJECT;   // тема
    lit := ListView1.Items.Add;
    lit.Caption := 'Subject';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(pv[0].pszVal)
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_AUTHOR;   // автор
    lit := ListView1.Items.Add;
    lit.Caption := 'Author';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(pv[0].pszVal)
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_COMMENTS; // комментарий
    lit := ListView1.Items.Add;
    lit.Caption := 'Comment';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(pv[0].pszVal)
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_REVNUMBER; // номер редакции
    lit := ListView1.Items.Add;
    lit.Caption := 'Revision';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(pv[0].pszVal)
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_APPNAME;   // приложение, создавшее пакет
    lit := ListView1.Items.Add;
    lit.Caption := 'Application';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(pv[0].pszVal)
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_CREATE_DTM;   // Дата создания
    lit := ListView1.Items.Add;
    lit.Caption := 'Creation date';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(SysDateTimeToStr(FileTimeToLocalSystemTime(pv[0].filetime)))
    else
      lit.SubItems.Add('');

    ps[0].propid := PIDSI_WORDCOUNT; // кол-во слов в документе
    lit := ListView1.Items.Add;
    lit.Caption := 'Word count';
    if (stgP.ReadMultiple(1, @ps[0], @pv[0]) = S_OK) then
      lit.SubItems.Add(IntToStr(pv[0].lVal))
    else
      lit.SubItems.Add('');

  finally
    if assigned(ps) then
      FreeMem(ps);
    if assigned(pv) then
      FreeMem(pv);
    stgP := nil;
    stgPS := nil;
    stgRoot := nil;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // добавим в список 2 столбца (лучше это делать в дизайнере)
  ListView1.ViewStyle := vsReport;
  ListView1.Columns.Add.Caption := 'Name';
  ListView1.Columns.Add.Caption := 'Value';

  // добавим в диалог открытия несколько типов compound-файлов
  OpenDialog1.Filter := 'All Files (*.*)|*.*|MS Office Documents (*.doc;*.xls;*.dot)' +
    '|*.doc; *.xls; *.dot|MSI Databases (*.msi)|*.msi';
end;

end.


Author: Krid
Source: Взято из http://forum.sources.ru
ID: 03147