VCLとFMXのフォーム間でアプリケーションテザリング

一般的に言われている「テザリング」とは、スマートフォンや携帯電話を中継点とすることで、他の通信機器（ノートパソコン・タブレット端末・ゲーム機など）をインターネットに繋いで利用できる機能のことです。
Delphi/C++Builder/RAD Studio で提供されている「アプリケーションテザリング」とは、同じマシン、またはリモートマシン(デバイス)で動作しているほかのアプリケーションとやり取りを行い、他のアプリケーションを操作したり、他のアプリケーションとデータを共有したりすることができる機能です。
アプリケーションテザリングの為のコンポーネント TTetheringManager、TTetheringAppProfile が提供されています。
で、このコンポーネントは VCL/FireMonkey(FMX) 共に提供されていて、これを使うと、VCL アプリケーションと FireMonkey アプリケーション間でアプリケーションテザリングってできてしまうわけですよ。

##では、実際に作ってみようか
この記事では、VCL アプリケーションと、この VCL アプリケーションとアプリケーションテザリングを使って通信する FireMonkey アプリケーションの２つを作成します。
FireMoneky アプリケーション側でボタンを押すと、VCL アプリケーション側にセットされているアクションを連動し、動作させるという簡単なものです。１つの PC の中でアプリケーション通信を行なっています。
あーこんな感じに作れるんだーという理解の助けになればいいなぁと

###VCL 側
VCL フォームアプリケーションを作成し、フォーム上に TLabel、TActionList、TTetheringManager、TTetheringAppProfile を置きます。

- ActionListに aUp と aDown というアクションを登録し、それぞれの OnExecute イベントで、Label に「上」「下」の文字をセットするだけのコードを書く
- TetheringManager は、何も設定しません
- オブジェクトインスペクタで、TetheringAppProfile の Actions プロパティに作成した２つのアクション (aUp, aDown) を登録　(追加して既に登録済みの Action を関連付ける）
- オブジェクトインスペクタで、TetheringAppProfile の Group プロパティに任意のグループ名を設定（たとえば G0）
- オブジェクトインスペクタで、TetheringAppProfile の Manager プロパティに TetheringManager を設定

###FireMonkey 側
クロスプラットフォームアプリケーションを作成し、フォーム上に TButton x3、TTimer、TTetheringManager、TTetheringAppProfile を置きます。

- TetheringManagerは、何も設定しません
- オブジェクトインスペクタで、TetheringAppProfile の Group プロパティに任意のグループ名を設定（**VCL側で設定したものと同じ**にする）
- オブジェクトインスペクタで、TetheringAppProfile の Manager プロパティに TetheringManager を設定
- 後は Button x3 の OnClick、Timterの OnTimer のコードを記述するだけ

同じ PC 上で、VCL、FireMonkey 双方のアプリケーションを起動します。FireMonkey 側で通信を開始し、通信可能になったところで、「上」「下」のボタンを押すと、VCL 側の Label に、押したボタンの文字が表示されます。

##サンプルコード（エラー処理は全く考えてないです ^^;;;）
###VCL側
```delphi
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Actions, Vcl.ActnList,
  Vcl.StdCtrls, IPPeerClient, IPPeerServer, System.Tether.Manager,
  System.Tether.AppProfile;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    ActionList1: TActionList;
    aUp: TAction;
    aDown: TAction;
    TetheringManager1: TTetheringManager;
    TetheringAppProfile1: TTetheringAppProfile;
    procedure aUpExecute(Sender: TObject);
    procedure aDownExecute(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.aDownExecute(Sender: TObject);
begin
  // aDown というアクションで実行するコード
  // Label に「下」という文字を表示するだけの簡単なお仕事
  Label1.Caption := '下';
end;

procedure TForm1.aUpExecute(Sender: TObject);
begin
  // aUp というアクションで実行するコード
  // Label に「上」という文字を表示するだけの簡単なお仕事
  Label1.Caption := '上';
end;

end.
```


###FireMonkey側
```delphi
unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, IPPeerClient,
  IPPeerServer, System.Tether.Manager, System.Tether.AppProfile,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Timer1: TTimer;
    TetheringManager1: TTetheringManager;
    TetheringAppProfile1: TTetheringAppProfile;
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { private 宣言 }
  public
    { public 宣言 }
  end;

var
  Form2: TForm2;

implementation

{$R *.fmx}

procedure TForm2.Button1Click(Sender: TObject);
begin
// 同じグループのアプリケーションへ接続する
  TetheringManager1.AutoConnect();
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
// aUp アクションをリモートから実行させる
  TetheringAppProfile1.RunRemoteAction(TetheringManager1.RemoteProfiles[0],'aUp');
end;

procedure TForm2.Button3Click(Sender: TObject);
begin
// aDown アクションをリモートから実行させる
  TetheringAppProfile1.RunRemoteAction(TetheringManager1.RemoteProfiles[0],'aDown');
end;

procedure TForm2.Timer1Timer(Sender: TObject);
begin
// 接続しているかを確認する - Countが 0より上であれば、接続が行なわれている
  if TetheringManager1.RemoteProfiles.Count > 0 then
    Button1.Text := '接続中'
  else
    Button1.Text := '未接続';

end;

end.
```

##おまけの図
今回の記事の別アプリケーションからアクションを操作する仕組みをざくっと図にするとこんな感じになります。
![Tethring.png](https://qiita-image-store.s3.amazonaws.com/0/164551/188a2870-5138-ee83-8dd5-2993b40dce80.png)