Community Open Day 2012 の補足その1

Community Open Day 2012
http://cod.ms/
Community Open Day 2012 の準備 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3476

なところで、発表したものの、事前準備が足りない…つーか、コード自体を見せるの忘れていたので補足しておきます。
スライド自体は、

Cod2012 デバッグ講座
http://www.slideshare.net/moonmile/cod2012

なところを参照してください。概要しか書いて無くて、実際は、スレートpc へのリモートデバッグが主なところです。
開発マシンからスレートPCへリモート接続する場合は、Visual Studio から接続すればよいのですが、テストをしていくといくつか問題があったのです。

  • Visual Studio からデバッグ実行していると、サスペンドしない。
  • スレートPCへの接続は「無線LAN」なので、スピード自体に問題がでそう。

多分、サスペンドしないのはデバッグモードで動いているために、権限が desktop アプリ側にあるからだと思います。デバッグ時にサスペンドして貰っても困るし、というのもありますが。
が、実機で動かす場合、サスペンドする時、サスペンドから復帰する時、というのが metro アプリでは結構重要で、このタイミングを実機でみるのは重要かなと思ったわけです。

で、通常はローカルファイルに書き込むわけですが、metro アプリの場合は desktop 側への制限が厳しいために、

  • マイドキュメントなどのファイルアクセスに、マニフェストを変えないといけない。
  • metro アプリ内のサンドボックスに書き込むとアンインストールした時にログファイルも一緒に消えてしまう。

という欠点があります。なので、ひとつの手段として、metro アプリから HttpClient を使って開発機にログを飛ばそうという試みです。最終的には、log4net のように使えるといいんでしょうねえ。

デモで使ったサンプルコード自体は、

win8/MetroMemoPadiPadLike at master ・ moonmile/win8 ・ GitHub
https://github.com/moonmile/win8/tree/master/MetroMemoPadiPadLike

に一式があります。

■ metro アプリからログ出力

App.xaml.cs の中に次のようなログ関数を作っておきます。単純に「win8rp-pc.local」というマシンに HttpClinet を飛ばしているだけです。
本来ならば、きちんと Web REST 形式にすれば良いのですが、まあサンプルということで。
ログ自体は、WCF サービスにするときっちりと作れるのでしょうが、WCF だといちいちサービスを立ち上げないといけないので、結構面倒です。
なので、適当に GET コマンドで送れるパターンにしておきます。

        public async static void DebugLog(string text)
        {
            string url = "http://win8rp-pc.local:8083/metro/method";
            HttpClient client = new HttpClient();
            try
            {
                HttpResponseMessage res = await client.GetAsync(url + "/" + text);
            }
            catch
            {
            }
            // string response = await res.Content.ReadAsStringAsync();
        }

サーバーに接続できない場合もあるので、try-catch は必須ですね。また、このパターンだといちいち new HttpClient をしているので、本当は別に privete 変数で保持したほうがいいと思います。

        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            App.DebugLog("OnSuspending");
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            deferral.Complete();
        }

metro アプリ自体のサスペンドは、App.OnSuspending になるので、ここに仕込みます。

■回転時にログを出力する

スレートPCを回転させたときのログ出力は、MainPage.xml.cs の Current_SizeChanged メソッドになります。
このイベントは、あらかじめ Window.Current.SizeChanged += Current_SizeChanged; のように回転イベントに仕込んでおきます。

        private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
        {
            var state = Windows.UI.ViewManagement.ApplicationView.Value;
            VisualStateManager.GoToState(this, state.ToString(), false);
            App.DebugLog("Current_SizeChanged " + state.ToString());
        }

こうすると、実機を回転した時にログが出力されます。

■簡易ログサーバー

metro アプリからログ受信をするためのサーバーを作ります。
HttpListener クラスという目的そのままのクラスがあるので、これを利用します。これは non-blocking で動くので、background スレッドを作らなくてよいので、非常に便利です。

namespace MetroLogServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        HttpListener listener;

        private void button1_Click(object sender, EventArgs e)
        {
            string url = "http://*:8083/metro/";
            // 開始
            listener = new HttpListener();
            listener.Prefixes.Add(url);

            listener.Start();
            listBox1.Items.Add("サーバー開始");
            listener.BeginGetContext(ListenerCallback, listener);
        }

        public void ListenerCallback(IAsyncResult result)
        {
            // HttpListener listener = (HttpListener)result.AsyncState;
            HttpListenerContext context;
            try
            {
                context = listener.EndGetContext(result);
            }
            catch
            {
                // stop メソッドで例外が発生するので、対処
                return;
            }
            // var content = listener.GetContext();
            var req = context.Request;
            var url = req.RawUrl;
            var res = context.Response;

            string text = url.Replace("/metro/method/", "");
            text = System.Web.HttpUtility.UrlDecode(text);
            listBox1.Items.Add("受信:" +  text);

            var output = new StreamWriter(res.OutputStream);
            output.WriteLine(string.Format("called {0}", url));
            output.Close();

            // 次の受信の準備
            listener.BeginGetContext(ListenerCallback, listener);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 終了
            listener.Stop();
            listBox1.Items.Add("サーバー終了");
        }
    }
}

本当は、これを説明しないと駄目だったんですけどね…なんか、いきおいデモのほうに進んでしまって、前提のコードの紹介が抜けてしまいました。時間配分は10分ごとに確認していたのですが、回転時の動作とかリモート接続とかの解説に力を入れ過ぎてしまったという感じです。

こんな風にシミュレータ上と開発機で通信もできます。

という訳で、もうちょっと cod2012 の話が続く。

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

Community Open Day 2012 の補足その1 への1件のコメント

  1. masuda のコメント:

    metro 用のシミュレータがいまひとつ(=環境を汚す)という話は、このシミュレータ内にあるフォルダ(マイコンピュータ)を見ると分かります。ファイルなどは全てシミュレータの起動元と同じになっているので、シミュレータ内で設定を変えると、開発環境の方も変わってしまうという訳ですね。ホスト名とかネットワークも共有しているので、そのあたり「シミュレータ環境内だからサンドボックスになっている」と思うと妙なことになります。
    iPhone開発をするときのシミュレータは完全にサンドボックスなので外部環境には影響を与えません。多分 Windows Phone の開発環境もそうなんだろうけど。どうせならば、Metro アプリのシミュレータもそうなってほしいなぁと。

    が、シミュレータ自体で動かすのであれば、VMPlayer などを使って、仮想マシンとして Windows 8 を立ち上げるほうが「環境を汚さない」という点では楽です。なので、別のマシンにログを HttpClient で飛ばすのは、それなりに理由があるかと。ただ、仮想環境ならば、ファイル共有をしてしまってローカルファイルに書き込むのでもいいような気もしますが、それだとマニフェストが変わってしまうということで(そのうち、なにか良いログツールが出るとは思いますが)。

コメントは停止中です。