App Tethering : Sharing Persistent Resources Part 2

Last post we were introduced to sharing resources, in particular, persistent resources, and we wrote an example to share a persistent resource of type Data between two apps.

This time around let’s do the same with a resource of type Stream. On the surface it feels pretty similar, but we’ll look under the covers to understand a key difference between sharing Data and Stream resources, and why you might opt to use a Stream resource even if you are sharing a String.

Let’s open up our App1 and App2 projects from last time. We’re going to update them to share an Image resource. As we saw before, Data resources are numbers, booleans and strings, so we’ll use a Stream resource to share our image.

Bring up the main form of App1, and drop down a TButton, a TImageControl and a TOpenDialog component. Set the Text property of the TButton to “Load Image”.

tethering4.1

We also need to add a new Resource, so like before, go into the property editor for the TetheringAppProfile1.Resources property and add a new Resource. Set the ResType to Stream and give it a Name of SomeImage.tethering4.2

In the OnClick event of the Load Image button put the following code.

For Delphi:

procedure TForm3.Button2Click(Sender: TObject);
var
  LStream : TMemoryStream;
begin
  if OpenDialog1.Execute then
  begin
    ImageControl1.LoadFromFile(OpenDialog1.FileName);

    LStream := TMemoryStream.Create;
    ImageControl1.Bitmap.SaveToStream(LStream);
    LStream.Position := 0;

    TetheringAppProfile1.Resources.FindByName('SomeImage').Value := LStream;
  end;
end;

For C++:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
  if (OpenDialog1->Execute()) {
    ImageControl1->LoadFromFile(OpenDialog1->FileName);

    TMemoryStream* LStream = new TMemoryStream;
    ImageControl1->Bitmap->SaveToStream(LStream);
    LStream->Position = 0;

    TetheringAppProfile1->Resources->FindByName("SomeImage")->Value = 
      TResourceValue::_op_Implicit(LStream);
  }
}

We’re using the OpenDialog to browse for an Image file, then loading it into the ImageControl. Then, we’re creating a local TMemoryStream component and save the bitmap from the ImageControl to that stream. We then position the stream back to the beginning before assigning it to the SomeImage resource we just created. The first part is different to what we did with our string resource last time, but fundamentally that’s all just setup for the main part, which is setting the resource value. We did this in basically the same way as the string resource.

Also, note we created the stream but did not free it. The App Tethering framework will take care of that for us.

That’s it for the sharing side, let’s look at what we need for the receiver.

Open up the main form of App2, and drop down another TImageControl. Also, create another resource on the TetheringAppProfile1 component. Same values as above, but importantly change the Kind from Shared to Mirror.

tethering4.3

Just like our String resource, we need to put some code in this resource’s OnResourceReceived event (not the TetheringAppProfile component’s OnResourceReceived event). The code should look like this.

For Delphi:

procedure TForm4.TetheringAppProfile1Resources1ResourceReceived(
const Sender: TObject; const AResource: TRemoteResource);
begin
  AResource.Value.AsStream.Position := 0;
  ImageControl1.Bitmap.LoadFromStream(AResource.Value.AsStream);
end;

For C++:

void __fastcall TForm2::TetheringAppProfile1Resources1ResourceReceived(TObject * const Sender,
TRemoteResource * const AResource)
{
  AResource->Value.AsStream->Position = 0;
  ImageControl1->Bitmap->LoadFromStream(AResource->Value.AsStream);
}

By now this should be pretty self-explanatory, but just in case, we’re accessing the AResource parameter as a stream and positioning it back to the start. Then we’re loading the bitmap in the ImageControl from that stream. Again, we don’t free the stream afterwards, that’s App Tethering’s job.

Run both App1 and App2 and click Connect in App1. Once connected, click the Load Image button in App1, select an image and press OK. You should see that image shared across to App2 and displayed on the form.

tethering4.4

Apart from mucking around with the stream, this was almost identical to sharing a Data resource last time, so why dedicate a whole post to it?

Well, let’s look at the flow of events when we shared that Image:

App 1
Network
App 2
Resources.FindByName(‘SomeText’).Value := LStream
UDP Point to PointResource Updated (UDP Point to Point)
UDP Point to Point
GetResourceValue (TCP)
UDP Point to PointResource Value (TCP) Resource[1].OnResourceReceived

Unlike our Data resource,  the actual value of a Stream resource is not sent in the UDP Resource Updated notification. The receiver opens a TCP connection back to the sender and requests the value, which then comes back via TCP.

This makes sense, given a Stream Resource could potentially be quite large. While UDP has the advantage for low latency, TCP is much better suited to large transfers.

My diagram above shows this as a single request and reply, but for those familiar with TCP, you’ll know that there are potentially many back-and-forth transmissions to transfer the full stream.

tethering4.5

In the network dump above, you can see packet 7406 in blue. This is the UDP Point to Point Resource Update Notification. 7507 is the TCP reply request the value of the resource. You can then see a series of packets sending the data and the occasional acknowledgement back from the receiver. I’ve not shown all of them, I encourage you to try this out for yourself if you’re interested.

One thing we should take from this is if we’re sending large amounts of data, we should do it via a Stream resource. Even if that data is text, and could be shared via a Data resource, performance-wise my tests show you are significantly better-off sending it via a Stream.

We now know how to share persistent resources between apps. Next post I’ll cover off transient resources and then we can get into sharing Actions.

You can get the source for the sample apps at this stage of the series, in both C++ and Delphi, but to really understand what is going on there is no substitute for building the apps yourself. They aren’t that big.

One Comment

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>