複数起動時の処理について

投稿者: 投稿日:2019/09/30(Mon) 23:07:30 No.2504

 こんにちは、お世話になります。

 現在メディアプレーヤーのようなものを作ろうとしています。
 この際、コマンドラインオプションで何かファイルが渡された時に、すでにプログラムが起動されていたらそちらに渡されたファイル名を引き継ぎたいと考えています。
 一般的なソフトでは、ウィンドウメッセージでやりとりするのですが、プロデルではウィンドウメッセージが扱えないため、困っています。
 プラグインを自作して、ウィンドウメッセージが発生した際にイベントを発生させようとしましたが、イベントを発生させる方法がわからず、つまずいてしまいました。
 また、一時ファイルを経由するという方法も考えましたが、できればほかの方法がないかと考えています。
 何かアドバイスをいただけないでしょうか。

 以上、よろしくお願いいたします。

 追記:あまりきれいではないですが、ウィンドウメッセージの受け取り部分をC#で書いたものを貼り付けておきます。
 ちなみに、このプラグインを読み込むと、プロデルが動作停止してしまいます。

----- サンプルソース -----
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Produire;

namespace rdr_msg{
public class メッセージレシーバ : IProduireClass{
private delegate int D_MyWndProc(IntPtr hwnd, int msg, int wParam, int lParam);
private static int GWL_WNDPROC = -4;

[DllImport("user32.dll", EntryPoint = "GetWindowLongA")]
extern static int GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA")]
extern static int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongA")]
extern static int SetWindowLong(IntPtr hwnd, int nIndex, D_MyWndProc dwNewLong);
[DllImport("user32.dll", EntryPoint = "CallWindowProcA")]
extern static int CallWindowProc(int lpPrevWndFunc, IntPtr hwnd, int msg, int wParam, int lParam);

private static int lngWnP;

[自分で]
public void メッセージ処理開始([として]IntPtr ハンドル){
lngWnP = GetWindowLong(ハンドル, GWL_WNDPROC);
SetWindowLong(ハンドル, GWL_WNDPROC, MyWndProc);
}

[自分で]
public void メッセージ処理終了([として]IntPtr ハンドル){
SetWindowLong(ハンドル, GWL_WNDPROC, lngWnP);
}

private static int MyWndProc(IntPtr hwnd, int msg, int wParam, int lParam){
return CallWindowProc(lngWnP, hwnd, msg, wParam, lParam);
}
}

public class MessageEventArgs : EventArgs {
private int _msg;
private int _lParam;
private int _wParam;

public MessageEventArgs(int msg, int lParam, int wParam){
this._msg = msg;
this._lParam = lParam;
this._wParam = wParam;
}

public int msg{
get { return this._msg; }
}

public int lParam{
get { return this._lParam; }
}

public int wParam{
get { return this._wParam; }
}
}
}
----------

Re: 複数起動時の処理について

投稿者:通り道 投稿日:2019/10/03(Thu) 01:07:25 No.2506

回答ではないのですが、一応。

dotnetの場合、多重起動防止と引数の受け渡しには、一般的には
mutexとIPC通信を用います。
メッセージによる方法は、主に、Win32のNativeアプリの手法です。
(WinFormsも、中の仕組みは似たようなもんですが・・・)

mutexとIPCの両機能が、プロデルの命令になるかどうかは未確認です。

また、多重起動にかかわる処理は、できれば、プロデルのソースで
書かれたプログラムが実行されるよりも、もうちょっと手前でチェックと
引数の受け渡しをしてほしいところではあるので、プロデル本体に
対応を要望として挙げておくのも手かと思います。
(通常は、GUIの部品を確保し始めるよりも前で行う)

なお、本題のイベントの作り方は、
public event ProduireEventHandler ユーザセッション終了する;
のような、感じでプロデルから使うイベントを定義しておいて、
コンストラクタで、
this.SessionEnding += (sender, e) => { if (this.ユーザセッション終了する != null) { this.ユーザセッション終了する(sender, new ユーザセッション終了イベント情報(e)); } };
のような感じで、実際のイベントに紐づけています。
もととなるイベントからではない場合、コンストラクタ等での事前設定は不要で、イベントを発行したい時に、適切なsenderとイベント情報を引数に指定して、関数のように呼び出せば、大丈夫です(nullチェックは必要)
(上記は、WPFのApplicationを継承したクラスに使った例です)

Re^2: 複数起動時の処理について

投稿者: 投稿日:2019/10/06(Sun) 17:59:58 No.2508

 こんにちは、お世話になります。

 ありがとうございます。
 やはり現状では難しいですよね。
 一応複数起動を検出して、エラーメッセージを表示するくらいのことであれば、今でもできるんですが、起動中のプログラムに文字列等を送信するというのは難しいようです。
 やはりプロデルに機能として追加していただけるとうれしいんですけどね。

 以上、今後ともよろしくお願いいたします。

Re: 複数起動時の処理について

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/12(Tue) 23:46:41 No.2517

こんばんは。アークです。

例えば、バッチファイル1の内容が「start /b TestApp.exe "X:\MyApp\新規1.txt"」で、
バッチファイル2の内容が「start /b TestApp.exe "X:\MyApp\新規2.txt"」だったとして、
それを順番に起動したら多重起動しないでテキストのパス名だけがTestApp.exeに渡される、
という理解でよろしいでしょうか。

Re^2: 複数起動時の処理について

投稿者: 投稿日:2019/11/13(Wed) 21:01:22 No.2518

 こんにちは。

 そういうことです。
 メディアプレイヤーソフト(WindowsMediaPlayerとか)をイメージしていただくとわかりやすいかと思います。

 それでは。

私にできるのは…

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/14(Thu) 10:26:09 No.2519

こんにちは。アークです。

二重起動を防止しつつ、既に起動しているプロセスにファイル名等を渡すだけなら一応可能です。
ウィンドウメッセージは送出するよりも受け取り方のが難しそうです。
標準出力を利用する方法はタイミングが難しそうです
環境変数を利用する方法は一度プロセスを再起動しないと反映されないようです。
本当はプロセス間で共通に使える変数が有ると良いのですが…。

取り敢えずクリップボードを利用して実現してみました。
ファイル名は「TestApp.exe」としました。
-----------------------------------------------------------
もしプログラムの起動時設定が「」でないなら
  引数は、プログラムの起動時設定
  第一引数は、引数(1)
そうでないなら
  第一引数は、「」
もし終わり
一覧は、「TestApp」のプロセス一覧
インスタンス数は、一覧の個数

クリップボードをクリアする
「[第一引数]」をクリップボードへコピーする

もしインスタンス数=1なら
  メイン画面を表示する
  待機する
そうでないなら
  一覧を要素にそれぞれ繰り返す
    もし要素のタイトルが「」でないなら
      要素へ切り替える
    もし終わり
  繰り返し終わり
  終了する
もし終わり

メイン画面とは
  ウィンドウを継承する
  はじめの手順
    初期化する
    ーー貼り付けた部品に対する操作をここに書きます
  終わり
  初期化する手順
  ーー自動生成された手順です。ここにプログラムを書き加えても消える場合があります
  この実質大きさを{408,125}に変える
  この内容を「メイン画面」に変える
  初期化開始する
  テキスト1というテキストを作る
    その位置と大きさを{12,25,380,19}に変える
    その移動順を3に変える
  ボタン1というボタンを作る
    その位置と大きさを{154,72,85,32}に変える
    その内容を「全て終了」に変える
    その移動順を1に変える
  初期化終了する
終わり
  
  開いた時の手順
    テキスト1の内容を「[第一引数]」に変える
  終わり

  ボタン1がクリックされた時の手順
    一覧を要素にそれぞれ繰り返す
      終了する
    繰り返し終わり
  終わり
  
  引数を更新する時の手順
    ファイルパスは、クリップボードの内容
    もしファイルパスが「」でないなら
      テキスト1の内容をファイルパスに変える
      クリップボードをクリアする
    もし終わり
  終わり

  アクティブ時の手順
    引数を更新する
  終わり
終わり
-----------------------------------------------------------
薦められる方法では有りませんが一応引数の内容を渡せます。

本格的なプロセス間通信を実装するのなら「メモリマップドファイル」を使って、
プロセス間で共通に使える変数を利用するのが良さそうです。
陸さんはCを遣られているので如何でしょうか。

Re: 私にできるのは…

投稿者: 投稿日:2019/11/14(Thu) 21:19:41 No.2521

 こんにちは。

 クリップボードはちょっと使えないですね。
 そもそもすでに何かしらデータが入っていたら、クリップボードの中身が上書きされてしまうので。
 クリップボードに入っているのが文字列とか画像とかだけなら、1回バックアップして、書き戻すこともできるかもしれないですが、ファイルがコピーされていたりすると、かなりめんどうなことになるので。
 それなら一時ファイルを経由させる方がまだましだと思います。

> 本格的なプロセス間通信を実装するのなら「メモリマップドファイル」を使って、
> プロセス間で共通に使える変数を利用するのが良さそうです。
> 陸さんはCを遣られているので如何でしょうか。

 これも試してみました。
 ほぼほぼ要求は達成できましたが、1つ問題があって、メモリに書き込んだ側のプログラムが終了されてしまうと、書き込んだデータは読み込めなくなってしまいます。
 タイマーで定期的に監視しても、多分取得漏れが発生すると思います。

 それでは。

Re^2: 私にできるのは…

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/14(Thu) 21:55:37 No.2522

こんばんは。アークです。

「メモリマップドファイル」には2種類あるようです。
そのまま消えてしまうタイプと終了時にファイルに残すタイプです。
まぁ、ファイルを使いたくないという事であれば残るは「ADS」辺りでしょうか。

Re: 複数起動時の処理について

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/18(Mon) 07:44:32 No.2525

おはようございます。アークです。

既に解決済みかも知れませんがADSによる方法を試してみました。
ウィンドウメッセージでもそうですが通常、
送出する側は受け取る側の状況を考慮しないので、
受け取ったデータを保管する入れ物を用意しました。
使う時は此処から一つずつ取り出して使います。
入れ物は使い終わると空になります。
当方の環境では問題無く機能していますが、
陸さんが希望する条件に合致するかは分かりません。

もう一つ疑似ウィンドウメッセージ方式のも作ってみました。
こちらも問題無く機能しています。
動作確認用のサンプルを下記に積みました。

https://ux.getuploader.com/reg_mem/download/263

Re^2: 複数起動時の処理について

投稿者: 投稿日:2019/11/19(Tue) 22:40:01 No.2526

 こんばんは。

 サンプル試してみました。
 こちらでもうまく動作しています。
 よろしければどのように実装したのか、教えていただけないでしょうか。

 それでは。

こんな感じです

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/20(Wed) 09:56:20 No.2527

こんにちは。アークです。

疑似ウィンドウメッセージ方式の実体は「sendkeys()」です。
起動したら自分と同じプロセスを確認します。
この時点では未だウィンドウは持っていないのでタイトルも無いです。
それを利用して既に起動中のプロセスを見分けています。

今回は処理の複雑化を避ける為に最初の引数だけを扱っていますが、
引数を並べて処理をする事もできなくは有りません。
多重起動チェックで同じプロセスが既に起動していたら、
引数を送り自らは終了します。

問題は「sendkeys()」された引数を受け取る処理ですが、
プロデルの命令には無いようです。
そこで工夫をしてテキストボックスで受けるように細工しています。
そのテキストボックスは裏処理で使うだけなので領域外に追い出して隠しています。

テキストボックスで引数を受け取るとそのイベントを利用してキューに入れています。
これは既に起動していたプロセスの動作状態により直ぐに使えない状況が考えられるからです。
テキストボックスのイベントを使わずにタイマーで監視した方が良い場合も有ると思います。
以下、ソースです。
------------------------------------------------------------------------------------
もしプログラムの起動時設定が「」でないなら
  引数は、プログラムの起動時設定
  第一引数は、引数(1)
そうでないなら
  第一引数は、「」
もし終わり
一覧は、「TestApp」のプロセス一覧
インスタンス数は、一覧の個数

もしインスタンス数=1なら
  メイン画面を表示する
  待機する
そうでないなら
  一覧を要素にそれぞれ繰り返す
    もし要素のタイトルが「」でないなら
      要素へ「[第一引数]|」を入力する
    もし終わり
  繰り返し終わり
  終了する
もし終わり

メイン画面とは
  ウィンドウを継承する
  はじめの手順
    初期化する
    ーー貼り付けた部品に対する操作をここに書きます
    キュー1というキューを作る
    記録器は、文字列書込器を作ったもの
    値=0
  終わり
  初期化する手順
  ーー自動生成された手順です。ここにプログラムを書き加えても消える場合があります
  この実質大きさを{408,125}に変える
  この内容を「メイン画面」に変える
  初期化開始する
  テキスト2というテキストを作る
    その位置と大きさを{12,25,379,19}に変える
    その移動順を11に変える
  ボタン2というボタンを作る
    その位置と大きさを{280,72,85,32}に変える
    その内容を「表示」に変える
    その移動順を10に変える
  テキスト1というテキストを作る
    その位置と大きさを{12,-100,380,19}に変える
    その文字配置を「左」に変える
    その移動順を1に変える
    その背景色を「」に変える
  ボタン1というボタンを作る
    その位置と大きさを{154,72,85,32}に変える
    その内容を「全て終了」に変える
    その移動順を9に変える
  初期化終了する
終わり
  
  開いた時の手順
    テキスト1の内容を「[第一引数]」に変える
    引数を更新する
  終わり
  
  アクティブ時の手順
    テキスト1へフォーカス
  終わり

  ボタン1がクリックされた時の手順
    記録器をクリアする
    一覧を要素にそれぞれ繰り返す
      終了する
    繰り返し終わり
  終わり

  テキスト1の内容が変化した時の手順
    もし(テキスト1の内容の末尾から1文字取り出したもの)が「|」なら
      引数を更新する
    もし終わり
  終わり
  
  引数を更新する時の手順
    もしテキスト1の内容が「」でないなら
      キュー1に「[テキスト1の内容]」を入れる
      テキスト1の内容を「」にする
    もし終わり
  終わり

  ボタン2がクリックされた時の手順
    もしキュー1の要素数>0なら
      テキスト2の内容を(キュー1から取り出したもの)に変える
    そうでないなら
      テキスト2の内容を「」に変える
    もし終わり
  終わり
終わり
------------------------------------------------------------------------------------

次はNTFS代替データストリーム(ADS:Alternate Data Stream)方式です。
ADS自体はフォルダやバイナリも対象になっているので網羅して実装すると大変なのですが、
テキストに限定して個数や名前も固定して使うのなら比較的容易です。
今回は最も簡単な方法で実装してみました。

この方式は先の「sendkeys()」方式とは異なり共有メモリのイメージに近いです。
多重起動のチェック等は先の方式と同じです。
引数はADSの裏ストリームへ一時保存します。

先に起動していたプロセス側はアクティブ時に裏ストリームから読み出してキューに蓄積します。
フォーカスイベント利用しているので完全にバックグラウンドで動かす場合には、
やはりタイマーなどで定期的にチェックした方が良いかも知れません。
どちらの方式も引数を渡す処理以外は手を抜いています。

この方式の条件は使用するストレージのファイルシステムがNTFSで有る事です。
今のウィンドウズは殆どがそうだと思います。
以下がソースです。
------------------------------------------------------------------------------------
もしプログラムの起動時設定が「」でないなら
  引数は、プログラムの起動時設定
  第一引数は、引数(1)
  「echo [第一引数]>"[プログラムの位置]TestApp.exe:argument"」をコマンド実行する
そうでないなら
  第一引数は、「」
もし終わり
一覧は、「TestApp」のプロセス一覧
インスタンス数は、一覧の個数

もしインスタンス数=1なら
  メイン画面を表示する
  待機する
そうでないなら
  一覧を要素にそれぞれ繰り返す
    もし要素のタイトルが「」でないなら
      要素のIDのプロセスへ切り替える
    もし終わり
  繰り返し終わり
  終了する
もし終わり

メイン画面とは
  ウィンドウを継承する
  はじめの手順
    初期化する
    ーー貼り付けた部品に対する操作をここに書きます
    キュー1というキューを作る
  終わり
  初期化する手順
  ーー自動生成された手順です。ここにプログラムを書き加えても消える場合があります
  この実質大きさを{408,125}に変える
  この内容を「メイン画面」に変える
  初期化開始する
  テキスト1というテキストを作る
    その位置と大きさを{12,25,379,19}に変える
    その最大文字数を200に変える
    その選択表示維持を○に変える
    その移動順を11に変える
  ボタン2というボタンを作る
    その位置と大きさを{280,72,85,32}に変える
    その内容を「表示」に変える
    その移動順を10に変える
  ボタン1というボタンを作る
    その位置と大きさを{154,72,85,32}に変える
    その内容を「全て終了」に変える
    その移動順を9に変える
  初期化終了する
終わり
  
  アクティブ時の手順
    テキスト1へフォーカス
    引数を更新する
  終わり

  ボタン1がクリックされた時の手順
    キュー1をすべて消す
    「@PowerShell ^&{Clear-Item -Path '[プログラムの位置]TestApp.exe' -Stream argument}」をコマンド実行する
    一覧を要素にそれぞれ繰り返す
      終了する
    繰り返し終わり
  終わり
  
  引数を更新する時の手順
    結果は、「more < "[プログラムの位置]TestApp.exe:argument"」をコマンド実行したもの
    「@PowerShell ^&{Remove-Item -Path '[プログラムの位置]TestApp.exe' -Stream argument}」をコマンド実行する
    もし結果が「」でないなら
      キュー1に「[結果]」を入れる
    もし終わり
  終わり

  ボタン2がクリックされた時の手順
    もしキュー1の要素数>0なら
      テキスト1の内容を(キュー1から取り出したもの)に変える
    そうでないなら
      テキスト1の内容を「」に変える
    もし終わり
  終わり
終わり
------------------------------------------------------------------------------------

Re: こんな感じです

投稿者: 投稿日:2019/11/23(Sat) 11:55:36 No.2528

 こんにちは。

 うーん、なかなか難しいですね。
 もうちょっと頑張ってみます。
 ありがとうございました。

 それでは。

Re^2: こんな感じです

投稿者:アーク <info.nds.laboratry あっとまーくgmail.com> 投稿日:2019/11/23(Sat) 21:14:46 No.2530

こんばんは。アークです。

質問等有りましたら遠慮なくどうぞ。
プロデルとは関係ない内容でしたら私のブログの方でも構いまいません。

▲ページの先頭へ

- WebForum -