入出力ストリームをStreamRelay.NET.exe のプラグインとして実装する方法について説明する

---
### 入出力ストリームをStreamRelay.NET.exe のプラグインとして実装する方法について説明する

StreamRelay.NET.exe/StreamRelay.NET.x86.exeには、いくつかの機能をプラグインとして実装できるようにインターフェイスを公開している。

今回は、標準入出力(オプションだと「**-[Local|Remote]Port 0**」の場合)のリダイレクト先のストリームをプラグインのインターフェイスとして公開していて、そのプラグインを作成する方法を記述する。

---
### 既存のプラグイン

ファイルから入力/出力とか、マイクから音声入力とか、スピーカーへ音声出力とか、音声認識とか、音声合成とか、名前付きパイプとか、指定されたURLへ送信とかそういうところに使われている。

プラグイン的には、このあたり。

- Plugins\Win32\stdioFileC32.ddl
- Plugins\Win64\stdioFileC64.ddl
- Plugins\Win32\stdioSoundRecordMDX.ddl
- Plugins\Win32\stdioSoundRecordSlimDX.x86.ddl
- Plugins\Win64\stdioSoundRecordSlimDX.x64.ddl
- Plugins\stdioFile.ddl
- Plugins\stdioICMPTunnel.ddl
- Plugins\stdioNamedPipe.ddl
- Plugins\stdioSpeachRecognition.ddl
- Plugins\stdioTCP.ddl
- Plugins\stdioUrl.ddl
- Plugins\stdioUrlWS.ddl

---
### 実装したプラグインの呼び出され方

「**-[Local|Remote]Port 0**」が指定されて、ストリームの物理実体が標準入出力となっている状態で、プラグインで指定された引数が渡されると、標準入出力のリダイレクト先として、プラグインが呼ばれる。

という感じ。

---
### 実装するインターフェイス

以下のインターフェイスを継承したクラスを実装すればよい

- IPlugin.dll 中の jp.dip.rocketeer.Plugins.IPluginStdioout インターフェイス

このクラス一つだけ実装すればよい。

---
### jp.dip.rocketeer.Plugins.IPluginStdioout インターフェイス

StreamRelay.NET.exeが、引数/オプションの解析処理中に呼び出して、引数の指定内容によって有効化していれば、
標準入力を置き換える(リダイレクト)する場合は、プロパティ「**CanInputStream=true**」とする。
標準出力を置き換える(リダイレクト)する場合は、プロパティ「**CanOutputStream=true**」とする。

そうすると、StreamRelay.NET.exeで、(標準入出力の代わりに)ストリームを開く際に、入力を置き換えた(リダイレクト)場合には**getInputStream()**、出力を置き換えた(リダイレクト)場合には**getOutputStream()**が呼ばれる。

という感じ。

具体的には、起動直後の引数/オプションの解析処理中に、
**int Args(String iStr1, String iStr2, String iStr3);**
を呼び出すので、自分の引数であれば、プロパティ「**CanInputStream=true**」または「**CanOutputStream=true**」にして、使った引数の数を返してくれればよい。
自分の引数でなければ、0を返す。

より、具体的には、
例えば、
`c:\>StreamRelay.NET.exe a b c d e f`
というコマンドラインで起動したら、

1. iStr1=a, iStr2=b,　iStr3=c, で、Args() を呼び出し、
2. iStr1=b, iStr2=c,　iStr3=d, で、Args() を呼び出し、
3. iStr1=c, iStr2=d,　iStr3=e, で、Args() を呼び出し、
4. iStr1=d, iStr2=e,　iStr3=f, で、Args() を呼び出し、
5. iStr1=e, iStr2=f,　iStr3=NULL, で、Args() を呼び出し、

という感じ・・・

例えば、iStr1=b, iStr2=c,　iStr3=d, の時、自分の引数ということで、iStr1=b, iStr2=c, を Args() で使用したら、2を返す。すると次は iStr1=d, iStr2=e,　iStr3=f, で、Args() が呼び出される。

ということになっている。

---
#### Args()の呼ばれ方の留意点

Args()の第一引数の先頭が「-Local」または「-Remote」の場合だけ、Args()は呼び出される。

しかも、先頭の「-Local」または「-Remote」は削られた形で呼び出されるので注意。

より、具体的には、
例えば、
`c:\>StreamRelay.NET.exe -LocalStdinRedirect b c d e f`
というコマンドラインで起動したら、

1番最初だけ、「StdinRedirect」というように「-Local」または「-Remote」が削られて、結局「StdinRedirect」「b」「c」という引数でArgs()が呼ばれる。

ということになる。

---
#### jp.dip.rocketeer.Plugins.IPluginStdioout インターフェイスで定義しているメソッド/プロパティ

実装する必要があるのは以下

jp.dip.rocketeer.Plugins.IPluginStdiooutインターフェイスで定義されているのは、以下のとおり

<dl>
 <dt><b>String[] usage { get; }</b>
  <dd>ヘルプに引数を表示する
 <dt><b>int Args(String iStr1, String iStr2, String iStr3)</b>
  <dd>引数を解析して、自分のものだったかどうかを返す(「名前」「 値」の二つを必要とするバージョン)<br>第一引数の「-Local」または「-Remote」は削られて渡される
 <dt><b>String InputStreamName { get; }</b>
  <dd>現在の状態を表示する(設定を受け取っての状態(Verboseモード時や接続状態の表示時に呼び出される))(入力側)
 <dt><b>String OutputStreamName { get; }</b>
  <dd>現在の状態を表示する(設定を受け取っての状態(Verboseモード時や接続状態の表示時に呼び出される))(出力側)
 <dt><b>Stream getInputStream()</b>
  <dd>ストリームを取得する(入力)<br>サーバ系統の場合(ローカル側で IsServer=True)は、接続ごとにこのメソッドが呼び出されるようなイメージ<br>よって、呼び出しごとに新規ストリームを返すというようなイメージ
 <dt><b>Stream getOutputStream()</b>
  <dd>ストリームを取得する(出力)<br>サーバ系統の場合(ローカル側で IsServer=True)は、接続ごとにこのメソッドが呼び出されるようなイメージ<br>よって、呼び出しごとに新規ストリームを返すというようなイメージ
 <dt><b>Boolean CanInputStream { get; }</b>
  <dd>InputStream が利用可能かどうかを示す
 <dt><b>Boolean CanOutputStream { get; }</b>
  <dd>OutputStream が利用可能かどうかを示す
 <dt><b>Boolean IsLocal { get; set; }</b>
  <dd>ローカルか、リモートか (StreamRelay.NET.exeがセットする)
 <dt><b>UInt32 SleepTime { get; set; }</b>
  <dd>スレッドのインターバル(ms) (StreamRelay.NET.exeがセットする)
 <dt><b>UInt32 RecvBufSize { get; set; }</b>
  <dd>バッファサイズ (StreamRelay.NET.exeがセットする)
 <dt><b>Encoding InputEncoding { get; set; }</b>
  <dd>文字コード(必要な場合) (StreamRelay.NET.exeがセットする)
 <dt><b>Encoding OutputEncoding { get; set; }</b>
  <dd>文字コード(必要な場合) (StreamRelay.NET.exeがセットする)
 <dt><b>Boolean IsSameStream { get; set; }</b>
  <dd>ストリームは入力だけ、出力だけではなくて、入出力の時、True。<br>True の場合、output 系統のインスタンス側のメソッドは呼び出されない。
 <dt><b>Boolean IsServer { get; set; }</b>
  <dd>サーバ(複数クライアントの多重接続)として動作させるかどうか
 <dt><b>Stop()</b>
  <dd>サービスとして動作しているような場合の終了処理を記述する<br>例: ローカルで、TCPサーバとして起動している場合のサーバの停止処理<br>接続中のストリームの停止処理は、別途しているので、待ち受けサーバの停止処理だけ記述すればよい
 <dt><b>void init(PluginManagePublicObj iObj)</b>
  <dd>他のプラグイン(乱数とか文字コードとか)も操作できるオブジェクトを渡す (引数はStreamRelay.NET.exeがセットして呼び出される)
</dl>

jp.dip.rocketeer.Plugins.IPluginBaseインターフェイスで定義されている分については、[インターフェイス IPluginBase について](https://qiita.com/tomoki0sanaki/items/60613c29f0d627353e67)を参照

---
### 目次へ戻る

[目次というか最初の一歩](http://qiita.com/tomoki0sanaki/items/85f20cbe18378e33cd6f)