Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年01月13日] [更新 2016年12月26日] Delphi サンプルプログラム集
   
816_ファイルのプロパティ
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
816_FileProperty.zip [7,545 KB] 2016年12月26日版 (EXE 同梱)



  • 2013年01月30日
  • コードの整備と記事の修正等
  • [03] ツールチップで表示されるテキストを取得するコードを追加
  • [06] TOpenDialog の Filter に docx, docm を追加
  • [07] TOpenDialog の Filter に xlsx, xlsm を追加
  • 2014年03月27日
  • TShell を使用したサンプルで,取得プロパティの項目数を増やした
  • TShell を使用した画像ファイルの画像サイズの取得サンプルを追加
  • dsofile.dll 用のインストーラを添付




ファイルのプロパティ


ファイルのプロパティには,以下のような種類があります.

種類1 NTFS になって導入された構造化ストレージのメインストレージの情報
Office 2007 よりも前のワードやエクセルのフロパティは,このメインストレージに格納されている.この情報は FAT でも取得可能
Office 2007 以降は,構造化ストレージを使用しない XML 形式となった
種類2 構造化ストレージのサブストレージの情報
この情報は揮発性で,ファイルを他の媒体に移動すると失われる
テキストファイル等のプロパティはこれに該当する
種類3 ファイルの種類によって決められた形式で格納されている情報


下図は,エクスプローラのファイル上の右クリックで表示した [プロパティ] ダイアログです.これらはファイルの種類によって異なり,構造化ストレージの情報だけではなく,RIFF 情報も一部表示しています.
本ページのサンプルプログラム類は,これらのプロパティを操作するサンプルです.ただし,RIFF 形式の情報が取得できるのは TShell オブジェクトを使用した場合だけです.


図1
ワード文書のプロパティ例
  • [詳細] タブ
  • 構造化ストレージの値が表示される
図2
[カスタム] プロパティの例
図3
テキストファイルの場合
  • テキストファイル等,サブストレージに格納される情報は,値を設定しても,他の媒体に移動すると消失してしまう
図4
マルチメディア関係のファイル
  • オーディオ,イメージ,ムービー等,マルチメディア関係のファイルでは RIFF 情報の一部が取得できるが,詳細な内容はファイルによって決められた形式を解析する必要がある




01_DSOFile オブジェクトを使用したプロパティの取得


マイクロソフトの Office チームが開発した DSOFile (Microsoft Developer Support OLE File Property Reader) を利用する方法です.プロパティの取得と設 定もできるようになっています. DSOFile では Officeアプリケーションを起動するオーバーヘッドがないため,より速く情報を取得することができます.DSOFile は次の様にインストールして使用します.
  • (1) 下記のマイクロソフトのページから [DsoFileSetup_KB224351_x86.exe パッケージ] をダウンロードする
  • (2) ダウンロードしたファイルを実行してインストールする
    これは,DLL をレジストリに登録する作業
  • (3) Delphi を起動し,IDE から [コンポーネント] [コンポーネントのインポート] から
    [ActiveX の取り込み]で [DSO OLE Document Properties Reader 2.1 (Version 2.1) を選択しして [コンポーネントラッパーを生成]
DSOFile 利用の欠点は,実行環境にも DLL の登録が必要なことです.
また,構造化ストレージの情報でプロパティの操作をするため,Office 2007 以降のワードやエクセルのプロパティは,基本的に操作できません.プロパティを取得しようとすると,下図のようなエラーダイアログが現れます.


図5
エラー

図6
設計時画面
  • TOleDocumentProperties を配置
  • [btnRead] でファイルを選択
  • [btnSave] で現在表示中の値を保存
  • 日時関係は読出し専用
図7
実行中
  • 添付のワードの文書のプロパティ
  • ワードやエクセル以外でも,サブストレージに読書きできるが,対象ファイルを別の媒体に移動すると消失してしまう

リスト1
DSOFile を使用したファイルのプロパティの操作
unit FileProperties_DSOFileUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DSOFile_TLB, ExtCtrls, OleServer;

type
  TForm1 = class(TForm)
    btnRead: TButton;
    btnSave: TButton;
    Panel1: TPanel;
    OpenDialog1: TOpenDialog;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    Panel2: TPanel;
    lblEdit11: TLabeledEdit;
    lblEdit12: TLabeledEdit;
    lblEdit13: TLabeledEdit;
    OleDocumentProperties1: TOleDocumentProperties;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure btnReadClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);

  //プロパティ保存ボタンは使用不可に
  //作成日等はDSOFileでは変更不可なのでDisableに
  btnSave.Enabled := False;
  Panel2.Enabled  := False;
end;

//=============================================================================
//  終了処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //現在のドキュメントオブジェクトを閉じる
  try
    OleDocumentProperties1.Close(False);
  except
    //DSOFileがインストールされていないPCでは実行できない
  end;
end;

//=============================================================================
//  ファイルのプロパティを読出して表示
//
//  DSOFileによるプロパティの読書きは,対象ファイルを他で操作している場合は実行
//  できない(例えば対象ワード文書を開いている時等).ロック違反となる
//
//  DSOFileで読出した日付はワードやエクセルのプロパティ
//  エクスプローラの[プロパティ][概要]タブの原作者のところの日付で,[全般]タブ
//  に表示されている日付ではない
//  Shellオブジェクトによる日付取得の場合は,[全般]タブの日付
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
var
  DateTime11 : TDateTime;
  DateTime12 : TDateTime;
  DateTime13 : TDateTime;
begin
  OpenDialog1.InitialDir := BasePath;
  if OpenDialog1.Execute then begin
    Update;
    //プロパティを保存しないで現在のドキュメントオブジェクトを閉じる
    try
      OleDocumentProperties1.Close(False);
    except
      //DSOFileがインストールされていないPCでは実行できない
      exit;
    end;

    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    lblEdit11.Text := '';
    lblEdit12.Text := '';
    lblEdit13.Text := '';

    OleDocumentProperties1.Open(OpenDialog1.FileName, //フルパス名
                                False,                //書込みを行うのでFalse
                                0);                   //オプション(0はdefault)

    //各コントロール(TLabeledEdit)にプロパティを表示
    Panel1.Caption := ExtractFileName(OpenDialog1.FileName);
    lblEdit01.Text := OleDocumentProperties1.SummaryProperties.Title;
    lblEdit02.Text := OleDocumentProperties1.SummaryProperties.Subject;
    lblEdit03.Text := OleDocumentProperties1.SummaryProperties.Company;
    lblEdit04.Text := OleDocumentProperties1.SummaryProperties.Author;
    lblEdit05.Text := OleDocumentProperties1.SummaryProperties.LastSavedBy;
    lblEdit06.Text := OleDocumentProperties1.SummaryProperties.Comments;

    DateTime11 := OleDocumentProperties1.SummaryProperties.DateCreated;
    DateTime12 := OleDocumentProperties1.SummaryProperties.DateLastSaved;
    DateTime13 := OleDocumentProperties1.SummaryProperties.DateLastPrinted;
    if DateTime11 > 0 then
      lblEdit11.Text := FormatDateTime('yyyy年mm月dd日 hh時mm分', DateTime11);
    if DateTime12 > 0 then
      lblEdit12.Text := FormatDateTime('yyyy年mm月dd日 hh時mm分', DateTime12);
    if DateTime13 > 0 then
      lblEdit13.Text := FormatDateTime('yyyy年mm月dd日 hh時mm分', DateTime13);
     btnSave.Enabled := True;
  end;
end;

//=============================================================================
//  各コントロール(TLabeledEdit)に表示中のプロパティを保存する
//=============================================================================
procedure TForm1.btnSaveClick(Sender: TObject);
begin
  OleDocumentProperties1.SummaryProperties.Title       := lblEdit01.Text;
  OleDocumentProperties1.SummaryProperties.Subject     := lblEdit02.Text;
  OleDocumentProperties1.SummaryProperties.Company     := lblEdit03.Text;
  OleDocumentProperties1.SummaryProperties.Author      := lblEdit04.Text;
  OleDocumentProperties1.SummaryProperties.LastSavedBy := lblEdit05.Text;
  OleDocumentProperties1.SummaryProperties.Comments    := lblEdit06.Text;
  OleDocumentProperties1.Save;
end;

end.




02_DSOFile オブジェクトを使用したスタムプロパティの取得


同様に DSOFile を使用したサンプルですが,カスタムプロパティの操作を追加しています.


図8
設計時画面
  • 背景色が淡い緑の項目がカスタムプロパティ
  • [btnDelete] はカスタムプロパティの削除
図9
実行中
図10
カスタムプロパティを表示
  • カスタムプロパティを作成すると,エクスプローラの [プロパティ] [カスタム] に作成したプロパティが表示される

リスト2
DSOFile オブジェクトを使用してカスタムプロパティを操作
unit FileProperties_DSOFile_CustomUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DSOFile_TLB, ExtCtrls, ComCtrls, OleServer, XPMan;

type
  TForm1 = class(TForm)
    btnRead: TButton;
    btnSave: TButton;
    Panel1: TPanel;
    OpenDialog1: TOpenDialog;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    FileProp: TOleDocumentProperties;
    btnDelete: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);

  btnSave.Enabled   := False;
  btnDelete.Enabled := False;
end;

//=============================================================================
//  終了処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  //現在のドキュメントオブジェクトを閉じる
  try
    FileProp.Close(False);
  except
    //DSOFileがインストールされていないPCでは実行できない
  end;
end;

//=============================================================================
//  ファイルのプロパティを読出して表示
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
var
  HasCustomProp : Boolean;
  APropName     : OleVariant;
  i             : Integer;
  AValue        : OleVariant;
begin
  HasCustomProp := False;

  OpenDialog1.InitialDir := BasePath;
  if OpenDialog1.Execute then begin
    //プロパティを保存しないで現在のドキュメントオブジェクトを閉じる
    try
      FileProp.Close(False);
    except
      //DSOFileがインストールされていないPCでは実行できない
      MessageDlg('DSOFileをインストールしないと利用できません.',
                 mtInformation,[mbOK],0);
      exit;
    end;

    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    Update;
    FileProp.Open(OpenDialog1.FileName,   //対象ファイルのフルパス名
                  False,                  //書込みを行うのでFalse
                  0);                     //オプション(0はdefault)

    //各コントロール(TLabeledEdit)にプロパティを表示
    Panel1.Caption := ExtractFileName(OpenDialog1.FileName);
    lblEdit01.Text := FileProp.SummaryProperties.Title;
    lblEdit02.Text := FileProp.SummaryProperties.Subject;
    lblEdit03.Text := FileProp.SummaryProperties.Company;

    //カスタムプロパティの存在を調査してあれば値を表示
    for i := 0 to FileProp.CustomProperties.Count - 1 do begin
      APropName := FileProp.CustomProperties.Item[i].Name;
      if APropName = '送付先' then begin
        HasCustomProp  := True;
        AValue         := FileProp.CustomProperties.Item[APropName].Get_Value;
        lblEdit04.Text := AValue;
      end else
      if APropName = '送付部数' then begin
        HasCustomProp  := True;
        AValue         := FileProp.CustomProperties.Item[APropName].Get_Value;
        if AValue <> 0 then lblEdit05.Text := AValue;
      end else
      if APropName = '送付日' then begin
        HasCustomProp  := True;
        AValue         := FileProp.CustomProperties.Item[APropName].Get_Value;
        if AValue <> 0 then lblEdit06.Text := AValue;
      end;
    end;

    btnSave.Enabled   := True;
    btnDelete.Enabled := HasCustomProp;
  end;
end;

//=============================================================================
//  各コントロール(TLabeledEdit)に表示中のプロパティを保存する
//
//  カスタムプロパティの設定は以下の様に行う
//  CustomProperties.Item[プロパティ名].Set_Value(値);
//  存在しないプロパティに対して実行するとエラー
//  日付型の値は,日付型の書式にしたがってTLabledEditに入力しておく
//  (デフォルトではyyyy/mm/dd)
//  プロパティの型に使用可能なものは以下の通り
//  ----------------------------
//    データ型             値
//  ----------------------------
//    数値型               1
//    ブール型 (Yes/No)    2
//    日付型               3
//    文字列型             4
//=============================================================================
procedure TForm1.btnSaveClick(Sender: TObject);
var
  HasCustomProp   : Boolean;
  PropertyNumber  : OleVariant;
  PropertyBoolean : OleVariant;
  PropertyDate    : OleVariant;
  PropertyString  : OleVariant;
  AName           : OleVariant;
  ACount          : OleVariant;
  ADate           : OleVariant;
  i               : Integer;
  APropName       : OleVariant;
begin
  //カスタムプロパティの定数値
  PropertyNumber  := 1;
  PropertyBoolean := 2;
  PropertyDate    := 3;
  PropertyString  := 4;

  //それらの値
  AName  := lblEdit04.Text;
  ACount := StrToIntDef(lblEdit05.Text,0);
  try
    ADate  := StrToDate(lblEdit06.Text);
  except
    ADate := 0;
  end;

  FileProp.SummaryProperties.Title       := lblEdit01.Text;
  FileProp.SummaryProperties.Subject     := lblEdit02.Text;
  FileProp.SummaryProperties.Company     := lblEdit03.Text;
  FileProp.SummaryProperties.LastSavedBy := 'Mr.XRAY';

  //APropListのカスタムプロパティがなければ作成
  //作成したら保存しないと有効にならない
  HasCustomProp := False;
  for i := 0 to FileProp.CustomProperties.Count - 1 do begin
    APropName := FileProp.CustomProperties.Item[i].Name;
    if APropName = '送付先' then begin
      HasCustomProp := True;
      break;
    end;
  end;

  //プロパティがない場合
  if HasCustomProp = False then begin
    FileProp.CustomProperties.Add('送付先'  , PropertyString);
    FileProp.CustomProperties.Add('送付部数', PropertyNumber);
    FileProp.CustomProperties.Add('送付日'  , PropertyDate);
    FileProp.Save;
  end;

  //カスタムプロパティの値を設定
  FileProp.CustomProperties.Item['送付先'].  Set_Value(AName);
  FileProp.CustomProperties.Item['送付部数'].Set_Value(ACount);
  FileProp.CustomProperties.Item['送付日'].  Set_Value(ADate);
  FileProp.Save;

  btnDelete.Enabled := HasCustomProp;
end;

//=============================================================================
//  カスタムプロパティを全て削除
//  このコードでは全てのカスタムプロパティを削除してしまうので要注意
//
//  Item番号の代わりにプロパティ名での指定でも可能
//=============================================================================
procedure TForm1.btnDeleteClick(Sender: TObject);
var
  i : Integer;
begin
  for i := FileProp.CustomProperties.Count - 1 downto 0 do begin
    FileProp.CustomProperties.Item[i].Remove;
  end;
  FileProp.Save;
end;

end.




03_TShell オブジェクトを使用したプロパティの取得


Shell オブジェクトをコンポーネントにし,フォーム上に配置して利用しています.プロパティの取得のみ可能です.マウスカーソル下のツールチップに,ファイルの情報が表示されます.これも取得できます.これを TMemo に表示するようにしました.
Office 2007 以降のワードやエクセルのファイルのプロパティは,Shell オブジェクトでは取得できません.

Shell オブジェクトでは,RIFF 形式の情報の一部も取得できます.Shell オブジェクトをコンポーネントとしてインストールする方法は,[タイプライブラリの取り込み] を参考にして下さい.

TShell を使用したオブジェクトで取得するプロパティは,基本的には,取得順番によって種類が決まっていますが,Windows のバージョンあるいはファイルの種類によって異なることがあります.
また,ファイルのプロパティを操作するアプリケーションの中には,この番号を操作してしまうものもあるようです.
プロパティの項目名を確認してから,実際のプロパティを取得する方がいいかも知れません.



図11
設計時画面
図12
実行結果
  • 添付のデジカメの画像ファイルのプロパティ
  • RIFF 情報として,カメラのモデルと写真の撮影日等も取得できる

リスト3
TShell を使用したファイルのプロパティ取得
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Shell1: TShell;
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    ListView1: TListView;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
   BasePath := ExtractFileDir(Application.ExeName);
   BasePath := ExtractFileDir(BasePath);

   ListView1.Column[1].AutoSize := True;
end;

//=============================================================================
//  Folderオブジェクトのプロパティ名とその値をリスト
//  RIFF情報の一部も取得できる
//  プロパティのリストとプロパティ名はPCの環境によって異なる
//
//  Shellオブジェクトで取得するプロパティは取得順番によって種類が決まっている
//  したがって,プロパティの何番目かを指定しても取得できる
//  ただし,ファイルの種類によって,同じ番号でも異なるプロパティのことがある
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  AFolderPath  : String;
  AFilePath    : String;
  SHFolder     : Shell32_TLB.Folder;
  SHFolderItem : Shell32_TLB.FolderItem;
  i            : Integer;
  AValue       : WideString;
  PropNoList   : TStringList;
begin
  OpenDialog1.InitialDir := BasePath;

  if OpenDialog1.Execute then begin
    BasePath := ExtractFilePath(OpenDialog1.FileName);

    ListView1.Items.Clear;
    ListView1.Update;
    ListView1.Items.BeginUpdate;

    PropNoList  := TStringList.Create;
    AFilePath   := OpenDialog1.FileName;
    AFolderPath := ExtractFilePath(AFilePath);
    SHFolder    := Shell1.NameSpace(AFolderPath);

    try
      //SHFolderオブジェクトのプロパティ名リストを取得
      //予め項目数を取得する方法はないようなので,適当な値だけループ
      for i := 0 to 1000 do begin
        AValue := SHFolder.GetDetailsOf(varNull, i);
        if AValue = '' then break;

        ListView1.Items.Add.Caption := FormatFloat('00 ', i) + AValue;
        PropNoList.Add(IntToStr(i));
      end;

      //OpenDialogで選択したファイルのFolderItemオブジェクトを取得
      SHFolderItem := SHFolder.ParseName(ExtractFileName(AFilePath));

      //ツールチップで表示される文字列
      Memo1.Lines.Text := SHFolder.GetDetailsOf(SHFolderItem, -1);

      //そのFolderItemのプロパティ値を順番に取得
      for i := 0 to PropNoList.Count - 1 do begin
        AValue := SHFolder.GetDetailsOf(SHFolderItem,StrToInt(PropNoList[i]));
        ListView1.Items[i].SubItems.Text := AValue;
      end;
    finally
      FreeAndNil(PropNoList);
    end;
    ListView1.Items.EndUpdate;
  end;
end;

end.


下のリストは,上のコードで取得したプロパティの項目名のリストです.
これは筆者の Windows 7 U64(SP1) での結果です.全部で 287 項目あります.このプロパティの項目名と項目の数は,そのファイルがどのフォルダにあるかによっても違ってきます.

TShell オブジェクトによる操作では,これらのプロパティは読み出し専用です.これらのプロパティはワードやエクセル等のアプリケーションが書き込み操作を行っています.Delphi のプロジェクトでも,各種の情報を書き込んで設定する項目があります.
これらの中には個人情報も含まれています.他の方にファイルを配布する時は,これらの個人情報も提供することになります.



 000 名前
 001 サイズ
 002 項目の種類
 003 更新日時
 004 作成日時
 005 アクセス日時
 006 属性
 007 オフラインの状態
 008 オフラインで利用可能
 009 認識された種類
 010 所有者
 011 分類
 012 撮影日時
 013 参加アーティスト
 014 アルバム
 015 
 016 ジャンル
 017 指揮者
 018 タグ
 019 評価
 020 作成者
 021 タイトル
 022 件名
 023 分類項目
 024 コメント
 025 著作権
 026 トラック番号
 027 長さ
 028 ビット レート
 029 保護
 030 カメラのモデル
 031 大きさ
 032 カメラの製造元
 033 会社
 034 ファイルの説明
 035 プログラム名
 036 継続時間
 037 オンライン
 038 再帰
 039 場所
 040 任意出席者アドレス
 041 任意出席者
 042 開催者住所
 043 開催者名
 044 アラーム時刻
 045 必須出席者アドレス
 046 必須出席者
 047 リソース
 048 会議の状態
 049 空き時間情報
 050 合計サイズ
 051 アカウント名
 052 進捗状況
 053 コンピューター
 054 記念日
 055 秘書の名前
 056 秘書の電話
 057 誕生日
 058 住所 (会社)
 059 市 (会社)
 060 国/地域 (会社)
 061 私書箱 (会社)
 062 郵便番号 (会社)
 063 都道府県 (会社)
 064 番地 (会社)
 065 FAX 番号 (会社)
 066 会社のホーム ページ
 067 会社電話
 068 コールバック番号
 069 自動車電話
 070 子供
 071 会社代表電話
 072 部署
 073 電子メール アドレス
 074 電子メール2
 075 電子メール3
 076 電子メールの一覧
 077 電子メール表示名
 078 表題2
 079 
 080 氏名
 081 性別
 082 名2
 083 趣味
 084 住所 (自宅)
 085 市 (自宅)
 086 国/地域 (自宅)
 087 私書箱 (自宅)
 088 郵便番号 (自宅)
 089 都道府県 (自宅)
 090 番地 (自宅)
 091 自宅 FAX
 092 自宅電話
 093 IM アドレス
 094 イニシャル
 095 役職
 096 ラベル
 097 
 098 住所 (郵送先)
 099 ミドル ネーム
 100 携帯電話
 101 ニックネーム
 102 勤務先所在地
 103 住所 (その他)
 104 ほかの市区町村
 105 他の国/地域
 106 他の私書箱
 107 他の郵便番号
 108 他の都道府県
 109 他の番地
 110 ポケットベル
 111 肩書き
 112 市区町村 (郵送先)
 113 国/地域
 114 私書箱 (郵送先)
 115 郵便番号 (郵送先)
 116 都道府県
 117 番地 (郵送先)
 118 プライマリ電子メール
 119 通常の電話
 120 職業
 121 配偶者
 122 サフィックス
 123 TTY/TTD 電話
 124 テレックス
 125 Web ページ
 126 内容の状態
 127 内容の種類
 128 取得日時
 129 アーカイブ日時
 130 完了日
 131 デバイス カテゴリ
 132 接続済み
 133 探索方法
 134 フレンドリ名
 135 ローカル コンピューター
 136 製造元
 137 モデル
 138 ペアリング済み
 139 クラス
 140 状態
 141 クライアント ID
 142 貢献者
 143 コンテンツの作成日時
 144 前回印刷日
 145 前回保存日時
 146 事業部
 147 ドキュメント ID
 148 ページ数
 149 スライド
 150 総編集時間
 151 単語数
 152 期限
 153 終了日
 154 ファイル数
 155 ファイル名
 156 ファイル バージョン
 157 フラグの色
 158 フラグの状態
 159 空き領域
 160 ビットの深さ
 161 水平方向の解像度
 162 
 163 垂直方向の解像度
 164 高さ
 165 重要度
 166 添付
 167 削除
 168 暗号化の状態
 169 フラグの有無
 170 終了済
 171 未完了
 172 開封の状態
 173 共有
 174 製作者
 175 日付時刻
 176 フォルダー名
 177 フォルダーのパス
 178 フォルダー
 179 参加者
 180 パス
 181 場所ごと
 182 種類
 183 連絡先の名
 184 履歴の種類
 185 言語
 186 最終表示日
 187 説明
 188 リンクの状態
 189 リンク先
 190 URL
 191 メディアの作成日時
 192 リリース日
 193 エンコード方式
 194 プロデューサー
 195 発行元
 196 サブタイトル
 197 ユーザー Web URL
 198 作者
 199 添付ファイル
 200 BCC アドレス
 201 BCC
 202 CC アドレス
 203 CC
 204 会話 ID
 205 受信日時
 206 送信日時
 207 送信元アドレス
 208 差出人
 209 添付ファイルの有無
 210 送信者アドレス
 211 送信者名
 212 ストア
 213 送信先アドレス
 214 To do タイトル
 215 宛先
 216 経費情報
 217 アルバムのアーティスト
 218 アルバム ID
 219 ビート数/分
 220 作曲者
 221 イニシャル キー
 222 コンパイルの一部
 223 雰囲気
 224 セットのパート
 225 期間
 226 
 227 保護者による制限
 228 保護者による制限の理由
 229 使用領域
 230 EXIF バージョン
 231 イベント
 232 露出補正
 233 露出プログラム
 234 露出時間
 235 絞り値
 236 フラッシュ モード
 237 焦点距離
 238 35mm 焦点距離
 239 ISO 速度
 240 レンズ メーカー
 241 レンズ モデル
 242 光源
 243 最大絞り
 244 測光モード
 245 向き
 246 人物
 247 プログラムのモード
 248 彩度
 249 対象の距離
 250 ホワイト バランス
 251 優先度
 252 プロジェクト
 253 チャンネル番号
 254 この回のタイトル
 255 字幕
 256 再放映
 257 SAP
 258 放送日
 259 プログラムの説明
 260 記録時間
 261 ステーション コール サイン
 262 局名
 263 概要
 264 抜粋
 265 自動要約
 266 検索のランキング
 267 秘密度
 268 共有ユーザー
 269 共有状態
 270 製品名
 271 製品バージョン
 272 サポートのリンク
 273 ソース
 274 開始日
 275 課金情報
 276 完了
 277 仕事の所有者
 278 総ファイル サイズ
 279 商標
 280 ビデオ圧縮
 281 ディレクター
 282 データ速度
 283 フレーム高
 284 フレーム率
 285 フレーム幅
 286 総ビット レート




04_TShell を使用して画像ファイルの画像サイズを取得


Shell オブジェクトを使用したプロパティ取得には注意することがあります.
画像ファイルの場合,画像のサイズは「大きさ」という項目です.これは ( 375 x 300 ) のような文字列として取得することになります.この文字列から幅と高さの文字列を分解して取得する例です.下のコードは,上のサンプルコードの後に,幅と高さを取得するコードを追加しています.

取得したプロパティの文字列にはメタデータ操作用の制御文字が含まれています.したがって,取得した文字列から数字の部分だけを取り出すだけでは,数値には変換できません.そこで,下のコードでは,それらの制御文字,具体的には,ASCII 文字の 0 から 32 までの文字を削除してから実際の取得操作を行っています.
他のプロパティも,単に表示するだけでなく,取得した値を操作する時は,同様の処理が必要な場合があります.

このサンプルには,実際には,幅と高さの値を整数値に変換するコードはありません.



図13
実行結果の例
  • ( 375 x 300 ) というプロパティの値から,画像の幅と高さの値を分割した結果

リスト4
TShell を使用して, 画像ファイルの画像サイズの幅と高さを取得
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Shell1: TShell;
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    ListView1: TListView;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses StrUtils, Types;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
   BasePath := '../';
   BasePath := ExpandFileName(BasePath);

   ListView1.Column[1].AutoSize := True;
end;

//=============================================================================
//  Folderオブジェクトのプロパティ名とその値をリスト
//  RIFF情報の一部も取得できる
//  プロパティのリストとプロパティ名はPCの環境によって異なる
//
//  Shellオブジェクトで取得するプロパティは取得順番によって種類が決まっている
//  したがって,プロパティの何番目かを指定しても取得できる
//  ただし,ファイルの種類によって,同じ番号でも異なるプロパティのことがある
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  AFolderPath  : String;
  AFilePath    : String;
  SHFolder     : Shell32_TLB.Folder;
  SHFolderItem : Shell32_TLB.FolderItem;
  i            : Integer;
  AValue       : String;
  PropNoList   : TStringList;
  StrArray     : TStringDynArray;
  StrText      : String;
begin
  OpenDialog1.InitialDir := BasePath;

  if OpenDialog1.Execute then begin
    BasePath := ExtractFilePath(OpenDialog1.FileName);

    ListView1.Items.Clear;
    ListView1.Update;
    ListView1.Items.BeginUpdate;

    PropNoList  := TStringList.Create;
    AFilePath   := OpenDialog1.FileName;
    AFolderPath := ExtractFilePath(AFilePath);
    SHFolder    := Shell1.NameSpace(AFolderPath);

    try
      //SHFolderオブジェクトのプロパティ名リストを取得
      //予め項目数を取得する方法はないようなので,適当な値だけループ
      for i := 0 to 1000 do begin
        AValue := SHFolder.GetDetailsOf(varNull, i);
        if AValue = '' then break;

        ListView1.Items.Add.Caption := FormatFloat('00 ', i) + AValue;
        PropNoList.Add(IntToStr(i));
      end;

      //OpenDialogで選択したファイルのFolderItemオブジェクトを取得
      SHFolderItem := SHFolder.ParseName(ExtractFileName(AFilePath));

      //ツールチップで表示される文字列
      Memo1.Lines.Text := SHFolder.GetDetailsOf(SHFolderItem, -1);

      //そのFolderItemのプロパティ値を順番に取得
      for i := 0 to PropNoList.Count - 1 do begin
        AValue := SHFolder.GetDetailsOf(SHFolderItem,StrToInt(PropNoList[i]));
        ListView1.Items[i].SubItems.Text := AValue;
      end;
    finally
      FreeAndNil(PropNoList);
    end;
    ListView1.Items.EndUpdate;

    //---------------------------------------------------------------------
    //  追加部分
    //  画像サイズのプロパティを縦と横に分解して表示
    //  TStringDynArrayの使用には,usseにTypesが必要
    //---------------------------------------------------------------------

    //31は画像ファイルのサイズの文字列の取得(例: 640X480)
    AValue := SHFolder.GetDetailsOf(SHFolderItem, 31);
    //非表示文字を削除
    for i := Length(AValue) downto 1 do begin
      if CharInSet(AValue[i], [Chr(0)..Chr(32)]) then Delete(AValue, i, 1);
    end;
    if Trim(AValue) <> '' then begin
      StrArray := SplitString(AValue, 'x');
      StrText  := Trim(StrArray[0]) + sLineBreak + Trim(StrArray[1]);
      MessageBox(Handle, PChar(StrText), '画像サイズ', MB_OK);
    end;
    //---------------------------------------------------------------------
  end;
end;

end.




05_TShell を使用して画像ファイルの画像の幅と高さを個別に取得


画像の幅と高さは個別に取得することもできます.
それぞれ「幅」「高さ」という項目名のプロパティです.プロパティの値は,[ 375 ピクセル ] のような文字列となっています.次のサンプルでは,数字部分だけを取り出すために,取得したプロパティの文字列から数字だけを取り出し,それ以外の文字を削除する処理をしています.
この処理をしないで,単に,文字列から数字を取得するだけだと,上のサンプルと同じ理由によって,数字を数値に変換できません.


図14
実行結果の例
  • 画像ファイルの画像サイズの幅と高さを個別に取得した結果

リスト5
TShell を使用して, 画像ファイルの画像サイズの幅と高さを個別に取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, OleServer, Shell32_TLB;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Shell1: TShell;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses StrUtils;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
   BasePath := '../';
   BasePath := ExpandFileName(BasePath);
end;

//=============================================================================
//  画像ファイルの画像サイズを取得して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  AFolderPath  : String;
  AFilePath    : String;
  SHFolder     : Shell32_TLB.Folder;
  SHFolderItem : Shell32_TLB.FolderItem;
  i            : Integer;
  XValue       : String;
  YValue       : String;
  StrText      : String;
begin
  OpenDialog1.InitialDir := BasePath;

  if OpenDialog1.Execute then begin
    BasePath := ExtractFilePath(OpenDialog1.FileName);

    AFilePath   := OpenDialog1.FileName;
    AFolderPath := ExtractFilePath(AFilePath);
    SHFolder    := Shell1.NameSpace(AFolderPath);

    //OpenDialogで選択したファイルのFolderItemオブジェクトを取得
    SHFolderItem := SHFolder.ParseName(ExtractFileName(AFilePath));

    //162は画像の幅
    XValue := SHFolder.GetDetailsOf(SHFolderItem, 162);
    //164は画像の高さ
    YValue := SHFolder.GetDetailsOf(SHFolderItem, 164);
    //数字以外を削除
    for i := Length(XValue) downto 1 do begin
      if not CharInSet(XValue[i], ['0'..'9']) then Delete(XValue, i, 1);
    end;
    for i := Length(YValue) downto 1 do begin
      if not CharInSet(YValue[i], ['0'..'9']) then Delete(YValue, i, 1);
    end;

    StrText := Trim(XValue) + sLineBreak + Trim(YValue);
    if Trim(XValue) <> '' then begin
      MessageBox(Handle, PChar(StrText), '画像サイズ', MB_OK);
    end;
  end;
end;

end.




06_画像を読み込んで画像サイズを取得 - TWICImage を使用


画像ファイルの画像のサイズだけに限って言えば,実際に画像を読み込めば取得できます.
そのサンプルです.この方法は簡単ですが,画像を読み込むことによるオーバーヘッドがあります.もちろん,画像を表示するよりかは高速です.
下のコードは,Delphi 2010 で実装された TWICImage を使用しています.TWICImage を使用すると,画像の種類を気にする必要がありません.


リスト6
画像を読み込んで画像サイズを取得 - TWICImage を使用
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
   BasePath := '../';
   BasePath := ExpandFileName(BasePath);
end;

//=============================================================================
//  画像ファイルの画像サイズを取得して表示
//  TWICImageはDelphi 2010以降で使用可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  AFilePath : String;
  WIC       : TWICImage;
  StrText   : String;
begin
  OpenDialog1.InitialDir := BasePath;

  if OpenDialog1.Execute then begin
    BasePath := ExtractFilePath(OpenDialog1.FileName);
    AFilePath   := OpenDialog1.FileName;

    //TWICImageの対象画像ファイルはBitmap/Jpeg/GIF/PNG/Tiff etc.
    WIC := TWICImage.Create;
    try
      WIC.LoadFromFile(AFilePath);
      //画像ファイルがJPEGの時だけ処理
      if WIC.ImageFormat = wifJpeg then begin
        StrText := IntToStr(WIC.Width) + sLineBreak + IntToStr(WIC.Height);
        MessageBox(Handle, PChar(StrText), '画像サイズ', MB_OK);
      end;
    finally
      WIC.Free;
    end;
  end;
end;

end.




07_TShell を使用して構造化ストレージの値を取得


TShell では,Shell オブジェクトが提供するプロパティ以外にも,構造化ストレージのプロパティ値を取得することができます.添付の plFileInfo_Const.pas を使用します.


図15
設計時画面
図16
実行画面

リスト7
TShell を使用して構造化ストレージの値を取得
unit ExtendedProperty_ShellUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, OleServer, Shell32_TLB, XPMan;

type
  TForm1 = class(TForm)
    btnRead: TButton;
    Panel1: TPanel;
    OpenDialog1: TOpenDialog;
    Panel2: TPanel;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    lblEdit11: TLabeledEdit;
    lblEdit12: TLabeledEdit;
    lblEdit13: TLabeledEdit;
    Shell1: TShell;
    procedure FormCreate(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
    function CreatePropStr(GUID: TGUID; ID: Integer): WideString;
    function UTCTimeDateTimeOffset: TDateTime;    
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plFileInfo_Const, ActiveX;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);

  //Shellオブジェクトによるプロパティは読出し専用なのでDisableに
  Panel2.Enabled  := False;
end;

//=============================================================================
//  ファイルのプロパティを読出して表示
//  個々ののプロパティを取得するには,FolderItem2オブジェクトのExtendedProperty
//  メソッドを使用する.Extended(拡張)となっているが,このExtendedの意味は不明
//
//  FMTID_XXXXで取得するプロパティ値は構造化ストレージの値なので,対象ファイル
//  が構造化ストレージをサポートしていて,プロパティが格納されていなければ取得
//  できない.つまりRIFF情報は取得できない.
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
var
  AFolderPath   : String;
  AFileName     : String;
  SHFolderItem2 : Shell32_TLB.FolderItem2;
  PropStr       : WideString;
  AValue        : OleVariant;
  ADate         : TDateTime;
begin
  OpenDialog1.InitialDir := BasePath;
  if OpenDialog1.Execute then begin
    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    lblEdit11.Text := '';
    lblEdit12.Text := '';
    lblEdit13.Text := '';
    Update;

    AFolderPath   := ExtractFilePath(OpenDialog1.FileName);
    AFileName     := ExtractFileName(OpenDialog1.FileName);
    SHFolderItem2 := FolderItem2(Shell1.NameSpace(AFolderPath).
                                   ParseName(AFileName));
    Panel1.Caption := AFileName;

    //タイトル
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_TITLE);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit01.Text := VarToStr(AValue);
    end;

    //サブタイトル
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_SUBJECT);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit02.Text := VarToStr(AValue);
    end;

    //会社名
    PropStr := CreatePropStr(FMTID_DocSummaryInfo, PID_COMPANY);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit03.Text := VarToStr(AValue);
    end;

    //作成者
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_AUTHOR);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit04.Text := VarToStr(AValue);
    end;

    //最終保存者
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_LASTAUTHOR);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit05.Text := VarToStr(AValue);
    end;

    //コメント
    PropStr := CreatePropStr(FMTID_SummaryInfo,PID_COMMENTS);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_BSTR then begin
      lblEdit06.Text := VarToStr(AValue);
    end;

    //作成日
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_CREATE_DTM);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_DATE then begin
      //UTCからローカル時刻に変換
      ADate := AValue + UTCTimeDateTimeOffset;
      lblEdit11.Text := DateTimeToStr(ADate);
    end;

    //最終更新日
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_LASTSAVE_DTM);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_DATE then begin
      //UTCからローカル時刻に変換
      ADate := AValue + UTCTimeDateTimeOffset;
      lblEdit12.Text := DateTimeToStr(ADate);
    end;

    //最終印刷日
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_LASTPRINTED);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_DATE then begin
      //UTCからローカル時刻に変換
      ADate := AValue + UTCTimeDateTimeOffset;
      lblEdit13.Text := DateTimeToStr(ADate);
    end;
  end;
end;

//=============================================================================
//  FolderItem2オブジェクトのExtendedPropertyメソッドの引数を作成.これは
//  プロパティのカテゴリのGUIDの文字列 + プロパティの種類の値
//  という形式となっている(間に半角の空白)
//=============================================================================
function TForm1.CreatePropStr(GUID: TGUID; ID: Integer): WideString;
begin
  Result :=GUIDToString(GUID) + ' ' + IntToStr(ID);
end;

//=============================================================================
//  FolderItme2オブジェクトのExtenedPropertyので取得する日付はUTC(世界協定時刻)
//  このUTCとローカルとの時刻差を求める関数
//
//  GetTimeZoneInformationでシステムから現在のタイムゾーンパラメータを取得
//  Bias値は世界協定時刻(UTC)時間とローカル時間との分差,つまり分なので,これを
//  TDateTime型に変換して返す
//  日本の場合は常に9:00:00
//=============================================================================
function TForm1.UTCTimeDateTimeOffset: TDateTime;
var
  TZ   : TTimeZoneInformation;
  Hour : Integer;
  Min  : Integer;
begin
  GetTimeZoneInformation(TZ);
  Hour   := ABS(TZ.Bias) div 60;
  Min    := ABS(TZ.Bias) mod 60;
  Result := EncodeTime(Hour, Min, 0, 0);
end;

end.


Delphi XE では,TTimeZone という抽象クラスが実装されています.
これを使用すると,以下のように,TTimeZone.Local.ToLocalTime を使用して,UTC 相当の値からローカルの時刻が取得できます.TTimeZone の使用には,uses に DateUtils が必要です.



    //最終更新日
    PropStr := CreatePropStr(FMTID_SummaryInfo, PID_LASTSAVE_DTM);
    AValue  := SHFolderItem2.ExtendedProperty(PropStr);
    if TVariantArg(AValue).vt = VT_DATE then begin
      //UTCからローカル時刻に変換
      ADate := TTimeZone.Local.ToLocalTime(AValue);
      lblEdit12.Text := DateTimeToStr(ADate);
    end;




08_構造化ストレージの操作


構造化ストレージを操作する例です.添付の plFileInfo_Const.pas を使用します.カスタムプロパティ (ユーザ定義のプロパティ) を利用するには,コードの追加あるいは修正が必要です.

Office 2007 以降のワードやエクセルは,構造化ストレージを使用しないファイル形式,XML 形式に変更となりました.したがって,本サンプルでは操作できません.




図17
設計時画面
図18
実行画面

リスト8
構造化ストレージの値を操作
unit FileProperties_StorageUnit;

interface

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

type
  TForm1 = class(TForm)
    Bevel1: TBevel;
    OpenDialog1: TOpenDialog;
    btnSave: TButton;
    btnRead: TButton;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    lblEdit11: TLabeledEdit;
    lblEdit12: TLabeledEdit;
    lblEdit13: TLabeledEdit;
    Panel1: TPanel;
    procedure btnReadClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
    AFileName: string;
    function GetStorage(const FileName: WideString;
        GUID_Type: TGUID; PID_Info: cardinal): string;
    function SetStorage(const FileName: WideString; GUID_Type: TGUID;
        PID_Info: cardinal; InfoStr: string): boolean;
    function SetStorageDate(const FileName: WideString; GUID_Type: TGUID;
        PID_Info: cardinal; ADateTime: TDateTime): boolean;
    function PropertyVariantToString(const Prop: TPropVariant): String;
    function ToDateTime(ATime: TFileTime): TDateTime;
    function ToFileTime(ATime: TDateTime): TFileTime;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plFileInfo_Const;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);
end;

//=============================================================================
//  プロパティ読出しボタン
//
//  FMTID_XXXXで取得するプロパティ値は構造化ストレージの値なので,対象ファイル
//  が構造化ストレージをサポートしてなければ取得できない.
//  ただし,サブストリーム(揮発性情報)に書込まれた内容は表示可能
//
//  MP3のID3タグ,デジカメのフォーマット形式であるExif,またマイクロソフトが定
//  めたRIFF形式(AVI,WAV,MID,ビットマップのDIB等)は独自の形式でプロパティ値を格
//  納しているため取得できない.
//  エクスプローラの[プロパティ][概要]タブにはこれらの値の一部が表示されるよう
//  になっている.またShellオブジェクトでもそれらの一部を取得できる
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
begin
  OpenDialog1.InitialDir := BasePath;
  if OpenDialog1.Execute then begin
    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    lblEdit11.Text := '';
    lblEdit12.Text := '';
    lblEdit13.Text := '';
    Update;

    AFileName := OpenDialog1.FileName;
    Panel1.Caption := ExtractFileName(AFileName);

    lblEdit01.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_TITLE);
    lblEdit02.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_SUBJECT);
    lblEdit03.Text:=GetStorage(AFileName, FMTID_DocSummaryInfo, PID_COMPANY);
    lblEdit04.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_AUTHOR);
    lblEdit05.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_LASTAUTHOR);
    lblEdit06.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_COMMENTS);
    lblEdit11.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_CREATE_DTM);
    lblEdit12.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_LASTSAVE_DTM);
    lblEdit13.Text:=GetStorage(AFileName, FMTID_SummaryInfo,    PID_LASTPRINTED);
  end;
end;

//=============================================================================
//  プロパティ書込みボタン
//  文字列型と日付型で処理分岐(TDateTimePickerを使用した方がいいかも)
//  各テキストの日付書式を変更しないこと
//
//  FMTID_XXXXで操作するプロパティ値は構造化ストレージの値なので,対象ファイル
//  が構造化ストレージをサポートしてなければ操作できない.
//  ただし,サブストリーム(揮発性情報)には書込みができる.たとえばテキストファ
//  イルにもこのbtnSaveClick内で指定する値を書込めるが,FindFirst等で取得する
//  値とは全く別の(構造化ストレージの性質)情報となる.またこの揮発性の情報は,
//  対象ファイルを他の記憶媒体に移動すると消えてしまう.
//=============================================================================
procedure TForm1.btnSaveClick(Sender: TObject);
var
  ADate1 : TDateTime;
  ADate2 : TDateTime;
  ADate3 : TDateTime;
begin
  if FileExists(AFileName) then begin
    //文字列関係
    SetStorage(AFileName, FMTID_SummaryInfo,    PID_TITLE,      lblEdit01.Text);
    SetStorage(AFileName, FMTID_SummaryInfo,    PID_SUBJECT,    lblEdit02.Text);
    SetStorage(AFileName, FMTID_DocSummaryInfo, PID_COMPANY,    lblEdit03.Text);
    SetStorage(AFileName, FMTID_SummaryInfo,    PID_AUTHOR,     lblEdit04.Text);
    SetStorage(AFileName, FMTID_SummaryInfo,    PID_LASTAUTHOR, lblEdit05.Text);
    SetStorage(AFileName, FMTID_SummaryInfo,    PID_COMMENTS,   lblEdit06.Text);

    //日付型
    try
      //作成日
      ADate1 := StrToDateTime(lblEdit11.Text);
      SetStorageDate(AFileName, FMTID_SummaryInfo, PID_CREATE_DTM,  ADate1);
      //最終更新日
      ADate2 := StrToDateTime(lblEdit12.Text);
      SetStorageDate(AFileName, FMTID_SummaryInfo, PID_LASTSAVE_DTM,ADate2);
      //最終印刷日
      ADate3 := StrToDateTime(lblEdit13.Text);
      SetStorageDate(AFileName, FMTID_SummaryInfo, PID_LASTPRINTED, ADate3);
    except
    end;
  end;
end;

//=============================================================================
//  プロパティセットを読出す実際の関数
//
//  同じGUID_Typeのプロパティであれば,Enumメソッドを使用して列挙可能であるが,
//  ここでは使用していない.
//  一つのファイルに対してプロパティを列挙するのではなく,同じプロパティ値のフ
//  ァイルを選択するなどの目的を想定しているので.
//
//  IPropertySetStorage.Open
//  第1引数 開くプロパティセットの(通常FMTIDの識別子を使用)
//  第2引数 開くプロパティセットのモード
//  第3引数 IPropertyStorageインタフェースの受取り値へのポインタ
//
//  IPropertyStorage.ReadMultiple
//  第1引数 読取るプロパティの数
//  第2引数 読取るプロパティの配列
//  第3引数 読取ったプロパティ値が格納されているPROPVARIANT値の配列
//          (プロパティの型,実際の値)
//=============================================================================
function TForm1.GetStorage(const FileName: WideString;
  GUID_Type: TGUID; PID_Info: cardinal): string;
var
  hr          : HRESULT;
  Stg         : IStorage;
  PropSetStg  : IPropertySetStorage;
  PropStg     : IPropertyStorage;
  PropSpec    : TPropSpec;
  PropVariant : TPropVariant;
begin
  Result := '';

  //既存のストレージオブジェクトを開く
  hr := StgOpenStorageEx(pwidechar(FileName),
                                   STGM_READ or STGM_SHARE_DENY_WRITE,STGFMT_ANY,
                                   0,
                                   nil,
                                   nil,
                                   @IID_IPropertySetStorage,
                                   Stg);

  //GUID_Typeで指定するプロパティセットを読出しモードで開く
  //条件文は,if hr <> S_FALSEても可.Succeeded(hr)やnot Failed(hr)でもよい
  if (hr = S_OK) then begin
    PropSetStg := Stg as IPropertySetStorage;
    hr := PropSetStg.Open(GUID_Type,
                          STGM_READ or STGM_SHARE_EXCLUSIVE,
                          PropStg);
    if (hr = S_OK) then begin
      PropSpec.ulKind := PRSPEC_PROPID;
      PropSpec.propid := PID_Info;
      if (PropStg.ReadMultiple(1, @PropSpec, @PropVariant) = S_OK) then begin
        if (PropVariant.pszVal <> nil) then begin
          Result := PropertyVariantToString(PropVariant);
        end;
      end;
    end;
  end;
end;

//=============================================================================
//  プロパティセットを書込む実際の関数(文字列型専用)
//  単一プロパティの値のみを書込む仕様としている.理由はGetStorageと同じ
//
//  IPropertySetStorage.Open
//  第1引数 開くプロパティセットの(通常FMTIDの識別子を使用)
//  第2引数 開くプロパティセットのモード
//  第3引数 IPropertyStorageインタフェースの受取り値へのポインタ
//
//  IPropertyStorage.WriteMultiple
//  第1引数 設定するプロパティの数
//  第2引数 プロパティ識別子(PIDXXX)
//  第3引数 PROPVARIANT値の配列(プロパティの型,実際の値)
//  第4引数 割り当てられているプロパティ識別子の最小値 
//=============================================================================
function TForm1.SetStorage(const FileName: WideString;
  GUID_Type: TGUID; PID_Info: Cardinal; InfoStr: string): boolean;
var
  hr          : HRESULT;
  Stg         : IStorage;
  PropSetStg  : IPropertySetStorage;
  PropStg     : IPropertyStorage;
  PropSpec    : TPropSpec;
  PropVariant : TPropVariant;
begin
  Result := False;

  //既存のストレージオブジェクトを開く
  hr := StgOpenStorageEx(pwidechar(FileName),
                         STGM_SHARE_EXCLUSIVE or STGM_READWRITE,
                         STGFMT_ANY,
                         0,
                         nil,
                         nil,
                         @IID_IPropertySetStorage,
                         Stg);

  //GUID_Typeで指定するプロパティセットを読書き両用モードで開く
  if (hr = S_OK) then begin
    PropSetStg := Stg as IPropertySetStorage;
    hr  := PropSetStg.Open(GUID_Type,
                           STGM_READWRITE or STGM_SHARE_EXCLUSIVE,
                           PropStg);

    //新しいプロパティセットの作成
    //[詳細]タブに項目があってもストレージがあるとは限らないので作成
    if (hr <> S_OK) then begin
      hr := PropSetStg.Create(
         GUID_Type,GUID_Type,
         PROPSETFLAG_DEFAULT,STGM_CREATE or STGM_READWRITE or STGM_SHARE_EXCLUSIVE,
         PropStg);
    end;

    //新しいプロパティ値を書込む
    if (hr = S_OK) then begin
      PropSpec.ulKind    := PRSPEC_PROPID;
      PropSpec.propid    := PID_Info;
      PropVariant.vt     := VT_LPSTR;
      PropVariant.pszVal := PAnsiChar(AnsiString(InfoStr));
      if (PropStg.WriteMultiple(1,@PropSpec, @PropVariant, 2) = S_OK) then begin
        Result := (PropStg.Commit(STGC_DEFAULT) = S_OK);
      end;
    end;
  end;
end;

//=============================================================================
//  プロパティセットを書込む実際の関数(日付型専用)
//
//  上の関数との違いは,最後の引数がTDateTime型なので(1)(2)の部分を変更
//  必要であれば,SetFtorage関数の引数を変更して汎用的にしてもいいかも知れない
//=============================================================================
function TForm1.SetStorageDate(const FileName: WideString;
  GUID_Type: TGUID; PID_Info: cardinal; ADateTime: TDateTime): boolean;
var
  hr          : HRESULT;
  Stg         : IStorage;
  PropSetStg  : IPropertySetStorage;
  PropStg     : IPropertyStorage;
  PropSpec    : TPropSpec;
  PropVariant : TPropVariant;
begin
  Result := False;

  //既存のストレージオブジェクトを開く
  hr := StgOpenStorageEx(pwidechar(FileName),
                         STGM_SHARE_EXCLUSIVE or STGM_READWRITE,
                         STGFMT_ANY,
                         0,
                         nil,
                         nil,
                         @IID_IPropertySetStorage,
                         Stg);

  //GUID_Typeで指定するプロパティセットを読書き両用モードで開く
  if (hr = S_OK) then begin
    PropSetStg := Stg as IPropertySetStorage;
    hr  := PropSetStg.Open(GUID_Type,
                           STGM_READWRITE or STGM_SHARE_EXCLUSIVE,
                           PropStg);

    //新しいプロパティセットの作成
    //[詳細]タブに項目があってもストレージがあるとは限らないので作成
    if (hr <> S_OK) then begin
      hr := PropSetStg.Create(
        GUID_Type,
        GUID_Type,
        PROPSETFLAG_DEFAULT,STGM_CREATE or STGM_READWRITE or STGM_SHARE_EXCLUSIVE,
        PropStg);
   end;

   //新しいプロパティ値を書込む
   if (hr = S_OK) then begin
      PropSpec.ulKind      := PRSPEC_PROPID;
      PropSpec.propid      := PID_Info;
      PropVariant.vt       := VT_FILETIME;            //(1)
      PropVariant.filetime := ToFileTime(ADateTime);  //(2)
      if (PropStg.WriteMultiple(1, @PropSpec, @PropVariant,2) = S_OK) then begin
        Result := (PropStg.Commit(STGC_DEFAULT) = S_OK);
      end;
    end;
  end;
end;

//=============================================================================
//  プロパティ値をその値に応じて文字列に変換
//  プロパティ書込みの際は,この逆の変換が必要であるが,このサンプルプログラム
//  では文字列と日付のみを扱っているので省略し,文字列型と日付型の設定を別関数
//  とした.
//=============================================================================
function TForm1.PropertyVariantToString(const Prop: TPropVariant): String;
begin
  case Prop.vt of
    VT_EMPTY    : Result := '';
    VT_NULL     : Result := '';
    VT_VOID     : Result := '';
    VT_I2       : Result := IntToStr(Prop.iVal);
    VT_I4       : Result := IntToStr(Prop.lVal);
    VT_INT      : Result := IntToStr(Prop.lVal);
    VT_R4       : Result := FloatToStr(Prop.fltVal);
    VT_R8       : Result := FloatToStr(Prop.dblVal);
    VT_BSTR     : Result := Prop.bstrVal;
    VT_UI1      : Result := IntToStr(Prop.bVal);
    VT_UI2      : Result := IntToStr(Prop.uiVal);
    VT_UI4      : Result := IntToStr(Prop.ulVal);
    VT_UINT     : Result := IntToStr(Prop.ulVal);
    VT_I8       : Result := IntToStr(Int64(Prop.hVal));
    VT_UI8      : Result := IntToStr(Int64(Prop.uhVal));
    VT_LPSTR    : Result := String(Prop.pszVal);
    VT_LPWSTR   : Result := Prop.pwszVal;
    VT_ERROR    : Result := 'ERROR : ' + IntToStr(Prop.sCode);
    VT_BOOL     : if Prop.boolVal=False then Result := '0' else Result := '1';
    VT_CLSID    : if Prop.puuid<>nil then Result := GuidToString(Prop.puuid^);
    VT_FILETIME : Result := DateTimeToStr(ToDateTime(Prop.filetime));
  else
    Result := '';
  end;
end;

//=============================================================================
//  TFileTime型の値をDelphiのTDateTime型に変換する関数
//=============================================================================
function TForm1.ToDateTime(ATime: TFileTime): TDateTime;
var
  SystemTime : TSystemTime;
  TempTime   : TFileTime;
begin
  try
    //FileTime形式(世界協定時刻)を地域にあわせる
    FileTimeToLocalFileTime(ATime, TempTime);
    //FileTime形式をSystemTime形式にする
    FileTimeToSystemTime(TempTime, SystemTime);
    //SystemTime形式をDelphiのTDateTime形式に変換
    Result:=SystemTimeToDateTime(SystemTime);
  except
    Result := 0;
  end;
end;

//=============================================================================
//  DelphiのTDateTime型の値をTFileTime型に変換する関数
//=============================================================================
function TForm1.ToFileTime(ATime: TDateTime): TFileTime;
var
  SystemTime    : TSystemTime;
  LocalFileTime : TFileTime;
begin
  try
    //TFileTimeをSystemTimeに変換
    DateTimeToSystemTime(ATime, SystemTime);
    //SystemTimeをローカルのFileTimeに変換
    SystemTimeToFileTime(SystemTime, LocalFileTime);
    //最後にローカルの日時をGMT(グリニッジ標準時・世界協定時刻)に変換
    LocalFileTimeToFileTime(LocalFileTime, Result);
  except
    Result.dwLowDateTime  := 0;
    Result.dwHighDateTime := 0;
  end;
end;

end.




09_ワードのオブジェクトを使用してワード文書のプロパティを取得


ワードのオブジェクトを生成して,それを操作してプロパティを取得設定する例です.
右の ListView には,ワードで取得可能なプロパティを一覧表示します.送付先はカスタムプロパティです.[btnDelete] はカスタムプロパティの定義そのものを削除しますが,実際に削除できません.詳細は不明です.

ワードのオブジェクトを使用しますので,Word 2007 以上の docx, docm 形式の文書のプロパティも操作できます.ただし,コンピュータに Word 2007 以上がインストールされていないと,プロパティの取得も操作もできません.



  • 図19
  • 設計時画面
  • [送付先] はユーザ定義のカスタムプロパティ

  • 図20
  • 実行画面

リスト9
ワードのオブジェクトを使用したプロパティの取得
unit FileProperties_WordObjectUnit;

interface

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

type
  TForm1 = class(TForm)
    Bevel1: TBevel;
    btnSave: TButton;
    btnRead: TButton;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    lblEdit07: TLabeledEdit;
    Panel1: TPanel;
    Panel2: TPanel;
    OpenDialog1: TOpenDialog;
    ListView1: TListView;
    lblEdit11: TLabeledEdit;
    lblEdit12: TLabeledEdit;
    lblEdit13: TLabeledEdit;
    btnDelete: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj, Word2000, Office2000;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);

  btnSave.Enabled   := False;
  btnDelete.Enabled := False;
  Panel2.Enabled    := False;
end;

//=============================================================================
//  プロパティ読出しボタン
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
var
  WordApp      : OleVariant;
  WordDoc      : OleVariant;
  i            : Integer;
  Properties   : OleVariant;
  DocuProperty : OleVariant;
  Value        : OleVariant;
  AItem        : TListItem;
begin
  OpenDialog1.InitialDir := BasePath;
  OpenDialog1.Filter := 'Word Document|*.doc;*.docx;*.docm';

  if OpenDialog1.Execute then begin
    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    lblEdit07.Text := '';
    lblEdit11.Text := '';
    lblEdit12.Text := '';
    lblEdit13.Text := '';
    Update;
    ListView1.Items.Clear;
    ListView1.Update;
    ListView1.Items.BeginUpdate;

    WordApp := CreateOleObject('Word.Application');
    WordApp.Documents.Open(OpenDialog1.FileName);
    try
      //ワードに組込み済みのプロパティ(BuiltInDocumentPropertie)のリスト作成
      Properties := WordApp.ActiveDocument.BuiltInDocumentProperties;

      for i := 1 to Properties.Count do begin
        DocuProperty := Properties.Item[i];
        try
          Value := DocuProperty.Value;
        except
          Value := '';
        end;
        AItem := ListView1.Items.Add;
        AItem.Caption := DocuProperty.Name;
        AItem.SubItems.Add(Value);
      end;

      //ユーザ定義のプロパティ(CustomDocumentPropertie)があればリストに追加
      Properties := WordApp.ActiveDocument.CustomDocumentProperties;
      for i := 1 to Properties.Count do begin
        DocuProperty := Properties.Item[i];
        try
          Value := DocuProperty.Value;
        except
          Value := '';
        end;
        AItem := ListView1.Items.Add;
        AItem.Caption := DocuProperty.Name;
        AItem.SubItems.Add(Value);
      end;
      ListView1.Items.EndUpdate;

      //左側の項目別プロパティの取得
      Panel1.Caption := OpenDialog1.FileName;
      WordDoc := WordApp.ActiveDocument;

      //タイトル
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyTitle];
      lblEdit01.Text := Value;
      //サブタイトル
      Value := WordDoc.BuiltInDocumentProperties[wdPropertySubject];
      lblEdit02.Text := Value;
      //会社名
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyCompany];
      lblEdit03.Text := Value;
      //作成者
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyAuthor];
      lblEdit04.Text := Value;
      //最終保存者
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyLastAuthor];
      lblEdit05.Text := Value;
      //コメント
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyComments];
      lblEdit06.Text := Value;

      //作成日
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyTimeCreated];
      lblEdit11.Text := Value;

      //最終更新日
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyTimeLastSaved];
      lblEdit12.Text := Value;

      //最終印刷日付
      Value := WordDoc.BuiltInDocumentProperties[wdPropertyTimeLastPrinted];
      try
        lblEdit13.Text := Value;
      except
      end;

      //ユーザ定義プロパティ
      try
        Value := WordDoc.CustomDocumentProperties['送付先'].Value;
        lblEdit07.Text := Value;
      except
      end;

      btnSave.Enabled   := True;
      btnDelete.Enabled := True;
    finally
      //保存なしでワード終了
      WordApp.Quit(wdDoNotSaveChanges);
      WordDoc := Unassigned;
      WordApp := Unassigned;
    end;
  end;
end;

//=============================================================================
//  プロパティ値の書込み
//
//  フォームの左側に表示しているプロパティを定義して値をセット
//  ユーザ定義プロパティで使用可能なプロパティの種類は以下の通り
//  (Office2000.pas内での定義)
//  msoPropertyTypeBoolean   論理型
//  msoPropertyTypeDate      日付型
//  msoPropertyTypeFloat     実数型
//  msoPropertyTypeNumber    整数型
//  msoPropertyTypeString    文字列型
//=============================================================================
procedure TForm1.btnSaveClick(Sender: TObject);
var
  WordApp : OleVariant;
  WordDoc : OleVariant;
  Value   : OleVariant;
begin
  WordApp := CreateOleObject('Word.Application');
  WordApp.Documents.Open(OpenDialog1.FileName);
  try
    WordDoc := WordApp.ActiveDocument;
    //タイトル
    Value := lblEdit01.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertyTitle].Value := Value;
    //サブタイトル
    Value := lblEdit02.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertySubject].Value := Value;
    //会社名
    Value := lblEdit03.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertyCompany].Value := Value;
    //作成者
    Value := lblEdit04.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertyAuthor].Value := Value;
    //最終保存者
    //実際には編集内容は変更されない.最終保存者はWordApp.Quitの時にワードが
    //が判断して更新する
    Value := lblEdit05.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertyLastAuthor].Value := Value;
    //コメント
    Value := lblEdit06.Text;
    WordDoc.BuiltInDocumentProperties[wdPropertyComments].Value := Value;

    //プロパティを追加
    //値を設定しようとして例外発生ならプロパティなしと勝手に判断
    try
      WordDoc.CustomDocumentProperties['送付先'].Value := lblEdit07.Text;
    except
      WordDoc.CustomDocumentProperties.Add('送付先',
                                            False,
                                            msoPropertyTypeString,
                                            lblEdit07.Text,
                                            EmptyParam);
    end;
  finally
    //ワード文書を保存して終了
    WordApp.Quit(wdSaveChanges);
    WordDoc := Unassigned;
    WordApp := Unassigned;
  end;
end;

//=============================================================================
//  ユーザ定義プロパティの削除
//  実際には削除できない(参考コード)
//
//  VBAのヘルプではDeleteメソッドなのであるが,何故かこのコードでは削除されない
//=============================================================================
procedure TForm1.btnDeleteClick(Sender: TObject);
var
  WordApp : OleVariant;
  WordDoc : OleVariant;
begin
  WordApp := CreateOleObject('Word.Application');
  WordApp.Documents.Open(OpenDialog1.FileName);
  try
    WordDoc := WordApp.ActiveDocument;
    try
      WordDoc.CustomDocumentProperties['送付先'].Delete;
    except
      //該当プロパティが存在しない場合は例外発生
    end;
  finally
    //ワード文書を保存して終了
    WordApp.Quit(wdSaveChanges);
    WordDoc := Unassigned;
    WordApp := Unassigned;
  end;
end;

end.




10_エクセルのオブジェクトを使用してブックのプロパティを取得


エクセルのオブジェクトを生成して,それを操作してプロパティを取得設定するサンプルです.
右の ListView には,取得可能なプロパティを一覧表示します.送付先はカスタムプロパティです. [btnDelete] はカスタムプロパティの定義そのものを削除します.

エクセルのオブジェクトを使用しますので,Excel 2007 以上の xlsx, xlsm 形式のブックのプロパティも操作できます.ただし,コンピュータに Excel 2007 以上がインストールされていないと,プロパティの取得も操作もできません.



  • 図21
  • 設計時画面
  • [送付先] はユーザ定義のカスタムプロパティ

  • 図22
  • 実行画面例

リスト10
エクセルのオブジェクトを使用したプロパティの取得
unit FileProperties_ExcelObjectUnit;

interface

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

type
  TForm1 = class(TForm)
    Bevel1: TBevel;
    btnSave: TButton;
    btnRead: TButton;
    lblEdit01: TLabeledEdit;
    lblEdit02: TLabeledEdit;
    lblEdit03: TLabeledEdit;
    lblEdit04: TLabeledEdit;
    lblEdit05: TLabeledEdit;
    lblEdit06: TLabeledEdit;
    lblEdit07: TLabeledEdit;
    Panel1: TPanel;
    Panel2: TPanel;
    OpenDialog1: TOpenDialog;
    ListView1: TListView;
    lblEdit11: TLabeledEdit;
    lblEdit12: TLabeledEdit;
    lblEdit13: TLabeledEdit;
    btnDelete: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
  private
    { Private 宣言 }
    BasePath : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj, Office2000;

{$R *.dfm}

//=============================================================================
//  初期設定
//  選択フォルダの初期値を設定
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  BasePath := ExtractFileDir(Application.ExeName);
  BasePath := ExtractFileDir(BasePath);

  btnSave.Enabled   := False;
  btnDelete.Enabled := False;
  Panel2.Enabled    := False;
end;

//=============================================================================
//  プロパティ読出しボタン
//=============================================================================
procedure TForm1.btnReadClick(Sender: TObject);
var
  ExcelApp     : OleVariant;
  ExcelBook    : OleVariant;
  i            : Integer;
  Properties   : OleVariant;
  DocuProperty : OleVariant;
  Value        : OleVariant;
  AItem        : TListItem;
begin
  OpenDialog1.InitialDir := BasePath;
  OpenDialog1.Filter := 'Excel Book|*.xls;*.xlsx;xlsm';

  if OpenDialog1.Execute then begin
    lblEdit01.Text := '';
    lblEdit02.Text := '';
    lblEdit03.Text := '';
    lblEdit04.Text := '';
    lblEdit05.Text := '';
    lblEdit06.Text := '';
    lblEdit07.Text := '';
    lblEdit11.Text := '';
    lblEdit12.Text := '';
    lblEdit13.Text := '';
    Update;
    ListView1.Items.Clear;
    ListView1.Update;
    ListView1.Items.BeginUpdate;

    ExcelApp  := CreateOleObject('Excel.Application');
    ExcelBook := ExcelApp.Workbooks.Open(OpenDialog1.FileName);
    try
      //エクセルに組込み済みのプロパティ(BuiltInDocumentPropertie)のリスト作成
      Properties := ExcelBook.BuiltInDocumentProperties;
      for i := 1 to Properties.Count do begin
        DocuProperty := Properties.Item[i];
        try
          Value := DocuProperty.Value;
        except
          Value := '';
        end;
        AItem := ListView1.Items.Add;
        AItem.Caption := DocuProperty.Name;
        AItem.SubItems.Add(String(Value));
      end;

      //ユーザ定義のプロパティ(CustomDocumentPropertie)があればリストに追加
      Properties := ExcelBook.CustomDocumentProperties;
      for i := 1 to Properties.Count do begin
        DocuProperty := Properties.Item[i];
        try
          Value := DocuProperty.Value;
        except
          Value := '';
        end;
        AItem := ListView1.Items.Add;
        AItem.Caption := DocuProperty.Name;
        AItem.SubItems.Add(String(Value));
      end;
      ListView1.Items.EndUpdate;

      //左側の項目別プロパティの取得
      Panel1.Caption := OpenDialog1.FileName;

      //タイトル
      Value := ExcelBook.BuiltinDocumentProperties['Title'];
      lblEdit01.Text := Value;
      //サブタイトル
      Value := ExcelBook.BuiltInDocumentProperties['Subject'];
      lblEdit02.Text := Value;
      //会社名
      Value := ExcelBook.BuiltInDocumentProperties['Company'];
      lblEdit03.Text := Value;
      //作成者
      Value := ExcelBook.BuiltInDocumentProperties['Author'];
      lblEdit04.Text := Value;
      //最終保存者
      Value := ExcelBook.BuiltInDocumentProperties['Last Author'];
      lblEdit05.Text := Value;
      //コメント
      Value := ExcelBook.BuiltInDocumentProperties['Comments'];
      lblEdit06.Text := Value;

      //作成日
      Value := ExcelBook.BuiltInDocumentProperties['Creation Date'];
      try
        lblEdit11.Text := Value;
      except
      end;

      //最終更新日
      Value := ExcelBook.BuiltInDocumentProperties['Last Save Time'];
      try
        lblEdit12.Text := Value;
      except
      end;

      //最終印刷日付
      Value := ExcelBook.BuiltInDocumentProperties['Last Print Date'];
      try
        lblEdit13.Text := Value;
      except
      end;

      //ユーザ定義プロパティ
      try
        Value := ExcelBook.CustomDocumentProperties['送付先'];
        lblEdit07.Text := String(Value);
      except
      end;

      btnSave.Enabled   := True;
      btnDelete.Enabled := True;
    finally
      //保存なしエクセル終了
      ExcelApp.Quit;
      ExcelBook := Unassigned;
      ExcelApp  := Unassigned;
    end;
  end;
end;

//=============================================================================
//  プロパティ値の書込み
//
//  フォーム左側に表示しているプロパティを定義して値をセット
//  ユーザ定義プロパティで使用可能なプロパティの種類は以下の通り
//  (Office2000.pas内での定義)
//  msoPropertyTypeBoolean   論理型
//  msoPropertyTypeDate      日付型
//  msoPropertyTypeFloat     実数型
//  msoPropertyTypeNumber    整数型
//  msoPropertyTypeString    文字列型
//=============================================================================
procedure TForm1.btnSaveClick(Sender: TObject);
var
  ExcelApp  : OleVariant;
  ExcelBook : OleVariant;
  Value     : OleVariant;
begin
  ExcelApp  := CreateOleObject('Excel.Application');
  ExcelBook := ExcelApp.Workbooks.Open(OpenDialog1.FileName);
  //保存確認のダイアログを表示させない
  ExcelApp.DisplayAlerts:=False;

  try
    //タイトル
    Value := lblEdit01.Text;
    ExcelBook.BuiltInDocumentProperties['Title'].Value := Value;
    //サブタイトル
    Value := lblEdit02.Text;
    ExcelBook.BuiltInDocumentProperties['Subject'].Value := Value;
    //会社名
    Value := lblEdit03.Text;
    ExcelBook.BuiltInDocumentProperties['Company'].Value := Value;
    //作成者
    Value := lblEdit04.Text;
    ExcelBook.BuiltInDocumentProperties['Author'].Value := Value;
    //最終保存者
    //実際には編集内容は変更されない.最終保存者はExcelBook.Saveの時にエクセ
    //ルが判断して更新する
    Value := lblEdit05.Text;
    ExcelBook.BuiltInDocumentProperties['Last Author'].Value := Value;
    //コメント
    Value := lblEdit06.Text;
    ExcelBook.BuiltInDocumentProperties['Comments'].Value := Value;

    //プロパティを追加
    //値を設定しようとして例外発生ならプロパティなしと勝手に判断
    try
      ExcelBook.CustomDocumentProperties['送付先'].Value := lblEdit07.Text;
    except
      ExcelBook.CustomDocumentProperties.Add('送付先',
                                            False,
                                            msoPropertyTypeString,
                                            lblEdit07.Text,
                                            EmptyParam);
    end;
  finally
    //エクセルのブックを保存してエクセル終了
    ExcelBook.Save;
    ExcelApp.Quit;
    ExcelBook := Unassigned;
    ExcelApp  := Unassigned;
  end;
end;

//=============================================================================
//  ユーザ定義プロパティの削除
//=============================================================================
procedure TForm1.btnDeleteClick(Sender: TObject);
var
  ExcelApp  : OleVariant;
  ExcelBook : OleVariant;
begin
  ExcelApp  := CreateOleObject('Excel.Application');
  ExcelBook := ExcelApp.Workbooks.Open(OpenDialog1.FileName);
  //保存確認のダイアログを表示させない
  ExcelApp.DisplayAlerts := False;

  try
    try
      ExcelBook.CustomDocumentProperties['送付先'].Delete;
    except
    end;
  finally
    //エクセルのブックを保存してエクセル終了
    ExcelBook.Save;
    ExcelApp.Quit;
    ExcelBook := Unassigned;
    ExcelApp  := Unassigned;
  end;
end;

end.




11_構造化ストレージ操作用定数ユニット


このぺージのサンプルプログラムで使用しているファイルです.共通に使用するために,このサンプルプログラムのルートに収めてあります.このユニットを使用しているサンプルプログラムのプロジェクトに追加しています.


リスト11 
構造化ストレージを操作するプロジェクトのソースコード
program FileProperties_Storage;

uses
  Forms,
  FileProperties_StorageUnit in 'FileProperties_StorageUnit.pas' {Form1},
  plFileInfo_Const in '..\plFileInfo_Const.pas';

{$R *.res}

begin
  Application.Initialize;
  //Delphi2007以上
  {$IF CompilerVersion > 18.49}
  Application.MainFormOnTaskbar := True;
  {$IFEND}
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


リスト12
構造化ストレージ定数値定義用ユニットのコード
unit plFileInfo_Const;

interface

uses Windows, ComObj, ActiveX, Classes;

const
//-----------------------------------------------------------------------------
// プロパティセットのカテゴリのGUID
// 直接GUIDで指定しないでここでは定数名で使用するために定義
// どのGUIDがどんなファイルに対応するか正確には不明
//
// これらのGUIDは全てが,また全ての環境で(レジストリに)登録されているわけでは
// ない.オーディオ関係のアプリがインストールされていなければ,該当する情報は
// レジストリに登録されないので使用できない.
// つまり,どのような機能がそのPCにインストールされているかによる.
//
// FMTID_SummaryInfo
// ファイルのプロパティで概要タブで扱うプロパティセット
// FMTID_DocSummaryInfo
// 主にワード等が使用するプロパティセット
//  FMTID_UserDefinedProperties
//     [ファイル][プロパティ]のカスタムタブで扱うプロパティセット
//  FMTID_ImageSummaryInfo
//     イメージファイル関係
//  FMTID_MusicFileInfo
//     音楽ファイル関係
//  FMTID_AudioFileInfo
//     オーディオファイル関係
//  FMTID_VideoFileInfo
//     ビデオファイル関係
//  FMTID_MediaFileInfo
//     メディアファイル関係
//
//  実際のプロパティの値は,このGUIDと下の[各プロパティ変数]の組合わせを使用する
//-----------------------------------------------------------------------------

  FMTID_SummaryInfo           : TGUID='{F29F85E0-4FF9-1068-AB91-08002B27B3D9}';
  FMTID_DocSummaryInfo        : TGUID='{D5CDD502-2E9C-101B-9397-08002B2CF9AE}';
  FMTID_DiscardableInfo       : TGUID='{D725EBB0-C9B8-11D1-89BC-0000F804B057}';
  FMTID_UserDefinedProperties : TGUID='{D5CDD505-2E9C-101B-9397-08002B2CF9AE}';
  FMTID_ImageSummaryInfo      : TGUID='{6444048F-4C8B-11D1-8B70-080036B11A03}';
  FMTID_MusicFileInfo         : TGUID='{56A3372E-CE9C-11D2-9F0E-006097C686F6}';
  FMTID_AudioFileInfo         : TGUID='{64440490-4C8B-11D1-8B70-080036B11A03}';
  FMTID_VideoFileInfo         : TGUID='{64440491-4C8B-11D1-8B70-080036B11A03}';
  FMTID_MediaFileInfo         : TGUID='{64440492-4C8B-11D1-8B70-080036B11A03}';

//-----------------------------------------------------------------------------
//  IPropertySetStorageのGUID
//-----------------------------------------------------------------------------

  IID_IPropertySetStorage: TGUID ='{0000013A-0000-0000-C000-000000000046}';

//-----------------------------------------------------------------------------
//  各プロパティのID値の定義
//  FMTID_Summary Informationの定義はActiveX.pasで定義されているが再定義
//  (ActiveX.pas内での定数名はPIDSI_XXXXX)
//  定数名は,極力,ネット上にある参考コードと同じになるようにしているが,.NET
//  では,これらの定数名の定義が設定されているようである
//  正確な日本語表記名は不明
//  説明の参考リンク
//  http://msdn2.microsoft.com/en-us/library/ms690941.aspx
//
//  VT_XXXは変数の型.記載がないのは不明.変数の型には以下もあり
//  VT_EMPTY,VT_NULL,VT_VOID  空またはプロパティがない?
//  VT_R4,VT_R8               実数値
//  VT_ERROR                  エラー値(整数)
//-----------------------------------------------------------------------------

  // Summary Info用
  PID_TITLE            = 2;  // VT_LPSTR,タイトル
  PID_SUBJECT          = 3;  // VT_LPSTR,サブタイトル,表題
  PID_AUTHOR           = 4;  // VT_LPSTR,作成者,著作
  PID_KEYWORDS         = 5;  // VT_LPSTR,キーワード
  PID_COMMENTS         = 6;  // VT_LPSTR,コメント
  PID_TEMPLATE         = 7;  // VT_LPSTR,テンプレート
  PID_LASTAUTHOR       = 8;  // VT_LPSTR,前回保存者
  PID_REVNUMBER        = 9;  // 改定番号
  PID_EDITTIME         = 10; // VT_FILETIME(UTC),編集時間
  PID_LASTPRINTED      = 11; // VT_FILETIME(UTC),最終印刷日時
  PID_CREATE_DTM       = 12; // VT_FILETIME(UTC),作成日時
  PID_LASTSAVE_DTM     = 13; // VT_FILETIME(UTC),最終保存日時
  PID_PAGECOUNT        = 14; // VT_I4,頁数
  PID_WORDCOUNT        = 15; // VT_I4,単語数
  PID_CHARCOUNT        = 16; // VT_I4,文字数
  PID_THUMBNAIL        = 17; // VT_CF,サムネイル
  PID_APPNAME          = 18; // VT_LPSTR,アプリケーション名
  PID_SECURITY         = 19; // VT_I4,セキュリティ

  // Document Summary Info用
  PID_CATEGORY         = 2;  // VT_LPSTR,カテゴリ
  PID_PRESFORMAT       = 3;  // VT_LPSTR
  PID_BYTECOUNT        = 4;  // VT_I4
  PID_LINECOUNT        = 5;  // VT_I4,行数
  PID_PARCOUNT         = 6;  // VT_I4 
  PID_SLIDECOUNT       = 7;  // VT_I4,スライド数
  PID_NOTECOUNT        = 8;  // VT_I4
  PID_HIDDENCOUNT      = 9;  // VT_I4
  PID_MMCLIPCOUNT      = 10; // VT_I4
  PID_SCALE            = 11; // VT_BOOL
  PID_HEADINGPAIR      = 12; // VT_VARIANT or VT_VECTOR
  PID_DOCPARTS         = 13; // VT_LPSTR or VT_VECTOR
  PID_MANAGER          = 14; // VT_LPSTR
  PID_COMPANY          = 15; // VT_LPSTR,会社名
  PID_LINKSDIRTY       = 16; // VT_BOOL,未更新のリンクオブジェクト
  PID_CHARCOUNT2       = 17; //

  // FMTID_ImageSummaryInfo用
  PIDISI_FILETYPE      = 2;  // VT_LPWSTR,ファイルの種類
  PIDISI_CX            = 3;  // VT_UI4,幅
  PIDISI_CY            = 4;  // VT_UI4,高さ
  PIDISI_RESOLUTIONX   = 5;  // VT_UI4,水平方向の解像度
  PIDISI_RESOLUTIONY   = 6;  // VT_UI4.垂直方向の解像度
  PIDISI_BITDEPTH      = 7;  // VT_UI4,ビットの深さ
  PIDISI_COLORSPACE    = 8;  // VT_LPWSTR
  PIDISI_COMPRESSION   = 9;  // VT_LPWSTR,圧縮の有無
  PIDISI_TRANSPARENCY  = 10; // VT_UI4
  PIDISI_GAMMAVALUE    = 11; // VT_UI4,ガンマ値
  PIDISI_FRAMECOUNT    = 12; // VT_UI4,フレーム数
  PIDISI_DIMENSIONS    = 13; // VT_LPWSTR

  // FMTID_MusicFileInfo用
  // 3はhttp://msdn2.microsoft.com/en-us/library/ms690941.aspxには定義なし
  PIDSI_ARTIST         = 2;  // アーチストの名前
  PIDSI_SONGTITLE      = 3;  // 定義なし?
  PIDSI_ALBUM          = 4;  // アルバム名
  PIDSI_YEAR           = 5;  // アルバムが記録された年
  PIDSI_COMMENT        = 6;  // コメント
  PIDSI_TRACK          = 7;  // アルパムのトラック番号
  PIDSI_GENRE          = 11; // アルバムのジャンル
  PIDSI_LYRICS         = 12; // 音楽ファイルの歌詞

  // FMTID_VideoFileInfo用
  PIDVSI_STREAM_NAME   = 2;  // VT_LPWSTR,ビデオの名前,StreamName
  PIDVSI_FRAME_WIDTH   = 3;  // VT_UI4,幅,FrameWidth
  PIDVSI_FRAME_HEIGHT  = 4;  // VT_UI4,高さ,FrameHeight
  PIDVSI_TIMELENGTH    = 7;  // VT_UI4,長さ,TimeLength (milliseconds)
  PIDVSI_FRAME_COUNT   = 5;  // VT_UI4,フレーム数,FrameCount
  PIDVSI_FRAME_RATE    = 6;  // VT_UI4,フレーム率,FrameRate (frames/millisecond)
  PIDVSI_DATA_RATE     = 8;  // VT_UI4,ビットレート,DataRate (bps,bytes/second)
  PIDVSI_SAMPLE_SIZE   = 9;  // VT_UI4,SampleSize
  PIDVSI_COMPRESSION   = 10; // VT_LPWSTR,圧縮レベル,Compression
  PIDVSI_STREAM_NUMBER = 11; // VT_UI2,StreamNumber

  // FMTID_AudioFileInfo用
  PIDASI_FORMAT        = 2;  // VT_BSTR
  PIDASI_TIMELENGTH    = 3;  // VT_UI4,(milliseconds)
  PIDASI_AVG_DATA_RATE = 4;  // VT_UI4,(Hz)
  PIDASI_SAMPLE_RATE   = 5;  // VT_UI4,(bits)
  PIDASI_SAMPLE_SIZE   = 6;  // VT_UI4,(bits)
  PIDASI_CHANNEL_COUNT = 7;  // VT_UI4
  PIDASI_STREAM_NUMBER = 8;  // VT_UI2
  PIDASI_STREAM_NAME   = 9;  // VT_LPWSTR
  PIDASI_COMPRESSION   = 10; // VT_LPWSTR


  //FMTID_MediaFileInfo用
  PIDMSI_EDITOR        = 2;  // VT_LPWSTR
  PIDMSI_SUPPLIER      = 3;  // VT_LPWSTR
  PIDMSI_SOURCE        = 4;  // VT_LPWSTR
  PIDMSI_SEQUENCE_NO   = 5;  // VT_LPWSTR
  PIDMSI_PROJECT       = 6;  // VT_LPWSTR
  PIDMSI_STATUS        = 7;  // VT_UI4
  PIDMSI_OWNER         = 8;  // VT_LPWSTR
  PIDMSI_RATING        = 9;  // VT_LPWSTR
  PIDMSI_PRODUCTION    = 10; // VT_FILETIME(UTC)
  PIDMSI_COPYRIGHT     = 11; // VT_LPWSTR 

//-----------------------------------------------------------------------------
// StgOpenStorageEx関数の第3引数.ストレージファイルのフォーマット
//
// STGFMT_STORAGE  必ずワード等の複合ファイルである
// STGFMT_FILE     必ずしも複合ファイルではないことを指定.現在未サポート 
// STGFMT_ANY      ファイル形式をシステムが決定し,適当な構造化ストレージやプ
// ロパティセットの実装を使用
// STGFMT_DOCFILE  STGFMT_STORAGE同様にファイルが必ず複合ファイルであることを
// 指定.古い形式の複合ファイル用か?
//-----------------------------------------------------------------------------

  STGFMT_STORAGE = 0;
  STGFMT_FILE    = 3;
  STGFMT_ANY     = 4;
  STGFMT_DOCFILE = 5;

//-----------------------------------------------------------------------------
//  ファイルシステム上の既存のストレージオブジェクトを開く関数
//  ワード等の複合ファイルを開くためにも,通常のファイルを開くためにも使用可能
//  ActiveX.pasにはこの関数がないため,定義して使用
//
//  Delphi7の場合,StgOpenStorage関数はあるが,MicrosoftではWindows2000以降の
//  アプリはこのStgOpenStorageEx関数を使うべきとしている.
//  ただし,Office 2007以降は,この構造化ストレージを使用しなくなった
//
//  pwcsName    : フルパスで指定.文字数はMAX_PATHを越えてはいけない
//  grfMode     : このオブジェクトのアクセスモード.各グループから一つ指定
//                Access group
//                STGM_READ      読取専用
//                STGM_WRITE     現在未サポート
//                STGM_READWRITE 読書き両用
//                Sharing group
//                STGM_SHARE_DENY_NONE  後発のオブジェクト読み書きアクセスを拒否
//                                      しない 
//                STGM_SHARE_DENY_READ  後発のSTGM_READモードの取得を阻止する
//                                      通常ストレージオブジェクトに使用
//                STGM_SHARE_DENY_WRITE 後発のSTGM_WRITE,STGM_READWRITEアクセス
//                                      モードでのオブジェクトの取得を阻止する
//                                      複数のユーザによる操作禁止に使用される
//                STGM_SHARE_EXCLUSIVE  STGM_SHARE_DENY_READと
//                                      STGM_SHARE_DENY_WRITEとの組合わせ
//                                      後発のあらゆるアクセスモードでのオブジェ
//                                      クトの取得を阻止する
//                STGM_PRIORITY         排他アクセスで開く指定
//                Direct SWMR and Simple group
//                STGM_SIMPLE           複合ファイルのより高速な実装を提供.
//                                      よく使用される(制限があるらしい)
//  stgfmt      : ストレージファイルフォーマット.通常0で可
//  grfAttrs    : 予約.0を指定
//  pStgOptions : STGOPTIONS ポインタのアドレス.通常nil
//  reserved2   : 予約.nilを指定
//  riid        : インタフェースポインタのGUID
//                通常はID IPropertySetStorageでよい
//  stgOpen     : インタフェースポインタのアドレス
//                開かれたストレージオブジェクトのインタフェースポインタを受け
//                取るポインタ変数のアドレス.
//                オープンに失敗すると0を返す
//
//  戻値
//  正常終了の場合はS_OKを返す
//  STG_E_INVALIDPOINTER  stgOpenパラメータのポインタが無効
//  STG_E_INVALIDPOINTER  pStgOptionsパラメータのポインタが無効
//  E_NOINTERFACE         指定されたインタフェースはサポートされていない
//  STG_E_INVALIDFLAG     grfMode ポインタのフラグの組み合わせが無効
//  STG_E_INVALIDNAME     pwcsName パラメータの名前が無効
//  STG_E_LOCKVIOLATION   ファイルが開かれロックしているためアクセス拒否  
//  STG_E_SHAREVIOLATION  同上
//  STG_E_INCOMPLETE      高度に隠れているデバイス(?)のためファイルが開けない
//  STG_E_ACCESSDENIED   ユーザアクセスを許可していないためファイルが開けない
//
//  [参考]
//  新しいストレージファイルの作成はStgCreateStorageEx関数
//-----------------------------------------------------------------------------
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';

implementation

end.




12_画像ファイルのプロパティ


デジタルカメラの写真画像の Exif 情報や,GPS 関係の情報を取得するには,GDI+ の機能を利用すると便利です.[G160_GDI+ 画像ファイルのプロパティとメタデータ] の記事を参考にしてください.





13_プロパティのダイアログの表示 - ShellExecuteEx 関数


ShellExecuteEx 関数を使用して,ファイルのプロパティのダイアログを表示する例です.
フォルダのパス名を指定すればフォルダのプロパティのダイアログを表示します.
ShellExecute 関数の第 2 引数にも Properties が指定できるようですが,実際にはプロパティのダイアログは表示できません.



図23
ファイルのプロパティのダイアログを表示

リスト13
ShellExecuteEx 関数を使用してプロパティのダイアログを表示
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FBasePath : string;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ShellAPI;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  FBasePath := '../';
  FBasePath := ExpandFileName(FBasePath);
end;

//=============================================================================
//  ファイルを選択してプロパティのダイアログを表示
//  usesにShellAPIが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSHExeInfo : TSHELLEXECUTEINFO;
  LFlag      : Cardinal;
  LFilePath  : String;
begin
  OpenDialog1.InitialDir := FBasePath;
  if not OpenDialog1.Execute then exit;

  //ファイルのパス
  LFilePath := OpenDialog1.FileName;

  LFlag := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_INVOKEIDLIST or SEE_MASK_FLAG_NO_UI;

  LSHExeInfo.cbSize       := Sizeof(LSHExeInfo);
  LSHExeInfo.fMask        := LFlag;
  LSHExeInfo.lpVerb       := 'Properties';
  LSHExeInfo.Wnd          := Handle;
  LSHExeInfo.lpParameters := nil;
  LSHExeInfo.lpDirectory  := nil;
  LSHExeInfo.lpIDList     := nil;
  LSHExeInfo.lpFile       := PChar(LFilePath);

  if not ShellExecuteEx(@LSHExeInfo) then begin
    //表示失敗
  end;
end;

end.




14_プロパティのダイアログの表示 - SHObjectProperties 関数


SHObjectProperties 関数を使用して,ファイルのプロパティのダイアログを表示する例です.
フォルダのパス名を指定すればフォルダのプロパティのダイアログを表示します.
ダイアログを表示した時に,最後の引数で指定したタブを表示します.この引数の値が nil か,指定した名前のタブがない場合はデフォルトのタブが表示されます.



図24
ファイルのプロパティのダイアログを表示
  • [詳細] タブが表示される

リスト14
SHObjectProperties 関数を使用してプロパティのダイアログを表示
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FBasePath : string;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ShlObj;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  FBasePath := '../';
  FBasePath := ExpandFileName(FBasePath);
end;

//=============================================================================
//  ファイルを選択してプロパティのダイアログを表示
//  usesにShlObjが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : String;
  LPage     : String;
begin
  OpenDialog1.InitialDir := FBasePath;
  if not OpenDialog1.Execute then exit;

  //ファイルのパス
  LFilePath := OpenDialog1.FileName;
  FBasePath := ExtractFilePath(LFilePath);
  //初期表示タブ名
  //指定したタブがない場合はデフォルトのタブとなる
  LPage := '詳細';

  SHObjectProperties(Handle, SHOP_FILEPATH, PChar(LFilePath), PChar(LPage));
end;

end.