2013年8月29日木曜日

構築やコンパイルをした後に自動的にコマンドを実行

構築やコンパイルをした後に自動的にコマンドを実行することができます。
各構築モードごとに設定できます。

リリースモードを指定してコンパイルした時のみ、コンパイル済みのファイルをリリース用のディレクトリにコピーしたりなど便利に使えそうです。

コマンドは、「コンパイラオプション」の「コンパイル」で指定できます。
コマンドプロンプトで使えるコマンドは一応そのまま使えそうです。
実行結果は、IDE のメッセージウィンドウで確認できますが、文字化けします。
コマンドが出力したメッセージを正しく表示できないようです。
なので、バッチファイルを作り、コードページを変更したほうが良さそうです。
しかし、コードページ 932 (日本語)、65001 (UTF8) のどちらでも文字化けしてしまいました。
chcp 437ascii を指定し、結果は英文で受け取るしかなさそうです。

以下は設定とバッチファイルの例です。

設定の例

次の後の実行」の「コマンド」の欄に記述します。
$ProjPath()\_release.bat $TargetFile()

$ProjPath()でプロジェクトディレクトリがコマンドに渡ります。
$TargetFile()でコンパイルされてできたバイナリのファイル名が渡ります。

$ProjPath()等のマクロはメニューの「ツール」→「外部ツールの設定」→編集または追加→「マクロ」の欄 で一覧が確認できます。

バッチファイルの例
_release.bat で保存
@echo off
chcp 437

set RELEASE_DIR=D:\MyTOOLS\64bit\tcesyn\

if not exist %RELEASE_DIR%NUL goto err
echo copying...
C:\Windows\System32\xcopy.exe /Y "%1" %RELEASE_DIR%
echo completed.
goto eof

:err
@echo %RELEASE_DIR%
@echo That directory not found.

:eof

以上です。

Lazarus IDE


画像は Lazarus 1.0.10 の見た目。

2週間ほど使ったのでその感想でも書いてみようかと。

Windows CE,Linux,Ubuntu,unix,arm,Android,iOS となんでも行けるようです。
試してはいませんが。

IDEの起動がメモ帳並に速いので、けっこう気楽に落とせます。
次に開いた時も、表示していたコードの位置も覚えててくれるので、起動したままであったかのように継続して作業できます。
便利すぎます。

コンパイルは Delphi に比べれば遅いものの、他と比べれば速いほうでしょう。
旧Borland は凄かった。

Delphi 同様、コンパイルしたものが単体で実行できるように作ることができます。
作ったものの実行速度も速いほうだと思います。
特に最近の某フレームワーク依存のものに比べると軽く感じます。

コンポーネントパレットは、昔のDelphi よりも充実してます。
(今のDelphi は分かりません)
無いものも探せばそこそこあります。
Delphi の資産もけっこう使いまわせるようです。
何かに依存したつくりでなければ、そのままコンパイルできるようです。
Delphi ソースを変換する機能がついています。

ウィンドウは MDI風でもなく、各ウィンドウもドックできません。
と、他と見劣りするかもしれません。
最初は私も戸惑いましたが、慣れとは怖いもので。
2週間ほど使った結果、まったく気にならなくなりました。
MDI はもともと好きではないんで、むしろ好ましく思います。

ドッキングできたようです。
こちらで紹介しています。
http://laznyan.blogspot.jp/2013/09/lazarus-ide.html

マルチディスプレイで使ってると、あちこちウィンドウを動かします。
どうも、それをシングルディスプレイにした時に、外に出ているウィンドウが戻ってきてくれません。
なので、準備運動がてらにウィンドウ再配置ツール的なものを最初につくったほうが良さそうです。


すでに 1.0.12 があるようですが、まだ未確認です。

PS
2013/09/10 ドッキング対応について追記しました。

2013年8月28日水曜日

Lazarus での Memory Leak チェック

Lazarus IDE での Memory Leak のチェック。
Delphi では FastMM にお世話になりましたが。

プロジェクトオプション→コンパイラオプション

デバッグ中: (上段)
  実行時エラーの後方探索で行番号を表示 (-gl) をチェック

デバッグ中: (中段)
  Heaptrcユニットを使用 (-gh) をチェック。






以上で heaptrc を uses に書かなくても heaptrc が使われるようになり、SetHeapTraceOutput等が使えるようになります。

GDBのためのデバッグ情報生成もチェックしておいてください。


これだけだと、アプリケーション終了時にメッセージダイアログで情報が表示されてしまいます。

プロジェクトファイル(.lpr) の 冒頭に (beginの後)

  if FileExists('heap.trc') then DeleteFile('heap.trc');
  SetHeapTraceOutput('heap.trc');

を追加しておきます。
こうすると heap.trc が出力されるようになります。
heap.trc はメモ帳等で開いても読めますが、それを IDE のメニューの「ツール」→ Leak View から読み込むと heap.trc の内容をIDEで表示してくれます。

項目をクリックするとその行にジャンプもできます。

なお、FileExists は SysUtils を要求します。

モードの構築で、Memory Leak Check などを定義し(前の記事参照
-dLEAKCHECK を「その他」のカスタムオプションに設定し、前述のオプションも含めてしまい

uses
{$IFDEF LEAKCHECK},SysUtils{$ENDIF}

begin の後に
{$IFDEF LEAKCHECK}
  if FileExists('heap.trc') then DeleteFile('heap.trc');
  SetHeapTraceOutput('heap.trc');
{$ENDIF}

としておけば、切り替えも簡単です。


関連


構築モード で DEFINE を指定する。(あるいはコード中から構築モードを検出する)

Lazarus の コンパイラオプションの「モードの構築」から構築モードに

「リリース」



「デバッグ」

を作ったとします。


それをコード中から検出する事はできませんが、

コンパイラオプションの「その他」のカスタムオプションで DEFINE を指定することができます。

なので、

構築モードが「デバッグ」 の時は、「その他」のカスタムオプションに

-dDEBUG

と書いておけば {$DEFINE DEBUG} した事になるようです。



コード中から

{$IFDEF DEBUG}
writeln('This is debug.');
{$ENDIF}

のようにして間接的に構築モードを検出することもできます。

そうなると、DEFINE は DEFINEで、構築モードは構築モードで別々に考えてコードを書いても問題ないという事にもなります。


2013年8月27日火曜日

Windows OSが 64bit かどうか判定する。

64bit の Windows で実行中のアプリケーションから OS が 64bit かどうか判定する。

手順は

 http://msdn.microsoft.com/en-us/library/windows/desktop/ms684139(v=vs.85).aspx



 http://www.delphigroups.info/2/5/795988.html

等が見つかります。

が、これは 32bit アプリケーションが Windows の WOW64 上で実行されているかどうかの判定しかできません。

実行中のアプリケーションが64bitの場合 kernel32 の IsWow64Process は False を返します。

当然と言えば当然なのだけどしばらく悩みました。

Lazarus に移植した古臭い起動環境判定のユニットの一部の関数が 32bit としか返さないので、なぜなのかと読み返したらそういうことでした。

ということで、IsWow64Process に頼って OS が 64bit かどうか判定しているような場合は問題となります。

Lazarus,FPC みたいにクロスプラットフォーム対応で、Windows でも 32bit 版と64bit版が用意されているような開発環境では配慮したほうが良さそうです。

で、 64bit かどうか判定するには SizeOf で pointer 等のサイズを求めればOKのようです。
コンパイラオプションでサイズが変わる可能性がある型は避けたほうが良いように思います。

case SizeOf(pointer) of
 4: Result:='32bit';
 8: Result:='64bit';
end;

ちなみに各環境での結果は以下の通りです。

アプリ, OS, SizeOf結果, IsWOW64Process
32bit アプリケーション, 32bit Windows OS : SizeOf(pointer)=4, IsWow64Process=False
32bit アプリケーション, 64bit Windows OS : SizeOf(pointer)=4, IsWow64Process=True
64bit アプリケーション, 32bit Windows OS : ダイアログでエラー表示。起動しない。
64bit アプリケーション, 64bit Windows OS : SizeOf(pointer)=8, IsWow64Process=False

普段から SizeOf で型のサイズを使ってバッファのサイズを計算する習慣というのが Pascalの世界ではあちこちで推奨されていたはずなので判定の意味はないかもしれません。

判定などせずともコンパイラを変えればそのまま動くはずです。
リソースから32bit用、64bit用、それぞれのデータファイルを環境に応じて出力するような場合とか、API切り替えたり、OS環境そのものを表示する場合ぐらいにしか困ることは無いのかもしれません。


旧式からの移植は危険かもしれません。
たまたま動いているだけな気がします。
こんなのは氷山の一角なのでしょうね。


Lazarus, Unique Instance, CreateMutex

旧式のエディタと同様にインスタンスを唯一にしようと思いました。

その辺のコードも旧式から使いまわせそうでしたが、Ubuntuでも使おうと思い始めたので、今以上に Windows 寄りになってしまうと少々困ります。
すでに結構課題がたまっている状況・・・。

CreateMutex は問題なく機能し、FindWindow 等も使えるらしくコンパイルは通りますが

どうも FindWindow が 0 しか返してくれません。

まあいいや。

http://wiki.lazarus.freepascal.org/UniqueInstance

によると

https://code.google.com/p/luipack/

の uniqueinstance-1.0.zip というのがあります。

動作テストでは、ほとんど張るだけで OKでした。

uniqueinstanceは、unix と windows で動くようです。
うーん。ubuntu に対応できるか不安だがなんとかなるでしょう・・・。

uses に uniqueinstance を加えて

Identifier にユニークな名前を与えて Enabled を True にするだけです。

デザイナで張るならパッケージを Lazarus IDEに加えて IDE を再構築する必要があります。
必要なければプロジェクトに加えるだけでOKです。

UniqueInstanceOnOtherInstance の イベントハンドラで起動オプション(コマンドラインパラメータ)を受けとれます。

Enabled の実態が FEnabled になっており動的な切り替えによる再初期化やクリーンナップ等は実装されていません。

そのままだとデザイン時に張られていて Enabled=Trueになっていないと動きません(恐らく)

TComponent.Loaded を override して、そこで初期化等をしている模様です。
これを継承して Enabled が設定されたタイミングで Loaded が呼ばれるように変更します。

Reload だけ直に呼べばいいようにも思います。

  TUniqueInstanceLateLoad=class(TUniqueInstance)
  private
    function GetEnabled: boolean;
    procedure SetEnabled(AValue: boolean);
  protected
    procedure Loaded; override;
    procedure ReLoad;
    property Enabled: boolean read GetEnabled write SetEnabled;
  end;

implementation

function TUniqueInstanceLateLoad.GetEnabled: boolean;
begin
  Result:= inherited Enabled;
end;

procedure TUniqueInstanceLateLoad.SetEnabled(AValue: boolean);
begin
  if GetEnabled<>AValue then
  begin
     (inherited Enabled):= AValue;

     Assert(AValue);

     // FEnabled=True のタイミングで Loaded を実行
     Reload;
  end;
end;

procedure TUniqueInstanceLateLoad.Loaded;
begin
  // nothing
end;

procedure TUniqueInstanceLateLoad.ReLoad;
begin
  inherited Loaded;
end;  

これでデザイナを使わずに動的な生成に対応できます。

  FUnique:= TUniqueInstanceLateLoad.Create(Self);
  FUnique.Name:='UNI01';
  FUnique.UpdateInterval:= 1000;
  FUnique.OnOtherInstance:=UniqueInstanceOnOtherInstance;
  FUnique.Identifier:= 'unique_name'
  FUnique.Enabled:=True;

それだけ。

UniqueInstanceOnOtherInstance のイベントハンドラの Parameters: array of string は、0 オリジン。

他に関数バージョンのuniqueinstanceraw があります。


function InstanceRunning(const Identifier: String; SendParameters: Boolean = False): Boolean;

こちらは指定したインスタンスが既存かどうか確認できます。

if IstanceRunning('MYAPP',  True) then
begin
  Application.Terminate;
end

メインユニット(プロジェクトファイル)なんかで使うと便利そうです。
SendParamaters は True にするとコマンドラインパラメータを既存のプロセスに送ってくれます。(はず)


細かいことをしたい場合は、TSimpleIPCServer等を TUniqueInstanceを参考にクラスとか作って使ったほうがよさそうです。

PS
やっぱり、機能単位でメソッドやプロパティを分離して再配置することにしました。
フォーラムなどを読んでいると、もともと旧バージョンはそのような使い方をしていたらしく、現状における最新版は、さらに簡単に使えるようにしたもののようです。
なので、uniqueinstance.pas と uniqueinstanceraw.pas を参考に別のクラスを作ったほうが制御しやすいと思いました。

PS2
このユニット、windows 限定で使おうとするなら
SimpleIPCWrapper.InitServer(FIPCServer); と
SimpleIPCWrapper.IsServerRunning(Client); は
使わずに単に
FIPCServer.StartServer; と
Client.ServerRunning; でよさそうです。

ということはSimpleIPCWrapperは必要なくなり
simpleipc を直接使っているのと変わりなくなります。
つまりはそこが肝で、どううやらこれの使い方を学べばもっと用途に合ったコードが書けそうです。



2013年8月20日火曜日

コードエディタ

Delphi 3 ぐらいから作って使ってた およそ15年ぐらい使い続けたエディタが、Windows 8ではいろいろと問題がでてきて、Windows 8 pro 64bit用のコードエディタがほしくて、Lazarus で作っていたのだけど。

基本機能はほぼ実装できたように思う。



外観もほぼ同じ。
前の: http://eisen-japan.blogspot.jp/2013/08/blog-post_3604.html


アイコンなどは使わない方針。
タブが左側にあるのが特徴。

エディタの機能は SynEdit の機能がそのまま全面に出ている。
私は、文字コードの選択と、分割とクローンさえあればOKなので、それしか追加の機能はない。
検索と置換、あとは、SynEdit 由来の Hilighter と CTRL+J などのシンクロエディットと、マクロの記録再生などを実装。


コマンドプロンプトやメールアクセス機能は、この際なくてもいいかと思うので、無しにしようかと。
時代にあってないし。

サテライトビューというか広範囲ビューも、10年以上使って、おそらく総計数時間しか使ってなかったんでいらない気もする。
とはいえ、何かしらそれに代わる機能がほしいような気もするので構想中。

印刷機能も、前のはあったけど、結局印刷したのは数回だった気がするので未実装。

Lazarus の SynEdit には、HTMLエクスポート機能などのコンポーネントもあるので。
それらも利用するかどうか検討してもいいかもしれない。

Delphi 用に作った XMLパーサが、ほぼそのまま動くようだったので利用。
それゆえ 各種情報管理に TStringList を使わなくなったせいか、前のより起動・終了もかなり軽い。
マクロなども、前のより統制が取れて使いやすくなった。
検索・置換なども前のは、ほぼD3時に書いたコードをそのまま放置だったけど。
今回のは書き直したので、だいぶマシになった。

クローンからドックする時も ウィンドウハンドルの再設定するのではなく、新しくエディタのインスタンスから作成しなおす方針で実装。

一度作ったインスタンスを使いまわしたほうが初期化少なくて軽かろうと当時は思っていたのだけど。
ウィンドウハンドルの再設定時に初期化処理等で、メッセージが飛びまくるゆえ、各種ロックとアンロックを繰り返しながら1つ1つ処理していくため、かえって処理に時間がかかっていた。

ので、だいぶ軽くなり、ドック時のちらつきなどもなくなった。

あとは、履歴ぐらいがあればOKか。
そういや、バイナリエディタはどうするか・・・。

2013年8月16日金曜日

Lazarus で String と PChar を扱う場合の注意

環境:Lazarus IDE v1.0.10,FPC v2.6.2,-MDelphi

文字列関係は、まあいろいろあって面倒くさい。

Delphi 5 - 2007 → Lazarus で String と PChar を扱う場合の注意。

function test: string;
var
 tmp: String;
 s,e,p: PChar;

begin

 tmp:='AA';

 s:= @tmp[1];          //開始
 e:= s + length(tmp);  //終了
 p:= s;                //走査用

 while p&lt;e do
 begin
   if p^='A' then
      p^:='B'
   else
      p^:=chr(byte(p^)+1); //C,D,E,F....
   inc(p);
 end;
 Result:= tmp;
end;

この関数は、毎回 "BB" を返すことを想定している。
関数を呼び出す度に tmp は "AA" になるので結果は "BB"になる。

Delphi 5 だと想定通りの結果を返す。

何度呼び出そうとも tmp は毎回1度は"AA"になり、結果 Result は "BB" となる。

ところが Lazarus だと 1回目のtest()呼び出し時には 結果 Result は "BB" となるが 2回目以降 の test()呼び出し時には、結果 Result は "CC"~ になってしまう。
つまり "AA" それ自体が "AA" ではなくなってしまう。

function test: string;
const
  def='AA';
begin
  tmp:=def;

としても、同じように、2回目以降、結果 Result は "CC"~ が結果となる。
(const は interface部に書いても同様の結果。)
つまり const def ='AA' としようとも"AA"自体が書き換えられてしまう。

これを回避するには

tmp:='AA';


ではなく



move('AA',pointer(tmp)^,2);

のように毎回 "AA" を別の所(固定値のアドレスが異なるように配慮し)与える必要があるようだった。

このようにも書ける。

p:=s;
while p&lt;e do

begin
  p^:='A';
  inc(p);
end;


調べたところ単に


tmp:=PChar('AA');

とするだけで期待通りの動作になる。


とりあえず、固定値を定義して、PCharとかpointerでアクセスして値を書き換えるようなコードはすべて注意する必要がある・・・という事。

ということで、私と同じようにハマったらこの記事を思い出してみてください。

以上。

http://bugs.freepascal.org/view.php?id=22534

http://bugs.freepascal.org/view.php?id=10341

ということらしい。

なんだか Pascal の伝統が1つ失われたような気がしないでもない。

p.s.
この記事は、別の私的なブログから改訂しつつ移動しました。

Width = ClientWidth な問題

Lazarus (Windows 8 pro,Lazarus 1.0.10, FPC 2.6.2)
では TForm などの Width と ClientWidth が等しいようです。

仕様とのこと。

今後 Delphi 互換性向上の対策などもあり、ソースをたどると既にそれらしきメソッドなども用意されています。
(対策済みなのかどうなのか。確認してないので各自で確認してください。)

私的には適当にデザインしたり動的にコントロール作成したりして使ってる分には、これといって不都合は今のところありません。

なぜ不都合がないのか不思議なのだけど、それはそれでつじつまが合っているようです。
あまり深くは考えないことにしましょう。

ウィンドウを並べたい場合には、やはり問題が生じ、単に Left + Width の位置に新しいウィンドウを作成して配置しただけでは、ウィンドウの枠が大きく重なってしまいます。


とりあえず。

ウィンドウを横に二つ並べる場合、以下のコードで解決するので書いておきます。

procedure TForm1.Example;
var
  borderfix:= GetSystemMetrics(SM_CXFRAME);
  x, y : integer;
begin
  x:= Left + Width + borderfix;
  y:= Top;
  Form2.SetBounds( x, y, Width, Height);
end;

これできっちりそろいます。(Windows 8)

以上。

一部のヒントを非表示にする

example.pas(1054,24) Hint: Parameter "Target" not used

Lazarus IDEのメッセージウィンドウに、このようなヒントが表示される場合があります。

クラスのプライベートプロパティにメソッドポインタならぬ関数ポインタをもたせて、他のパラメータに応じて、関数を切り替えて使うような場合があるとします。

このような場合、引数を削ってしまうわけにはいかないので、何とかそこだけヒントを無効にしたいところです。

interface
type
  TProc = function (Target: TObject): integer;

implementation

function DefProc( Target: TObject): integer;
begin
   Result:= 0;
end;

function Proc01( Target: TObject): integer;
begin
   Result:= 0;
   if Target is TCompoment then Result:= 1;
end;

procedure Main;
var
  Proc: TProc;
begin
   Proc:= DefProc;

   if Mode=1 then Proc:=Proc01;

  Proc( object);
end;

こんな場合に、DefProc の Target が使われていないとヒントが出ます。

コンパイラオプションからヒントを無効にしてしまうと、他のヒントもごっそり表示されなくなってしまうので、それは避けねばなりません。

{%H-} を Target の頭につければ以後それに関してのヒントのみが表示されなくなります。

function DefProc( {%H-}Target: TObject): integer;

なお、Sender も同様に使われない場合が多々ありますが、Senderに限っては表示されません。
Sender が使われていない旨も表示させることもできますが、デフォルトではプロジェクトオプションで抑制されています。(画像内の右上の項目に注目)
昔は出ていたようです(?)





以上。

プロジェクトオプション(デバッグ情報の生成など)

Delphi のデバッグ情報の生成に相当するのは Lazarus では

  プロジェクトオプション→コンパイラオプション→「GDBのためのデバッグ情報を生成」



「GDBのためのデバッグ情報の生成」のチェックを外すと実行ファイルサイズが 40Mぐらいのだったものが 5M ぐらいになります。
(新規プロジェクト作成時、デフォルトでデバッグ情報の生成がオンになっています)

Delphi だとデバッグ情報を付加してもそこまで大きくはなりませんが、Lazarus は大きめになってしまうようです。(Delphi も TD32デバッグ情報を付加すればもっと大きくなりました)


{$IFOPT D+}
{$ENDIF}

なども Delphi 同様に利用できます。

他に、「モードを構築」というのがコンパイラオプションの所にあって、「情報をより多く表示」にある各種警告文とうの表示の状態や、デバッグ情報の生成などのコンパイラオプションの状態をひとまとめにして管理できます。

リリース用
デバッグ用

などで構築モードを作成しておけば、ちまちま設定を切り替える必要もないので便利です。
なお、構築モードのアクティブ状態を切り替えながらオプション設定を変更しても、それぞれの状態を記憶してくれています。
(最後にOKを押さないと、閉じたときに忘れられてしまうので注意が必要)




こちらはサイズには影響ないようです。(たぶん)



デバッグ情報を表示(-vd) や 全てを表示(-va) 等、右側にある項目はすごい量のメッセージが表示されます。
コンパイル速度もかなり落ち込むので、必要なければオフにしていたほうが良さそうです。
行番号の表示(-v) などは特に問題ありません。

構文モード
Delphiモード(-Mdelphi)にすると @ の扱い方などが変わるようです。
Object Pascal (-Mobjfpc) のままでも、Delphi をやっていた方なら特に問題ないと思います。
新しく作成するときは Delphi モードでは作成しないほうがよろしいかとも思います。
なぜなら、あちこちに点在するコード資産は -Mobjfpc のほうが多いようです。
なので、 @ の扱い方などがコードのファイル単位で変わってしまうと混乱してしまうのではないかと・・・。



コード生成。
おなじみのコンパイラオプションもあります。




コンパイラオプション
http://wiki.lazarus.freepascal.org/IDE_Window:_Compiler_Options/ja

詳細
http://www.freepascal.org/docs-html/user/usersu68.html#x105-1120006.11.4

一覧
http://www.freepascal.org/docs-html/user/usersu68.html#x105-1120006.11.4

けっきょくの所足りない部分は FPCのほうのマニュアルにたどり着くようです。
歴史があるんですね。
そういえば、FPCの存在自体は知っていたような気もします。
ただ Delphi があったから興味がわかなかっただけだったような。

以上。


2013年8月15日木曜日

Laz にゃん

Lazarus と FPC 関連の私的メモなどを投稿するブログ(というよりアーカイブ)。
現時点で私の環境が Windows 8 Pro 64bit のみなので他の環境は今の所対象外です。

・内容は無保証です。
・各自の責任でご利用ください。
・気まぐれでよく編集しなおします。
・誤字脱字がいっぱいあります。
・意味が逆転していることもあります。

ブログの名前にあわせて猫かぶってなるべくかわいく書く方針に決定しました。

以上。