アプリケーションテザリング解説

アプリケーションテザリングとは

他のアプリケーションとデータを共有したり、他のアプリケーションを操作したりすることが簡単にできるコンポーネントです。

次のようなアプリケーションを簡単に作成できます。

パワーポイントのスライドをスマートフォンから操作する

Windowsパソコンのパワーポイントのスライドを、スマートフォンのアプリケーションから操作するアプリケーション。

アクションを共有する機能を使って、数行のコードを書くだけで作成できます。

_images/002.gif

スマートフォンのカメラの映像をパソコンに表示する

スマートフォンに表示している映像をパソコンの画面に表示するアプリケーション。

データを共有する機能を使って、簡単に作成できます。

_images/003.gif

さまざまな端末でグループチャット

Windows・MAC・iPhone・iPad・Androidスマートフォン・Androidタブレットなど、さまざまな端末で動作するチャットアプリケーションを一つのコードで作成できます。

複数の端末と共有することも簡単にできます。

_images/005.gif

コンポーネント

使用するコンポーネント

アプリケーションテザリングでは、次の2つのコンポーネントを使用します。

_images/001.gif
  • TTetheringManager

    プロファイルの接続の管理を行います。

  • TTetheringAppProfile

    共有する機能や資源の管理を行います。

アプリケーションテザリングの機能

アプリケーションテザリングを使用すると、次のことを簡単に実現できます。

  • アプリケーションテザリングを使用している他のアプリケーションを検出して接続する
  • アクションをリモートで実行する
  • データを共有する

アプリケーションテザリングを使用している他のアプリケーションを検出して接続する

同一端末または同一LAN上で動作している他のアプリケーションを検出して接続できます。

接続元のアプリケーションも接続先のアプリケーションもIPアドレスやポート番号を意識する必要はありません。 パスワードによる接続の認証も可能です。

TTetheringManagerのAutoConnectメソッドは、検出から接続までのすべての処理を行います。 AutoConnectメソッドを呼び出すだけで、他のアプリケーションに接続できます。

TTetheringManagerのDiscoverManagersは検出だけを行います。 検出されたアプリケーションの中からどのアプリケーションに接続するかはプログラマが記述します。

アクションをリモートで実行する

他のアプリケーションのTActionを実行することができます。

この機能を使うことで、次のようなアプリケーションを簡単に作成できます。

  • パソコンの動画をスマートフォンから操作する
  • パワーポイントのスライドをスマートフォンから操作する

TTetheringAppProfileのActionsプロパティにアクションを設定すると、自動的に共有されます。 共有されたアクションを実行すると、リモートのアプリケーションのアクションを実行することができます。 リモートのアクションを実行するのに、ソースコードの記述は全く必要ありません。

TTetheringAppProfileのRunRemoteActionメソッドにリモートのアプリケーションとアクションの名前を指定して呼び出すと、リモートのアプリケーションのアクションを実行することができます。 プログラムで柔軟に呼び出す先を設定できます。

データを共有する

他のアプリケーションにデータを送信したり、データを受信することができます。

この機能を使うことで、次のようなアプリケーションを簡単に作成できます。

  • スマートフォンのカメラの映像をパソコンに表示する
  • WindowsパソコンのデスクトップをMacに表示する
  • スマートフォンのファイルをパソコンに転送する

TTetheringAppProfileのResourcesプロパティにデータを登録すると、データを共有しているアプリケーションに変更の通知と登録されたデータが自動的に送信されます。 また、他のアプリケーションの共有データを要求することもできます。

TTetheringAppProfileのSendStringメソッドやSendStreamメソッドを呼び出すと、リモートのアプリケーションに文字列やバイト列を送信することができます。 プログラムで柔軟に送信先を設定できます。

接続する

対応している接続

同じ端末または同じローカルネットワーク(LAN)

接続できるアプリケーションは、同じ端末上にあるアプリケーションまたは同じローカルネットワーク(LAN)上にあるアプリケーションです。

OS

OSはWindows/OS X/iOS/Androidに対応しています。

VCLアプリケーションとFireMonkeyアプリケーションの両方に接続できます。

None

接続できる台数

接続できるアプリケーションは1対1に限りません。複数のアプリケーションに接続することができます。

  • 1対nの接続

    1つの端末から複数の端末を操作・送信できます。

    たとえば、複数のパソコンをまとめて操作するリモコンアプリケーションを作成できます。

  • n対1の接続

    複数の端末から1つの端末を操作・送信できます。

  • n対nの接続

    複数対複数の接続も可能です。

    たとえば、チャットアプリケーションを作成できます。

Delphi XE6/C++Builder XE6では接続できる台数は最大20台までに制限されています。 (ソースコードに定数で設定されています。)

AutoConnectメソッドによる自動接続

TTetheringManagerのAutoConnectメソッドを呼び出すと、アプリケーションどうしで互いを自動的に検出し、Groupプロパティの値が同じTTetheringAppProfileをペアにします。

_images/006.gif
// 接続する
TetheringManager1.AutoConnect();

AutoConnectメソッドは引数にタイムアウトの時間をとることができます。

// 接続する(タイムアウトの時間は3000ミリ秒)
TetheringManager1.AutoConnect(3000);

AutoConnectメソッドの処理が終了すると、OnEndAutoConnectイベントが発生します。

procedure TForm1.TetheringManager1EndAutoConnect(Sender: TObject);
begin
  ShowMessage('接続しました。');
end;

DiscoverManagersメソッドによる手動接続

TTetheringManagerのDiscoverManagersメソッドを呼び出すと、アプリケーションテザリングを使用している他のアプリケーションの検出をします。

検出が完了すると、このマネージャのOnEndManagersDiscoveryイベントが発生します。

None

OnEndManagersDiscoveryイベントでは、検出したリモートマネージャのリストを読み取り、TTetheringManager.PairManagerを呼び出して、希望するものとペアにします。

procedure TForm1.TetheringManager1EndManagersDiscovery(const Sender: TObject;
  const RemoteManagers: TTetheringManagerInfoList);
var
  I: Integer;
begin
  for I := 0 to RemoteManagers.Count - 1 do
    if RemoteManagers[I].ManagerText = 'MyTetheringManager' then
      TetheringManager1.PairManager(RemoteManagers[I]);
end;

ローカルマネージャをリモートマネージャとペアにするたびに、ローカルマネージャのOnEndProfilesDiscoveryイベントが発生します。

OnEndProfilesDiscoveryイベントの引数RemoteProfilesはペアになったリモートマネージャのプロファイルのリストです。 ローカルプロファイルのConnectを呼び出して、希望するものに接続します。

procedure TForm1.TetheringManager1EndProfilesDiscovery(const Sender: TObject;
  const RemoteProfiles: TTetheringProfileInfoList);
var
  Info: TTetheringProfileInfo;
  I: Integer;
begin
  for I := 0 to TetheringManager1.RemoteProfiles.Count - 1 do
  begin
    Info := TetheringManager1.RemoteProfiles[I];
    if TetheringAppProfile1.Text = Info.ProfileText then
      // リモートプロファイルに接続する
      if TetheringAppProfile1.Connect(Info) then
        Break;
  end;
end;

パスワードで認証する

意図しないアプリケーションとの接続を防ぐため、パスワードを使って認証することができます。

リモートマネージャのPasswordプロパティにパスワードが設定されている場合、TTetheringManagerコンポーネントのOnRequestManagerPasswordイベントが発生します。

None

OnRequestManagerPasswordイベントの引数Passwordにパスワードを代入します。

procedure TForm1.TetheringManager1RequestManagerPassword(const Sender: TObject;
  const RemoteIdentifier: string; var Password: string);
begin
  Password := '1234';
end;
注意点
送信されるパスワードは暗号化されていません。

接続状況を知る

TTetheringManagerコンポーネントのRemoteProfilesプロパティで接続しているプロファイルのリストを取得できます。

接続しているアプリケーションが終了すると、RemoteProfilesプロパティは自動的に更新されます。

procedure TForm1.CheckRemoteProfiles;
begin
  if TetheringManager1.RemoteProfiles.Count > 0 then
  begin
    // 接続しているときの処理
  end
  else
  begin
    // 接続していないときの処理
  end;
end;

次のコードはTTimerコンポーネントを使用して、接続状況をラベルに表示します。

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if TetheringManager1.RemoteProfiles.Count > 0 then
  begin
    LabelConnect.Text := '接続中';
  end
  else
  begin
    LabelConnect.Text := '接続していません。';
  end;
end;

アクションを共有する

TTetheringAppProfileのActionsプロパティ

共有するアクションは、TTetheringAppProfileのActionsプロパティに設定します。

TTetheringAppProfileのActionsプロパティ登録するTLocalActionには、次の項目を設定します。

  • 実行するTAction(Actionプロパティ)
  • アクション名(Nameプロパティ)
  • 実行するのはローカルのアクションかリモートのアクションか(Kindプロパティ)

KindプロパティがMirrorであるTLocalActionを実行すると、同じNameプロパティを持つTLocalActionが実行されます。

_images/007.gif

操作する側のアクションの設定

  1. TLocalActionを作成する
  2. Nameプロパティを操作される側のアクションと同じにする
  3. KindプロパティにMirrorする
_images/008.gif

操作される側のアクションの設定

  1. TLocalActionを作成する
  2. Nameプロパティを操作する側のアクションと同じにする
  3. KindプロパティにSharedする
_images/009.gif

アクションを実行する

ローカルのTActionを実行すると、対応するリモートのアクションが実行されます。

RunRemoteActionを使ってリモートアクションを実行する

TTetheringAppProfileコンポーネントのRunRemoteActionメソッドを使うと、リモートアクションを実行することができます。

RunRemoteActionメソッドは引数にリモートアプリケーションプロファイルとアクションの名前をとります。

リモートアプリケーションプロファイルはTTetheringManagerのRemoteProfilesプロパティから取得できます。

function TTetheringAppProfile.RunRemoteAction(
    (* リモートアプリケーションプロファイル *)
    const AProfile: TTetheringProfileInfo;
    (* アクションの名前 *)
    const AnActionName: string
): Boolean;

もう一つは引数にTRemoteActionのインスタンスをとります。

TRemoteActionのインスタンスはリモートアプリケーションプロファイルとアクション名から取得できます。

function TTetheringAppProfile.RunRemoteAction(
    (* TRemoteActionのインスタンス *)
    const AnAction: TRemoteAction
): Boolean;

コード例

次のコードは、Groupプロパティが’APP_TEST1’でTextプロパティが’TetheringAppProfile1’であるリモートプロファイルの’ActionAddLine1’アクションを実行します。

var
  RemoteProfile: TTetheringProfileInfo;
  RemoteActions: TList<TRemoteAction>;
  RemoteAction: TRemoteAction;
begin
  for RemoteProfile in TetheringManager1.RemoteProfiles do
  begin
    if (RemoteProfile.ProfileGroup = 'APP_TEST1') and
      (RemoteProfile.ProfileText = 'TetheringAppProfile1') then
    begin
      RemoteActions := TetheringAppProfile1.GetRemoteProfileActions
        (RemoteProfile);
      if RemoteActions <> nil then
      begin
        for RemoteAction in RemoteActions do
        begin
          if RemoteAction.Name = 'ActionAddLine1' then
          begin
            TetheringAppProfile1.RunRemoteAction(RemoteAction);
          end;
        end;
      end;
    end;
  end;
end;

データを共有する

共有できるデータのデータ型

アプリケーションテザリングでは、次のデータ型のデータを共有できます。

  • 標準データ型
    • Boolean
    • Integer
    • Int64
    • Single
    • Double
    • String
  • TStream

データを共有する方法

データを共有する方法には、大きく分けて「一時リソースとして送信する」方法と「リソースを共有する」方法の2種類あります。

  • 一時リソースとして送信する

    _images/010.gif
  • リソースを共有する

    _images/011.gif

「リソースを共有する」方法では、データを接続先に送信する方法と、接続先のデータを要求する方法の2種類があります。

  • ローカルリソースの変更を通知して送信する

    _images/012.gif
  • リモートリソースを要求する

    _images/013.gif

一時リソースとして送信する

TTetheringAppProfileコンポーネントのSendStringメソッドやSendStreamメソッドを使用して、文字列やストリームを接続されたアプリケーションに送信することができます。

受信すると、TTetheringAppProfileコンポーネントのOnResourceReceivedイベントが発生します。

_images/014.gif

一時リソースの文字列を送信するコード

TTetheringAppProfileのSendStringメソッドを呼び出すと文字列を送信することができます。 引数には送信先のプロファイル・ヒント文字列・送信文字列を指定します。

var
  RemoteProfile: TTetheringProfileInfo;
begin
  for RemoteProfile in TetheringManager1.RemoteProfiles do
  begin
    if (RemoteProfile.ProfileGroup = 'APP_TEST1') and
      (RemoteProfile.ProfileText = 'TetheringAppProfile2') then
    begin
      // 文字列を送信する
      TetheringAppProfile1.SendString(RemoteProfile,
        '送信テスト', //受信側はAResource.Hintで取得する
        '送信文字列' //受信側はAResource.Value.AsStringで取得する
      );
      Exit;
    end;
  end;
end;

ストリームを送信する場合はTTetheringAppProfileのSendStreamメソッドを呼び出します。 引数には送信先のプロファイル・ヒント文字列・送信するTStreamを指定します。

一時リソースの文字列を受信するコード

文字列やストリームが送信されると、TTetheringAppProfileのOnResourceReceivedイベントが発生します。

procedure TForm1.TetheringAppProfile1ResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  …
end;

引数のAResourceのHintプロパティに送信されたヒント文字列が、Valueプロパティに送信されたデータが格納されています。

AResourceはResTypeプロパティで、送信されたデータが基本型とストリームのどちらかであるかを取得できます。

送信されたデータが基本型の場合は、ValueプロパティはDataTypeプロパティで送信されたデータの型を取得できます。 AsXXXメソッドを呼び出して指定した型でデータを受け取ることができます。

case AResource.Value.DataType of
  TResourceType.Integer:
    S := IntToStr(AResource.Value.AsInteger);
  TResourceType.Single:
    S := FloatToStr(AResource.Value.AsSingle);
  TResourceType.Double:
    S := FloatToStr(AResource.Value.AsDouble);
  TResourceType.Int64:
    S := IntToStr(AResource.Value.AsInt64);
  TResourceType.Boolean:
    S := BoolToStr(AResource.Value.AsBoolean);
  TResourceType.String:
    S := AResource.Value.AsString;
end;

送信されたデータがストリームの場合は、ValueプロパティのAsStreamプロパティでストリームを取得できます。

procedure TForm1.TetheringAppProfile1Resources1ResourceReceived
  (const Sender: TObject; const AResource: TRemoteResource);
begin
  Image1.Bitmap.LoadFromStream(AResource.Value.AsStream);
end;

次のコードでは送信された文字列をメモコンポーネントに出力します。 送信されたデータが文字列であることがわかっているため、データ型の識別はしていません。

procedure TForm1.TetheringAppProfile1ResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  TThread.Synchronize(nil,procedure
  begin
    Memo1.Lines.Add(AResource.Hint); //=> '送信テスト'
    Memo1.Lines.Add(AResource.Value.AsString); //=> '送信文字列'
  end);
end;

リソースを共有する

リソースを共有するには、共有するリソースを定義します。

共有リソースにデータを登録すると、アプリケーションに共有リソースの変更が通知され、登録されたデータが送信されます。

また、共有リソースに登録されているデータを要求することもできます。

共有リソースを定義する

TTetheringAppProfileのResourcesプロパティに、共有するリソース(TLocalResource)を追加します。

_images/015.gif
  • Kindプロパティ

    送信側か受信側かを指定します。

    送信側のときは「Shared」、受信側の時は「Mirror」を指定します。

  • Nameプロパティ

    送信側・受信側ともに同じ名前をつけます。

  • ResTypeプロパティ

    送受信するデータ型を指定します。

    標準データ型のときは「Data」、TStreamのときは「Stream」を指定します。

_images/016.gif

データを送信する

データを送信するには、TTetheringAppProfileのResourcesプロパティに定義したTLocalResourceのValueプロパティに、送信するデータを設定します。

データを登録すると、リモートのTLocalResourceに変更が通知されOnResourceReceivedイベントが呼び出されます。

TLocalResourceのValueプロパティに設定したデータは、リモート側からの要求によって送信することもできます。

基本データ型を送信する

ResourcesプロパティのFindByNameメソッドでTLocalResourceを取得して、Valueプロパティに送信するデータを設定します。

FindByNameメソッドは引数にリソースのNameプロパティを指定します。

TetheringAppProfile1.Resources.FindByName('SharedText').Value := '送信テスト';

TStreamを送信する

基本データ型を送信するるときと同様に、ResourcesプロパティのFindByNameメソッドでTLocalResourceを取得して、Valueプロパティに送信するデータを設定します。

登録したストリームは、登録後に解放してはいけません。保持し続ける必要があります。

次のコードでは登録するストリーム(FStream)はメンバ変数になっています。

begin
  if FStream <> nil then
    FStream.Free;

  FStream:= TMemoryStream.Create;
  FStream.LoadFromFile('C:\test\home_256_h.png');
  FStream.Position := 0;
  TetheringAppProfile1.Resources.FindByName('SharedImage').Value := FStream;
end;

データを受信する

送信側のTLocalResourcesのValueプロパティが変更されると、TTetheringAppProfileコンポーネントのOnAcceptResourceイベントが発生します。 受信したくない場合はOnAcceptResourceイベントで受信を拒否できます。

受信を受け入れた場合はOnResourceReceivedイベントが発生します。 このイベントの中で受信したデータを処理します。

_images/017.gif

受信を受け入れる

受信を拒否するときはOnAcceptResourceイベントの引数AcceptResourceにFalseを設定します。 OnAcceptResourceイベントを実装しない場合は自動的に受信します。

procedure TForm1.TetheringAppProfile1AcceptResource(const Sender: TObject;
  const AProfileId: string; const AResource: TCustomRemoteItem;
  var AcceptResource: Boolean);
begin
  // Falseにすると受信しない
  AcceptResource := False;
end;

受信するとTLocalResourcesのOnResourceReceivedイベントが発生します。

基本型のデータを受信する

TResourceValueのDataTypeプロパティで送信されたデータのデータ型を取得します。

case AResource.Value.DataType of
  …

データ型に応じて、AsInteger、AsSingle、AsDouble、AsInt64、AsBoolean、AsStringプロパティを使用してデータを取得します。

procedure TForm1.TetheringAppProfile1Resources0ResourceReceived
  (const Sender: TObject; const AResource: TRemoteResource);
var
  S: string;
begin
  case AResource.Value.DataType of
    TResourceType.Integer:
      S := IntToStr(AResource.Value.AsInteger);
    TResourceType.Single:
      S := FloatToStr(AResource.Value.AsSingle);
    TResourceType.Double:
      S := FloatToStr(AResource.Value.AsDouble);
    TResourceType.Int64:
      S := IntToStr(AResource.Value.AsInt64);
    TResourceType.Boolean:
      S := BoolToStr(AResource.Value.AsBoolean);
    TResourceType.String:
      S := AResource.Value.AsString;
  end;
  …
end;

Streamを受信する例

StreamはAsStreamプロパティで取得します。

procedure TForm1.TetheringAppProfile1Resources1ResourceReceived
  (const Sender: TObject; const AResource: TRemoteResource);
begin
  TThread.Synchronize(nil,procedure
  begin
    Image1.Bitmap.LoadFromStream(AResource.Value.AsStream);
  end);
end;

リモートリソースを要求する

TTetheringAppProfileコンポーネントのGetRemoteResourceValueメソッドを使用して、リモートリソースを要求することができます。

GetRemoteResourceValueメソッドは、引数にリモートリソースを含んだリモートアプリケーションプロファイルとリモートリソースの名前、またはリモートリソースのインスタンスをとります。

function TTetheringAppProfile.GetRemoteResourceValue(
  (* リモートリソースを含んだリモートアプリケーションプロファイル *)
  const AProfile: TTetheringProfileInfo;
  (* リモートリソースの名前 *)
  const ARemoteResName: string
): TRemoteResource;

function TTetheringAppProfile.GetRemoteResourceValue(
  (* リモートリソースのインスタンス *)
  const ARemoteRes: TRemoteResource
): TRemoteResource;

次のコードでは、Textプロパティが’TetheringAppProfile1’であるTTetheringProfileを検索して、Nameプロパティが’RequiredText’であるリモートリソースにデータを要求します。

procedure TForm1.ButtonRequiredTextClick(Sender: TObject);
var
  AProfile: TTetheringProfileInfo;
  AResources: TList<TRemoteResource>;
  AResource, RemoteResource: TRemoteResource;
begin
  // リモートリソースを探す
  for AProfile in TetheringManager1.RemoteProfiles do
  begin
    if AProfile.ProfileText = 'TetheringAppProfile1' then
    begin
      AResources := TetheringAppProfile1.GetRemoteProfileResources(AProfile);
      if AResources <> nil then
      begin
        for AResource in AResources do
        begin
          if AResource.Name = 'RequiredText' then
          begin
            // リモートリソースを要求する
            RemoteResource := TetheringAppProfile1.GetRemoteResourceValue(AResource);
            Memo1.Lines.Add(RemoteResource.Value.AsString);
            Exit;
          end;
        end;
      end;
    end;
  end;
end;