Webセミナー「アンドキュメンテッド(?) VCL 〜逆引きVCL新機能〜」自己フォロー

QAで指摘があった件を試してみました。

TListViewのグルーピングがうまくいかなかった件

本当は、こんな感じに表示されるはずでした。
テストは、64bit版のWindows 7でテーマ有効。実機はWindows 7の32bit版。この辺の差異かな…。

TDirectory.GetFilesでファイル属性を取得する

TDirectory.GetFilesはファイル名だけかと思いきや、無名メソッドを使用するバージョンで属性とか取れるという指摘があったので試してみました。
結果をフィルタリングするために使われる TFilterPredicate オプションがあって、そこで属性を取得。
Delphiのサンプルはこんな感じ。

procedure TForm1.Button1Click(Sender: TObject);
var
  Dir : String;
  Root: WideString;
  Files: TStringDynArray;
  FilterPredicate: TDirectory.TFilterPredicate;
begin
  Dir := GetMyDocumentsPath(CSIDL_PERSONAL);
  if SelectDirectory('フォルダを選択してください。', Root, Dir, [sdNewUI]) then
    Edit1.Text := Dir;

  with Memo1 do begin
    Clear;
    Lines.BeginUpdate;
    // ファイルを検索
    Files := TDirectory.GetFiles(Edit1.Text, '*.pas', TSearchOption.soAllDirectories,
        function(const Path: string; const SearchRec: TSearchRec): Boolean
        begin
            Lines.Add(Format('Path = %s Size = %d', [Path + SearchRec.Name, SearchRec.Size]));
            Result := true;
        end
    );
    Lines.EndUpdate;
  end;
end;

引数に無名メソッドを渡し、その中でファイルの属性を取得する。

C++Builderの場合は無名メソッドコンパイラがサポートしていないので、TCppInterfacedObjectを継承したクラスで判定する。(参考:http://www.gesource.jp/weblog/?p=4509

class Filter : public TCppInterfacedObject<TDirectory::TFilterPredicate>
{
public:
  bool __fastcall Invoke(const System::UnicodeString Path, const Sysutils::TSearchRec &SearchRec)
  {
    UnicodeString s = Format("Path = %s Size = %d", ARRAYOFCONST((Path + SearchRec.Name, SearchRec.Size)));
    Form1->Memo1->Lines->Append(s);
    return true;
  }
};

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  wchar_t DirPath[MAX_PATH];
  ::SecureZeroMemory(DirPath, sizeof(DirPath));
  ::SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, DirPath);
  UnicodeString Dir(DirPath);

  if (::SelectDirectory(L"フォルダを選択してください。", "//", Dir, TSelectDirExtOpts() << sdNewUI)) {
    Edit1->Text = Dir;
    Memo1->Clear();
    Memo1->Lines->BeginUpdate();
    // ファイルを検索
    Filter* filter = new Filter();
    TDirectory::GetFiles(Edit1->Text, "*.cpp", TSearchOption::soAllDirectories, filter);
    // delete filter; // deleteは不要
    Memo1->Lines->EndUpdate();
  }
}

フィルタリングを行うクラスのインスタンスはnewで初期化する必要があるが、明示的にdeleteする必要が無い。

ちなみに、C++11のラムダ式*1C++Builderに実装されたら、こんな感じになるのかな…。

    TDirectory::GetFiles(Edit1->Text, "*.cpp", TSearchOption::soAllDirectories, 
      [this](const System::UnicodeString Path, const Sysutils::TSearchRec &SearchRec)
      {
        UnicodeString s = Format("Path = %s Size = %d", ARRAYOFCONST((Path + SearchRec.Name, SearchRec.Size)));
        Memo1->Lines->Append(s);
        return true;
      });

ああ、滅茶苦茶便利。

*1:C++Builder XE3(?)でC++11のラムダ式サポートまだぁ?(AA略