高見知英の技術ログ

技術関係のログをQiitaから移行してきました。プログラミングのほか、使っているアプリの細かい仕様についてなど書いていきます。

Lazarusアプリ終了時にメモリリークチェック用ダイアログが表示される件

このブログはLazarus(FreePascal)アドベントカレンダー21日目の記事です。

qiita.com

今回のテーマはLazarusのアプリを終了すると表示されることがある デバッグレポートについて。

f:id:TakamiChie:20211214164602p:plain
Heap dump heaphtc unitのダイアログ

プロジェクトオプションの「コンパイラオプション\デバッグ\Heaptrcユニットを使用する」をチェックした構築モードでコンパイルを行なうと、起動したアプリケーションの終了時にこのようなダイアログが表示されます。

これはオプションに記載の通り、メモリリークの監視を行なうHeaptrcというユニットの出力ダイアログで、アプリケーション内で解放し忘れたオブジェクトがあるとここに表示されます。

中にはそのメモリを取得したファイル上の行番号も記載されているので、メモリリーク解消のための手がかりにはなるかと。

ログをダイアログではなく、ログファイルに出力する

このダイアログ、出力が多い場合は複数回に分けて表示され、いつまで経ってもダイアログが表示され続けるというちょっと厄介な性質を持っています。

ダイアログが煩わしい場合は、heaptrc.setHeapTraceOutputというメソッドでファイル名を指定することで、ログの出力先をファイルに変更することができます。

{$IFOPT D+}
  setHeapTraceOutput('trace.log');
{$ENDIF}

とはいえログの出力が終わるまでアプリケーションの実行が続くという性質は変わりありませんので、それでもログの出力が長すぎていつまで経ってもデバッグ実行が止まらない・・・ という場合は、Lazarusの実行メニューやツールバーから「終了」してしまいましょう。

自分個人で使っている場合にこれが出られると困る という場合は?

作ったアプリケーションのメインユーザーが自分自身で、自分自身が使うときはデバッグモードのバイナリを使っている という方も少なくないのではないかと思います。

ただそんなときにもこのトレースの出力が出続ける となるとさすがに煩わしいですね・・・。

そんなときは、構築モードを分けて以下のような構築の仕方にするのが良いのかなと思います。

  • リリースする場合:Releaseモード
  • Lazarusデバッガ上で実行する場合:Debugモード
  • 自分個人で使う場合:Defaultモード

そんなときに便利なものとして、構築モードがあります(いままでの記事でも何度か話題にしているので今更ですが…)

デバッグおよびリリースモードを作成

プロジェクトオプションより、コンパイラオプションノードを選択、上部の構築モードの右側にある「…」のボタンをクリックします。

f:id:TakamiChie:20211214170845p:plain
構築モードを編集する

Lazarusにはよくある構築モード構成として、「デバッグおよびリリースモードを作成」という機能がありますので、これを使います。

f:id:TakamiChie:20211214171011p:plain
デバッグおよびリリースモードを作成

すると、Defaultモードの下に、DebugとReleaseという構築モードが追加されるので、OKをクリックします。

f:id:TakamiChie:20211214171127p:plain
構築モードが追加された

ここでダイアログを閉じるとアクティブな構築モードがDebugになるので、IDE上でアプリケーションを実行するときはDebug、自分用にビルドするときはDefaultを選ぶようにします。

毎回手動でモードを切り替えるのは面倒くさい!

とはいえ毎回手動でモードを切り替えるのは面倒くさいですよね。その場合はツールメニューの外部ツールより、Defaultビルドを行なうメニューなどを作っておくと良いでしょう。

  • タイトル:Defaultでビルド(自分に分かれば何でもいい)
  • プログラムファイル名:C:\lazarus\lazbuild.exe
  • 引数:--build-mode=Default --no-write-project $ProjFile()
  • 作業ディレクトリ:$ProjPath()
  • オプション:FPCメッセージの出力をスキャン、"make"メッセージの出力をスキャンにチェック

f:id:TakamiChie:20211214172336p:plain
外部ツールの設定

これで、このメニューを選択したときだけDefaultモードでコンパイルが行なわれ、かつアクティブな構築モードは切り替わりません。

ただしこのDefaultモードでHeaptrcを使っていない場合はsetHeapTraceOutputメソッドの呼び出しがエラーになってしまいます。

なので、Defaultモードの「コンパイラオプション\カスタムオプション」の値に「-dDEFAULT」を追加しておきましょう。

f:id:TakamiChie:20211214172836p:plain
カスタムオプションの追加

次にsetHeapTraceOutputの呼び出しを次のように修正します。

{$IFOPT D+}
{$IFNDEF DEFAULT}
  setHeapTraceOutput('trace.log');
{$ENDIF}
{$ENDIF}

これでDefaultモードのときはこの行が実行されなくなります。

Lazarusのパッケージマネージャ

このブログはLazarus(FreePascal)アドベントカレンダー20日目の記事です。

qiita.com

今回のお題はLazarusのパッケージマネージャについて。Lazarusにはオンラインパッケージマネージャというものが付属しており、パッケージメニューから起動することができるようになっています。

f:id:TakamiChie:20211218174301p:plain
オンラインパッケージマネージャ

こちらは昨日のパッケージのインストールとやれることは基本的に同じですが、「パッケージのインストールもしくはアンインストール」メニューだとあらかじめLazarusに同梱されたパッケージを取捨選択できるのに対し、こちらはLazarusサイトにて公開されているさまざまなLazarusパッケージを追加インストールすることができるもの という感じ。

パッと見た感じビジュアルコンポーネントが多めのようです(非ビジュアルコンポーネントもありますが、TComponentを継承しているためデザイナーでフォームに配置して使える というものが多い)。

f:id:TakamiChie:20211218174708p:plain
オンラインパッケージマネージャ画面

ただIDEコンポーネント一覧に追加する都合上、パッケージのインストールが終了すると、IDEの再構築が必要になります。なのでこちらもおそらく、若干ながらなんらかの不整合によりLazarus自体が起動しなくなってしまうこともありえそうです。

なので万全を期すならこちらもSandbox環境でインストールしてテストしてみると良さそう。

実際に公開されているパッケージ

実際に公開されているものをいくつか入れてみました。

実際にフォームに貼り付けて使うビジュアルコンポーネントや・・・。

f:id:TakamiChie:20211218175233p:plain
TShapeLine(ATShapeLineパッケージ)

f:id:TakamiChie:20211218175421p:plain
TCaptchaLabel(Captchaパッケージ)

フォームに貼り付けるとアイコンだけが表示される非ビジュアルコンポーネントも公開されています。

f:id:TakamiChie:20211218175628p:plain
TUniqueInstance(UniqueInstanceパッケージ)、TPascalTZ(PascalTZパッケージ)

インストールされたコンポーネントはどこへ行く?

インストールされたコンポーネントコンポーネント一覧のどこかのタブに追加されます。

ここが結構厄介で、とりあえずパッケージ制作者が何も指定していない場合はMiscタブに格納されるようですが、まったく違うタブに追加されるものもあるみたい。

表示メニューのコンポーネントメニューから表示されるコンポーネント一覧で名前を探すとかしてみたほうがよさそう。

肝心のコンポーネントの使い方は・・・

だいぶ不明。ぱっと見で何となく分かるTCaptchaLabelとかはわかるけど非ビジュアルコンポーネントとかどう使えば良いのやら・・・。

いちおうコンポーネント一覧のHome pageを見た感じGithubのリンクになってる場合が多いので、そこを見てみるとREADMEに割としっかりとした説明が書かれているパッケージもありそうです。

ドキュメントコメントがないものもあり、ちょっと使いづらい感覚はあるかな・・・。

自分がインストールしていないパッケージを使っているプロジェクトを読み込もうとしたときは

さて、(自分が知っている限り)PythonのpipenvやJavaScriptのnpmのように、コマンド一つで使用しているモジュールなどをダウンロードできるコマンドが存在しないLazarus。

ここで公開されているコンポーネントを使用したプロジェクトを、パッケージをインストールしていない環境で開こうとした場合はどうなるか?

というと、次のようなダイアログが表示され、そのままパッケージをインストールすることができます。

f:id:TakamiChie:20211218180451p:plain
パッケージをインストールするかどうかを聞かれる

なので「なんか面白そうなプログラムのソースコード見つけたけどパッケージがないから使えないよ!」なんてことはとりあえずなさそう(バージョン違いとかどうするんだろう?)。

LazarusのIDE構成を変更する

このブログはLazarus(FreePascal)アドベントカレンダー19日目の記事です。

qiita.com

今回のお題はIDEの構成。LazarusはIDEの構成を変更することで、機能を追加・削除することができるというユニークな機能を持った開発環境です。

パッケージは基本的に*.lpkという形式のファイルにまとまっているのですが、ごく基本的なパッケージは既にLazarusに同梱されており、「パッケージ」メニューの「パッケージをインストールもしくはアンインストール」メニューから追加・削除することができます。

f:id:TakamiChie:20211213181848p:plain
パッケージのインストールもしくはアンインストール

なお、ここでインストールできるパッケージは基本的に日本語化されていませんので、日本語の設定にしていても英語で表示されます(なお、以降のスクリーンショットWindowsサンドボックスの環境内で撮影したため、全文英語となっています)。

どんなパッケージがあるのか

既に用意されているパッケージといっても様々で、たとえば様々なお役立ちメニューを追加したり・・・。

f:id:TakamiChie:20211213182128p:plain
Codyパッケージにより挿入メニューが増えた

ダイアログつきの簡易ウィザードを提供してくれたり・・・。

f:id:TakamiChie:20211213182227p:plain
MessageComposerPkgパッケージによる簡易ダイアログ生成ツール

なかには最近のDelphi風にウィンドウひとつの開発環境を提供してくれるものもあります( Laz にゃん: Lazarus IDE をドッキング対応にする参照 )。

f:id:TakamiChie:20211213182341p:plain
AnchorDocking, AnchorDockingDsgnパッケージをインストールすると、最近のDelphi風ウィンドウになる。

これらは基本的にパッケージのインストール画面からIDEを再構築することによって使用可能になります。

f:id:TakamiChie:20211213182718p:plain
パッケージのインストールもしくはアンインストール

導入には注意

ただし、このコンポーネント、いくつか組み合わせ次第ではうまく動かないコンポーネントも混じっているようで、便利そうだからとほいほいパッケージをインストールすると、Lazarusが起動しなくなってしまうことがあります。

そのようなときはLazarus自体の再インストールが必要になってしまいますので、Windows 10 / 11 Professionalを利用している方は、Windowsサンドボックス下で試験してから実環境に適用するようにするなど、なんらかの対策が必要になります。

LazarusでDelphiのプロジェクトを読み込む

このブログはLazarus(FreePascal)アドベントカレンダー18日目の記事です。

qiita.com

この機能を使えば昔Delphiで作ったあんなアプリをインポートして、Lazarusで使うことができます。

LazarusのLCLなどのライブラリにはある程度DelphiVCLと互換性があるので、運が良ければそのまま、そうでなくてもそれなりに改修を行なえばLazarusでもプロジェクトを使うことができます。

やってみた

ということで、今回インポートしてみたツールはこちら。とはいってもはるか昔まだOperaがPrestoエンジンを使っていた頃のアプリなので、もちろん今は全く使えないのですが・・・。

www.vector.co.jp

プロジェクトのインポート

とりあえずDelphiのプロジェクトを読み込みます。Delphiのプロジェクトを読み込むには、ツールメニューのDelphi変換を選びます。

f:id:TakamiChie:20211212185738p:plain
Delphi変換メニュー

ここで「DelphiプロジェクトをLazarusプロジェクトに変換」を選ぶと、*.dprのファイルを選択するダイアログが表示され、選択すると確認画面が表示されます。

f:id:TakamiChie:20211212190011p:plain
インポート詳細

このままインポートを開始すると、変換処理が開始されます。ここでやっているのは見た感じ次の通りです。

  • *.dprファイルを*.lprファイルに改名(元のファイルはConverterBackupフォルダにコピーされる)
  • ファイルのエンコードUTF-8に変更する
  • Windows APIを呼び出しているコードのうちLCLなどLazarusが使用できるライブラリで代用可能なものは、それに置き換えする(ShellExecuteOpenDocumentなど)
  • 互換性のないモジュールやプロパティの削除

いちおうこれらについては変換完了後、AutomaticConversion.logというファイルに出力されます。

エラーが起こった場合

なお、参照しようとしているモジュールが見つからなかった場合などは、次のようなダイアログが表示されます。

わたしの場合昔は参照フォルダから呼び出すクラスライブラリ集などを自分で作ってたので大変(幸い過去のライブラリは一つも捨てていないようだったので、時間をかければ復元できそうですが…)。

f:id:TakamiChie:20211212190738p:plain
インポート中のエラー表示

無事変換が終わると、通常通りプロジェクトが表示されます。F5で実行してみると・・・。

f:id:TakamiChie:20211212190834p:plain
当時のアプリが起動した。

なんとか起動しました。

ただ実際に使ってみると・・・

ただ実際にいろいろ過去のプロジェクトをいじってみたのですが、結構問題があちこちで起こります。

  • 画面上に表示されない範囲でLCLVCLの仕様が異なるところが多く*1、独自の拡張を行なったクラスなどがあった場合、かなりの手直しが必要になる。
  • ピクセル単位で描画処理を行なっている箇所などがあれば、縮尺がずれてしまい正常に表示されないことが多い。
  • dfmファイルがリソースに埋め込まれてしまっている場合などは、それらを削除せずにプログラムを読み込んでしまうため、エラーが発生する。
  • 変換時にソースコード文字コード変換を行なう・・・と思いきやときどき文字コードの変換を行なわずスキップしてしまうファイルが出る*2ため、結局すべての文字コードを確認し直す羽目になる。

このためとりあえずソースコードコンパイルして雰囲気を確認できたら、すべて閉じて一から実装し直し というほうがむしろ効率が良いような気もします。

つかいどころ

2003年頃、Delphi 6 Personalが無償提供され、それに飛びついていた というホビープログラマは、少なくないと思います(当時は無償で提供されている開発環境は貴重でしたので)。

プライベートフォルダに、GitやSVNリポジトリに、当時作っていたツールのソースコードが眠っている なんて方もいらっしゃるのではないでしょうか。

そんな人が、過去の遺産を取り出すには有用な機能ではないかと思います。

もしそんなプログラムがある方は、当時を振り返るという意味でとりあえずこの機能を使ってみる というのもよいのではないでしょうか?

ただ、実際にやる場合、プロジェクトごと変換してしまうと歪みが出るので、「DelphiユニットをLazarusユニットへ変換」メニューを使って参照ファイルを一個ずつ変換する 等というやり方が良いかもしれません・・・(だいぶ痛い目を見た)。

*1:たとえばTLabelがラベルの文字描画をDoDrawTextメソッドに分離せずPaintメソッド内で全部やっているとか、ウィジェットのサイズを自動調整するメソッドがAdjastBoundsではなくAdjastSizeになっているとか

*2:プロジェクト外のモジュールを呼んでいた場合などに起こりやすい様子

Lazarusでコードの折りたたみ

このブログはLazarus(FreePascal)アドベントカレンダー17日目の記事です。

qiita.com

今回は(も)小ネタ。Lazarusでコードの折りたたみブロックを作ります。

やり方は簡単で、折りたたみを開始する箇所を{$REGION 'リージョンの名前'}、終了する箇所を{$ENDREGION}で囲むだけです。

{$REGION 'ActionList'}

procedure TMainForm.メソッド1(Sender: TObject);
begin
end;

procedure TMainForm.メソッド2(Sender: TObject);
begin
end;

procedure TMainForm.メソッド3(Sender: TObject);
begin
end;

{$ENDREGION}

すると$REGIONの位置に折りたたみ用のボタンが表示され、クリックすると折りたたみが可能になります。

f:id:TakamiChie:20211210171426g:plain
コードの折りたたみ

ただし、以前もちょっと触れましたが、コードの折りたたみはコードの文脈を無視してなんでも折りたたんでしまうので、濫用厳禁です。

ただ個人的には宣言部と実装部が分かれているPascalなら、比較的混沌を生みづらいのではないかな?という気がします。しらんけど。

Lazarusにおけるドキュメントコメント

このブログはLazarus(FreePascal)アドベントカレンダー16日目の記事です。

qiita.com

Lazarus(というかPascal)ではメソッドの使用方法などを紹介するために、ドキュメントコメントを設定することができます。

Pascalにおけるドキュメントコメントは、XMLの形式をとっています*1

{・・・}
/// <summary>関数の説明</summary>
/// <param name="ScriptFile">引数1の説明</param>
/// <param name="StdOut">引数2の説明</param>
/// <param name="StdOut">引数3の説明</param>
/// <returns>戻り値の説明</returns>
function Execute(ScriptFile: String; out StdOut: String; out StdErr: String): Integer;
{・・・}

ちゃんと書かれていると、関数をマウスでホバーしたときにヒントが表示されるようになります。

f:id:TakamiChie:20211210164828p:plain
メソッドヒント

XMLで書いてる意味ないじゃん!という気がしなくもないです。まあDelphiだともうちょっと読みやすい形式が出てくるようですが。

で、このドキュメントコメントですが、Pascalの場合メソッドの宣言部に書きます。実装部に書いてもなにもないのでご注意ください。

FPDocエディタ

いちおう、Lazarusには入力を支援するツールして、FPDocエディタというものがあります。

f:id:TakamiChie:20211210165250p:plain
FPDoc

ただ個人的には使い方がよくわからなかった・・・ いちおう公式Wikiにも使い方は書いてあるのですが、結局よくわからず・・・。あれ使えている人いるんでしょうかねえ。

ただ、実際にどれほどの効果があるかどうかはともかく、メソッドの使い方がある程度書いてあると第三者が読んだときにかなり解析が楽になります(Free Pascal標準ライブラリのソースを読んだときの感想)。

XML書式を使うかどうかはともかく、積極的にコメントを書いていくと良いでしょう。

*1:の、割にはLCLなどのソースを見ると全く違う書式でドキュメントコメントが書かれているので困惑しますが…。せめてそこは統一して欲しかった

LazarusでHTMLをレンダリングする

このブログはLazarus(FreePascal)アドベントカレンダー15日目の記事です。

qiita.com

最近はなんだかんだちょっと複雑なテキストをレンダリングするには、HTMLビューアを使う というパターンが多いのではないかと思います。

LazarusでHTMLビューアを内蔵したアプリを作るには というと、標準の場合は、IProタブのTIpHtmlPanelというものを使います。

f:id:TakamiChie:20211210160515p:plain
TIpHtmlPanel

このコンポーネントを貼り付けると赤い○に「HTML」とだけ書かれた領域が表示されますので、実行時にHTMLを書き込んで、レンダリングを行ないます。

f:id:TakamiChie:20211210160900p:plain
TIpHtmlPanel(デザイン時)

f:id:TakamiChie:20211210161009p:plain
TIpHtmlPanel(実行時)

HTMLの書き込みには、TIpHtmlPanel#SetHtmlFromStr()というメソッドを使います。簡易的なCSSも解釈してくれるようなので、リソースにあらかじめCSSファイルを読み込ませておき、それをまぜてHTMLとして出力 みたいなコードを書くと楽です。

procedure TMainForm.UpdateStatus(HTML: String);
var
  r: TResourceStream;
  s: TStringStream;
begin
  try
    r := TResourceStream.Create(HINSTANCE, 'STYLE', RT_RCDATA);
    try
      s := TStringStream.Create();
      try
        s.LoadFromStream(r);
        FStatus.SetHtmlFromStr('<html>' +
          '<head>' +
          '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'+
          '<style type="text/css">' +
          s.DataString +
          '</style>' +
          '</head>'+
          '<body>' +
          HTML +
          '</body>' +
          '</html>'
          );
      finally
        s.Free;
      end;
    finally
      r.Free;
    end;
  except
    on E: Exception do MessageDlg('Error:'+ E.Message, mtError, [mbCancel], 0);
  end;
end;

TIpHtmlPanelの対応環境

では、このクラス、どの程度のHTMLに対応しているか?というと、残念ながらそんなに詳細な情報が得られず…。

とりあえずフォーラムに書き込んでみたところ「そんなによくない」とは言われました。

forum.lazarus.freepascal.org

あと、JavaScriptやアニメーションなどの比較的高度なCSS要素には対応していないようです。

このへんについてはソースコードを見てみたところ、たしかに対応してないプロパティは多いっぽい*1

とりあえずしっかりとしたHTML対応を求めるのであれば、fpCEFなどのブラウザコンポーネントの導入を検討してみましょう。

wiki.freepascal.org

wiki.freepascal.org