クラスヘルパーで既存のクラスを継承することなく拡張する

クラスヘルパーを使うと、既存のクラスを継承しなくても拡張することができます。

■サンプル

次の例はTListBoxにItemIndexValueという関数を追加しています。

デザインモードではいつも通りにコンポーネントを配置します。

クラスヘルパーでTListBoxにItemIndexValue関数を追加します。

type
  TListboxHelper = class helper for TListBox
    function ItemIndexValue: String;
  end;

implementation

function TListboxHelper.ItemIndexValue: String;
begin
  Result := '';
  if ItemIndex >= 0 then
    Result := Items[ItemIndex];
end;

ListBox1をクリックした時、選択されている項目をEdit1に表示します。

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Edit1.Text := ListBox1.ItemIndexValue; //追加した関数を呼び出す
end;

ListBox1の項目を選択すると、項目の値がEdit1に表示されます。

■おまけ

TListBoxを継承した同名のクラスを作成する方法もあります。

type
  //標準のTListBoxを継承した新しいTListBoxを作成
  //コンパイラはこっちのTListBoxを優先して使用する
  TListbox = class (StdCtrls.TListBox)
    function ItemIndexValue: String;
  end;

implementation

function TListbox.ItemIndexValue: String;
begin
  Result := '';
  if ItemIndex >= 0 then
    Result := Items[ItemIndex];
end;

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  Edit1.Text := ListBox1.ItemIndexValue; //追加した関数を呼び出す
end;

2011年8月22日追記:名前空間の検索順序に関する間違った記述を削除した。

EurekaLogでDelphiアプリケーションのメモリリークを検出する

■メモリリークを報告する「ReportMemoryLeaksOnShutdownプロパティ」

Delphiには、アプリケーション終了時にメモリリークを報告するReportMemoryLeaksOnShutdownプロパティがあります。

program Project1;

uses
  ExceptionLog,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  ReportMemoryLeaksOnShutdown := True; //<=追加

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

参考:メモリリークの報告 « 開発言語の備忘録

ただ、ReportMemoryLeaksOnShutdownプロパティではメモリリークがあったことがわかるだけで、どこに原因があるのかまではわかりません。


メモリリークの報告ダイアログ

■メモリリークの発生場所までわかる「EurekaLog」

EurekaLogを使用すると、メモリリークの原因まで検出することができます。

EurekaLogでメモリリークを検出するには、設定画面の「Catch Memory Leaks」チェックボックスにチェックを入れます。

メモリリークのあるアプリケーションを終了すると、次のようなダイアログが表示されます。

「click here」のリンクをクリックすると、詳細なエラー情報を表示する画面が表示されます。
「Call Stack」タブを選択すると、メモリリークの発生場所がわかります。

メモリリークの発生場所をダブルクリックすると、IDEの該当行にフォーカスが移動します。

このようにEurekaLogを使うと問題の箇所がすぐにわかり、修正することができます。

TStopwatchの経過時間は停止状態でないと更新されない?

経過時間を示すElapsed/ElapsedMilliseconds/ElapsedTicksプロパティはStopメソッドで停止した状態でないと更新された値を取得できない。

Owl’s perspective: TStopwatch」より

あれ?そうだったかな。と、疑問に思ったので確かめてみた。

Elapsed プロパティの値は、ストップウォッチがすでに停止している場合にのみ更新されます。

Diagnostics.TStopwatch.Elapsed – XE API Documentation」より

ちゃんとヘルプにもそう書かれていました。

そうすると記憶違いかな。

とりあえず動作を確認してみると、Stopメソッドで停止した状態でなくても、ちゃんと計測されているみたい。

uses
  SysUtils, Diagnostics, TimeSpan;
var
  sw: TStopwatch;
begin
  try
    sw := TStopwatch.Create;
    sw.Start;

    Sleep(1000);
    Writeln(sw.ElapsedMilliseconds);

    Sleep(1000);
    Writeln(sw.ElapsedMilliseconds);

    Sleep(1000);
    sw.Stop;

    Writeln(sw.ElapsedMilliseconds);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

実行結果

1000
2000
3000

ヘルプには「TStopwatch では、オペレーティング システム依存の機能を利用して、高解像度タイマにアクセスします(高解像度タイマを使用できない場合は、通常のタイマが使用されます)。 (Diagnostics.TStopwatch – XE API Documentation)」と書かれているので、環境によるのかもしれない。

2011年9月24日追記

元記事が修正されています。

また「Owl’s perspective: TStopwatchその2」でヘルプの記述が間違っている歴史的理由が考察されています。

TIdDecoderUUEでuudecode – uuencodeで作成されたファイルをデコードする

uuencodeで作成されたファイルをデコードするには、TIdDecoderUUEを使用します。

#include <memory>
#include <IdCoderUUE.hpp>

std::unique_ptr<TIdDecoderUUE> dec(new TIdDecoderUUE(NULL));

//デコードしたデータを保存するTStream
std::unique_ptr<TMemoryStream> ms(new TMemoryStream());
dec->DecodeBegin(ms.get());

//デコード開始
dec->Decode("MB5!.1PT*&@H    -24A$4@   \"     @\" 8   !S>GKT    !&=!34$  +&/");
dec->Decode("M\"_QA!0   $=)1$%46$?MUK$- \" (!$#<?VA$W0 *FR/Y4OU<@RLBLM*>S-'Q");
dec->Decode("M^^ZYH9TJ,!H%\"! @0(  @1CMTO<9F$4! @0($\"! X+? !I?UJM5MS!;U    ");
dec->Decode(") $E%3D2N0F\"\"");

//デコード終了
dec->DecodeEnd();

//デコードしたデータをファイルに保存する
ms->Position = 0;
ms->SaveToFile(L"C:\\testimg.png");

DecodeBeginメソッドの引数には、デコードしたデータを保存するTStream*を指定します。

Decodeメソッドの引数にエンコードされた文字を渡し、DecodeEndメソッドでデコードを完了します。