2013年8月27日火曜日

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 を直接使っているのと変わりなくなります。
つまりはそこが肝で、どううやらこれの使い方を学べばもっと用途に合ったコードが書けそうです。



0 件のコメント:

コメントを投稿