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 の話が続く。
metro 用のシミュレータがいまひとつ(=環境を汚す)という話は、このシミュレータ内にあるフォルダ(マイコンピュータ)を見ると分かります。ファイルなどは全てシミュレータの起動元と同じになっているので、シミュレータ内で設定を変えると、開発環境の方も変わってしまうという訳ですね。ホスト名とかネットワークも共有しているので、そのあたり「シミュレータ環境内だからサンドボックスになっている」と思うと妙なことになります。
iPhone開発をするときのシミュレータは完全にサンドボックスなので外部環境には影響を与えません。多分 Windows Phone の開発環境もそうなんだろうけど。どうせならば、Metro アプリのシミュレータもそうなってほしいなぁと。
が、シミュレータ自体で動かすのであれば、VMPlayer などを使って、仮想マシンとして Windows 8 を立ち上げるほうが「環境を汚さない」という点では楽です。なので、別のマシンにログを HttpClient で飛ばすのは、それなりに理由があるかと。ただ、仮想環境ならば、ファイル共有をしてしまってローカルファイルに書き込むのでもいいような気もしますが、それだとマニフェストが変わってしまうということで(そのうち、なにか良いログツールが出るとは思いますが)。