[WinRT] Surfaceのフォトで選択した画像を、デスクトップに送る

[WinRT] デスクトップで閲覧しているURLをSurface RTに送る – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/4718
[WinRT] Surface RT で閲覧しているURLをデスクトップに送る – Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/4723

送るシリーズの第3弾…って訳ではないですが、メモ的に。

Surface RTからURLをデスクトップに送る仕組みを応用して、画像ファイルを送ります。ここではWindows RT標準装備の「フォト」で共有した場合。他にも SkyDrive をやってみたいのですが、これは方式が違うのでまた別途ソースを書きおこします。内部動作が違っても、表面的な UI は同じほうがいいですからね。

■Surface から共有させる

マニフェストの宣言で「サポートされるファイルの種類」を増やしておきます。ここでは、画像ファイルしか扱わないので、まめに「.jpeg」とか書いていきます。「すべてのファイルの種類をサポートする」にチェックを入れてもokなんですが、なんかの理由付けがないと審査で落ちそうな感じがするし、まあ用途としては制限させたほうがいいので。

共有しているところの、ShareTargetPage.xaml.cs を書き換えます。
フォトからは StandardDataFormats.StorageItems を使って送られてくるので、これで受け取り。複数のファイルが送れるのですが、今回は最初の1枚だけを対象に。

public async void Activate(ShareTargetActivatedEventArgs args)
{
    this._shareOperation = args.ShareOperation;
...略
    if (this._shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        Debug.WriteLine("contain StorageItems");
        var files = await this._shareOperation.Data.GetStorageItemsAsync();
        foreach (StorageFile f in files)
        {
            Debug.WriteLine("name: {0}", f.Path);
        }
    }
}

画像転送は、HTTPのPUTを使ってBASE64で送ります。このあたりは先行きXMLシリアライズを使うつもりなので、ざっと書き下し。バイト配列とのコンバートは、慣れると…とはいえ慣れても面倒なのですが、この通りに各と動きます。

    private async void ShareButton_Click(object sender, RoutedEventArgs e)
    {
        this.DefaultViewModel["Sharing"] = true;
        this._shareOperation.ReportStarted();
        // TODO: this._shareOperation.Data を使用して共有シナリオに適した
        //       作業を実行します。通常は、カスタム ユーザー インターフェイス要素を介して
        //       このページに追加されたカスタム ユーザー インターフェイス要素を介して
        //       this.DefaultViewModel["Comment"]
...略
        if (this._shareOperation.Data.Contains(StandardDataFormats.StorageItems))
        {
            var files = await this._shareOperation.Data.GetStorageItemsAsync();
            var file = files[0] as StorageFile;
            // 画像データを転送
            var stream = await file.OpenReadAsync();
            using (var mem = new MemoryStream())
            {
                var rs = stream.AsStreamForRead();
                await rs.CopyToAsync(mem);
                byte[] data = mem.ToArray();
                string url = "http://mars:8090/PutImage";
                HttpClient cl = new HttpClient();
                string b64 = System.Convert.ToBase64String(data);
                StringContent cont = new StringContent(b64);
                var res = await cl.PutAsync( url, cont );
            }
        }
        this._shareOperation.ReportCompleted();
    }
}

c# – WinRT image handling – Stack Overflow
http://stackoverflow.com/questions/10013799/winrt-image-handling
DataPackageView.GetStorageItemsAsync | getStorageItemsAsync Method (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/apps/windows.applicationmodel.datatransfer.datapackageview.getstorageitemsasync

このあたりを読んで、まめに実装するがよいかと。

■デスクトップで画像を受ける

Listener のタスクが長くなってきたので、別関数にくくりだし。Task の中からは GUI コントロールを直接さわれないので、Invoke を使います。いろいろやってこれが簡単な方法かと。
画像ファイルは、BASE64 からバイナリ配列に直して Bitmap を作るという手順です。このあたりの流れは、適度に隠蔽化してしまうと楽になると思います。

private void Form1_Load(object sender, EventArgs e)
{
    listener = new HttpListener();
    listener.Prefixes.Add("http://*:8090/");
    listener.Start();

    // 受信待ちタスク
    Task tsk = new Task( ListenLoop );
    tsk.Start();
}

async void ListenLoop()
{
    while (true)
    {
        var cont = listener.GetContext();
        var req = cont.Request;
        var res = cont.Response;
        if (req.RawUrl.StartsWith("/GetUrl"))
        {
            Debug.WriteLine(string.Format("req {0}", req.RawUrl));
            var tw = new StreamWriter(res.OutputStream);
            tw.Write(_text);
            tw.Close();
            res.Close();
        }
        else if (req.RawUrl.StartsWith("/PutUrl") == true)
        {
            Debug.WriteLine(string.Format("req {0}", req.RawUrl));
            var text = req.RawUrl.Replace("/PutUrl/", "");
            text = System.Uri.UnescapeDataString(text);
            // textBox1.Text = text;
            textBox1.Invoke((MethodInvoker)(() => textBox1.Text = text));
            var tw = new StreamWriter(res.OutputStream);
            tw.Write("OK");
            tw.Close();
            res.Close();
        }
        else if (req.RawUrl.StartsWith("/PutImage") == true)
        {
            // 画像をBASE64で受け取る
            Debug.WriteLine(string.Format("req {0}", req.RawUrl));
            var st = req.InputStream;
            var sr = new StreamReader(st);
            string b64 = await sr.ReadToEndAsync();
            byte[] data =  System.Convert.FromBase64String(b64);
            var mem = new MemoryStream(data);
            Bitmap bmp = new Bitmap(mem);
            // pictureBox1.Image = bmp;
            pictureBox1.Invoke((MethodInvoker)(() => pictureBox1.Image =bmp ));

            var tw = new StreamWriter(res.OutputStream);
            tw.Write("OK");
            tw.Close();
            res.Close();
        }
        else
        {
            var tw = new StreamWriter(res.OutputStream);
            tw.Write("ERROR");
            tw.Close();
            res.Close();
        }
    }
}

■動かしてみる

フォトから共有させて、

フォームアプリに転送するだけです。

そのままファイルに保存してもいいし、Clipboard.SetImage(pictureBox1.Image) な風にしてい、クリップボードに送るのもいいでしょう。

■サンプルソース

サンプルソースはこちら。
http://sdrv.ms/10mK1ch

カテゴリー: C#, WinRT パーマリンク