[雑記]Androidケータイで安全運転サポートアプリSafety Sightを使ってみた。

たぶん、テレ東のWBSで放送されていた損保ジャパンと日本興亜損保の車用ケータイアプリ セーフティサイトのレポ。

これをインストールした携帯を車のダッシュボードに設置すると携帯のカメラを使って危険察知やドライブレコーダ的な役割をしてくれる。運転診断やGPSを使用してどこで危険な運転が起こったかなどを表示してくれちゃったりする。これがただなのはいいところだけど、やっぱりカメラの認識とかは携帯の機種などによる誤差などがあるのも仕方ないのね。あと、最近の携帯じゃないときつい。しかし、無料だし試してみるのは面白いと思います。

安全運転サポートアプリ Safety Sight – Google Play
Img20121227165032
以下、Galaxy S(SC-02B)の使用感。
・カメラの認識が余り正確ではない。信号待ちで前の車との距離が明らかに違う時がある。
・あきらかに信号待ちをしていて、前に車が居ないときなのに「ピーピー、発進確認」とか警告を言う。
・暗い時は前の車を認識しない。
・アプリが重い。スマホのスペックが必要。
・アプリが落ちることが度々。。これもスペックの問題か。
・電池のヘリ早すぎワロタ。常にカメラ+GPSを稼働させているので充電しながらでないときつい。
・電池熱すぎワロエナイ。これは初めての見たのだけど、電池が熱すぎ警告でた。たぶんOSが出しているものだと思う。

全般的に、スマホのスペックやら、カメラの相性やらがありそう。無料アプリにしてはなかなか面白くて、試してみる価値はあると思います。最近出てるスマホなら快適なのかなぁ。あと、ダッシュボードに横向きに固定するスタンドはiPhoneなどの横幅が小さめのは結構あるのだけれど、iPhoneよりも幅が広い携帯用のスタンドは商品数が少なかったよ。最近は液晶の大型化が進んでいるからスタンドも広いやつ出して欲しいところ。

[悲報]Microsoft Expression Encoder終了のお知らせ。

窓の杜の記事によるとMicrosoft Expression Encoderの開発が終了するらしい。Expression BlendがVisualStudioに統合されるのは良いニュース!Windows8のアプリ開発にはXAMLの知識が必要っぽいからねぇ。専用のデザイナが使えるのはいいこと。
Expression EncoderはMS純正だから信頼できるし、APIも結構豊富だから、今後の展開が終わってしまうのは残念。

窓の杜 Microsoft Expressionが終了へ、「Expression Web」「Expression Design」は無償化

Competences_Techno_Blend

[C#]Microsoft Expression Encoderを使用して簡易画面キャプチャソフトを作る、その2。

以前作成したMicrosoft Expression Encoder4を使用したキャプチャソフトにGIFアニメーション作成機能を追加してみた。

GIFエンコーダーにはNGifというエンコーダーを使用。フレームレートが高いとgifのエンコードにかなり時間がかかる。

あと、いろいろバグ修正。エンコード中画面を邪魔にならないようにしたり、複数同時起動時に画面番号をつけたりした。
Expression Encoderを使ってWebカメラを使ったストリーミングとか試してみたいけど、色々機能つけすぎてソースが汚くなったので、もういじりたくない。

ソースはこちら
別途、Microsoft Expression Encoder4が必要。
参照に、JSON.NETNGifが必要。

複数起動時の画面番号
Img20121225214309

GIFアニメーション作成機能の追加

[C#]System.Threading.Tasks.Taskを使用したプログレスバーの作成時のメモ。

このエントリーで作成したプログレスバーFormの仕組みを使って、ちょっとソースをいじっていたところ、System.Threading.Tasks.Taskクラスでハマった時のメモ。

やりたいこと
ある処理を実行している際にプログレスバーを表示させたい。処理はTaskクラスを使用して、結果はTask内で作成し、FormはそのTaskクラスの実行結果をWindowのタイトルバーに表示したい。なおFormはTaskを実行している時は固まったりしないこと。

だめなパターン
Taskクラスを使用してプログレスバーの進捗を行う仕組みを考えた時に以下のソースではうまくいきません。

ソース

        private void Form2_Load(object sender, EventArgs e)
        {
            Action<string> DispLog = (arg) => Trace.WriteLine(string.Format("ThreadID:{0}> {1}", Thread.CurrentThread.ManagedThreadId.ToString(), arg));

            DispLog("Form Start");

            var task = Task.Factory.StartNew(() =>
            {
                DispLog("Task Start");

                for (int i = 0; i < 100; i++)
                {
                    DispLog("loop:"+i.ToString());
                    System.Threading.Thread.Sleep(10);      // 重たい処理
                    this.Invoke(new Action<int>((p) =>
                    {
                        DispLog("ProgressChange:" + p.ToString());
                        progressBar1.Value = p + 1;
                    }), new object[] { i });
                }

                DispLog("Task End");
                return 0;
            });

            DispLog("Coffee break.");

            // taskの結果を待つ
            if (task.Result == 0)
            {
                DispLog("Task Result.");
                this.Text = "OK";
            }

            DispLog("Form End");

        }

実行結果

ThreadID:9> Form Start   // ID:9 Form
ThreadID:9> Coffee break.
ThreadID:6> Task Start     // ID:6 Task
ThreadID:6> loop:0

..デッドロック!!

デッドロックする理由
FormのスレッドIDは9,子Task(task)が6で、for文の1回目の”loop:0″を表示した後のthis.Invokeメソッド内はForm(ID:9)が呼ばれる状態ですが、Formは”Coffee break”を表示した後のtask.Resultでtask(ID:6)の終了待ち状態に入っているためデッドロックに陥ります。というか、親FormがTask待ちになってしまうこと自体が固まった状態だってことに気づかないなんて馬鹿ですねぇ~ → 私ですが何か。

this.BeginInvoke()に変えてみる。
this.Invoke()の部分を非同期呼び出しであるthis.BeginInvoke()に変えてみましょう。

ソース

                    this.BeginInvoke(new Action<int>((p) =>
                    {
                        DispLog("ProgressChange:" + p.ToString());
                        progressBar1.Value = p+1;
                    }),new object[]{i});

実行結果

ThreadID:9> Form Start      // ID:9 Form
ThreadID:9> Coffee break.
ThreadID:10> Task Start     // ID:10 Task
ThreadID:10> loop:0
ThreadID:10> loop:1
ThreadID:10> loop:2
...
ThreadID:10> loop:98
ThreadID:10> loop:99
ThreadID:10> Task End
ThreadID:9> Task Result.
ThreadID:9> Form End
ThreadID:9> ProgressChange:0
ThreadID:9> ProgressChange:1
...
ThreadID:9> ProgressChange:98
ThreadID:9> ProgressChange:99

今度はデッドロックには陥らないものthis.BeginInvoke()の実行タイミングがTask(ID:10)が全て終了した後に実行されています。実際の処理の結果は正確に取れるものの、メインの処理が実行中にプログレスバーが表示されることはありません。

改善策
プログレスバーFormをマルチスレッド化する理由は親のFormが固まらないようにするためです。なので、メインとなる処理を実行している別スレッド上のTaskの終了を待つにはTaskクラスで待たせればいいことになります。

Task待ちはTaskにまかせる

        private void Form2_Load(object sender, EventArgs e)
        {
            Action<string> DispLog = (arg) => Trace.WriteLine(string.Format("ThreadID:{0}> {1}", Thread.CurrentThread.ManagedThreadId.ToString(), arg));

            DispLog("Form Start");

            var task1 = Task.Factory.StartNew(() =>
            {
                DispLog("Task1 Start");

                var task2 = Task.Factory.StartNew(() =>
                    {
                        DispLog("Task2 Start");
                        for (int i = 0; i < 100; i++)
                        {
                            DispLog("loop:" + i.ToString());
                            System.Threading.Thread.Sleep(100);      // 重たい処理
                            this.Invoke(new Action<int>((p) =>
                            {
                                DispLog("ProgressChange:"+p.ToString());
                                progressBar1.Value = p+1;
                            }),new object[]{i});
                        }

                        DispLog("Task2 End");
                        return 0;
                    });

                DispLog("Coffee break.");

                // task2の結果を待つ
                if (task2.Result == 0)
                {
                    DispLog("Task2 Result.");
                    this.Invoke(new Action(() => this.Text = "OK"));
                }

                DispLog("Task1 End");

            });

            DispLog("Form End");

        }

実行結果

ThreadID:9> Form Start            // ID:9 Form
ThreadID:9> Form End
ThreadID:10> Task1 Start          // ID:10 Task1
ThreadID:10> Coffee break.
ThreadID:11> Task2 Start          // ID:11 Task2
ThreadID:11> loop:0
ThreadID:9> ProgressChange:0
ThreadID:11> loop:1
ThreadID:9> ProgressChange:1
...
ThreadID:11> loop:98
ThreadID:9> ProgressChange:98
ThreadID:11> loop:99
ThreadID:9> ProgressChange:99
ThreadID:11> Task2 End
ThreadID:10> Task2 Result.
ThreadID:10> Task1 End

Form(ID:9)はTask1(ID:10)を非同期で実行した後に”Form End”を表示して終了します。これでFormはどのTaskの終了待ち状態にならないため、固まったようになりません。Task1(ID:10)はTask2(ID:11)を非同期実行した後に、”Coffee break”を表示して、task2.Resultに値がセットされるまで終了待ちになります。Task2(ID:11)はfor文を実行させ、メインの処理( Sleep )を実行させながらthis.Invoke()でprogressBar1の進捗を進めていることがわかります。これで、ユーザにはスムーズに処理が進んでいるように表示させることができます。また、Task1(ID:10)はTask2(ID:11)が終了した後に、ちゃんと結果を受け取ることができています。

[C#]objectとJSONとXMLとMessagePackの変換。

このエントリーではobjectとJSONとXMLの変換をやってみましたが、以前から気になっていたMessagePackを試してみました。
JSONとXMLはいずれもフォーマット定義(要はタグ)と値をstringとして最終的に扱いますが、MessagePackはフォーマット定義と値をバイナリとして扱います。そのためXMLのような構造のデータは非常に軽量にできます。まあMessagePackに限らず他にもバイナリ型のフォーマットは存在するけど、アルゴリズムが違ったり使いやすさもいろいろとあることでしょう。

どれがどうでこうでというのはneueさんのサイトとかとても参考になります。
MemcachedTranscoder – C#のMemcached用シリアライザライブラリ
.NETの標準シリアライザ(XML/JSON)の使い分けまとめ

実際にやってみる。
難しいことはさておき、早速やってみます。
例としてスーパーマーケットの店舗別の店員、商品情報等をデータクラス化して、それぞれJSON、XML、MessagePackに変換して保存してみます。

データクラス

    public class Format
    {
        public enum 雇用形態種別
        {
            社員,
            パート
        }

        public class 店員
        {
            public 雇用形態種別 雇用形態 { get; set; }
            public string 名前 { get; set; }
            public int 年齢 { get; set; }
            public int 給与 { get; set; }

            public 店員()
            {
                雇用形態 = 雇用形態種別.社員;
                名前 = "";
                年齢 = 給与 = 0;
            }

            public void Disp()
            {
                var str = string.Format("名前[{0}],雇用形態[{1}],年齢[{2}],給与[{3}]",
                    名前, 雇用形態.ToString(), 年齢, 給与);
                Console.WriteLine(str);
            }
        }

        public class 商品
        {
            public string 商品名 { get; set; }
            public int 売価 { get; set; }
            public int 原価 { get; set; }
            public int 在庫数 { get; set; }
            public int 販売数 { get; set; }
            public int 売上()
            {
                return 売価 * 販売数;
            }

            public 商品()
            {
                商品名 = "";
                売価 = 原価 = 在庫数 = 販売数 = 0;
            }

            public void Disp()
            {
                var str = string.Format("商品名[{0}],売価[{1}],原価[{2}],在庫数[{3}],販売数[{4}],売上[{5}]",
                    商品名, 売価, 原価, 在庫数, 販売数, 売上());
                Console.WriteLine(str);
            }
        }

        public class 店舗
        {
            public string 店舗名 { get; set; }
            public 店員 店長 { get; set; }
            public List<店員> メンバー { get; set; }
            public List<商品> 店舗商品 { get; set; }

            public 店舗()
            {
                店舗名 = "";
                店長 = new 店員();
                メンバー = new List<店員>();
                店舗商品 = new List<商品>();
            }

            public void Disp()
            {
                var str = string.Format("\n----------------");
                str += string.Format("店舗名[{0}],店長[{1}]", 店舗名, 店長.名前);
                Console.WriteLine(str);
                Console.WriteLine("店員情報");
                メンバー.ForEach(p => p.Disp());
                Console.WriteLine("商品情報");
                店舗商品.ForEach(p => p.Disp());
                Console.WriteLine("\n");
            }
        }

        public class 店舗管理
        {
            public List<店舗> 店舗情報{get;set;}

            public 店舗管理()
            {
                店舗情報 = new List<店舗>();
            }

            public void Disp()
            {
                店舗情報.ForEach(p => p.Disp());
            }
        }

        public class 管理
        {
            public 店舗管理 管理情報{get;set;}

            public 管理()
            {
                管理情報 = new 店舗管理();
            }

            public void Disp()
            {
                管理情報.Disp();
            }
        }
    }

MessagePackで対象となるクラスを定義する際の注意点(たぶん)
・データをPackする対象のクラス・プロパティはすべてPublicにする。
・コンストラクタを定義する際は下記のデフォルトのコンストラクタを記述する。

public class DataClass
{
    public string Str{ get; set;}
    
    /// 他のコンストラクタを定義した場合は、
    /// デフォルトコンストラクタを必ず定義する
    public DataClass()
    {
        Str = "";
    }
 
    /// 自分で定義したコンストラクタ
    public DataClass(string str)
    {
        Str = str;
    }
}

ダミーのデータを作成する
管理クラスに対してダミーのデータをセットします。

        static void SetData(out Format.管理 manage)
        {
            var Names = new[] { "山田", "森", "大島", "野中", "中山", "小島", "中田", "吉田", "柳瀬", "井上", "小森", "佐藤", "小林", "松井", "篠田", "佐田" };

            var rnd = new Random(Environment.TickCount);

            Func<int,List<Format.店員>> GetMembers = (num) =>
                {
                    var ret = new List<Format.店員>();
                    Enumerable.Range(0,num).ToList().ForEach(p=>
                        {
                            var member = new Format.店員();
                            member.名前 = Names[ rnd.Next(0, Names.Count() - 1)];
                            member.雇用形態 = rnd.Next(0,100000) % 2 == 0 ? Format.雇用形態種別.社員 : Format.雇用形態種別.パート;
                            member.年齢 = rnd.Next(18, 60);
                            member.給与 = member.雇用形態 == Format.雇用形態種別.社員 ? (rnd.Next(20, 30) * 10000) : (rnd.Next(15, 20) * 10000);
                            ret.Add(member);
                        });
                    return ret;
                };

            Func<List<Format.商品>> GetGoods = () =>
            {
                var goods = new List<Format.商品>();
                goods.Add(new Format.商品() { 商品名 = "酒A", 売価 = 1000, 原価 = 800 , 在庫数 = rnd.Next(0,30), 販売数 = rnd.Next(0,30)});
                goods.Add(new Format.商品() { 商品名 = "酒B", 売価 = 2000, 原価 = 1500, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "酒C", 売価 = 2400, 原価 = 1650, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "菓子A", 売価 = 100, 原価 = 60, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "菓子B", 売価 = 50, 原価 = 10, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "文具A", 売価 = 100, 原価 = 80, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "鮮魚A", 売価 = 400, 原価 = 150, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "鮮魚B", 売価 = 980, 原価 = 700, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "肉A", 売価 = 398, 原価 = 240, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "肉B", 売価 = 1000, 原価 = 800, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "野菜A", 売価 = 298, 原価 = 150, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });
                goods.Add(new Format.商品() { 商品名 = "野菜B", 売価 = 398, 原価 = 300, 在庫数 = rnd.Next(0, 30), 販売数 = rnd.Next(0, 30) });

                return goods;
            };

            var StoreNames = new[] { "本店", "駅前店", "1丁目店", "4丁目店", "モール店", "中央店", "北店", "東店", "南店", "西店", "市場店" };

            Func<string, Format.店舗> GetStoreInfo = (name) =>
                {
                    var ret = new Format.店舗() { 店舗名 = name };
                    ret.メンバー = GetMembers(rnd.Next(10, 20));
                    ret.店長 = ret.メンバー.Where(p => p.雇用形態 == Format.雇用形態種別.社員).First();
                    ret.店舗商品 = GetGoods();
                    return ret;
                };

            var StoresInfo = new Format.店舗管理();
            StoreNames.ToList().ForEach(p => StoresInfo.店舗情報.Add(GetStoreInfo(p)));

            manage = new Format.管理() { 管理情報 = StoresInfo };

        }

ダミーのデータは次のようなデータが出来上がる
1店舗あたりのデータは次のデータが出来上がります。色々突っ込みどころはありますが、とりあえずテストなので。このデータが11店舗分できあがります。

----------------店舗名[市場店],店長[柳瀬]
店員情報
名前[柳瀬],雇用形態[社員],年齢[33],給与[230000]
名前[小島],雇用形態[パート],年齢[33],給与[160000]
名前[小林],雇用形態[パート],年齢[42],給与[190000]
名前[小森],雇用形態[社員],年齢[50],給与[230000]
名前[松井],雇用形態[パート],年齢[21],給与[170000]
名前[大島],雇用形態[社員],年齢[59],給与[250000]
名前[小島],雇用形態[パート],年齢[54],給与[160000]
名前[篠田],雇用形態[社員],年齢[40],給与[270000]
名前[大島],雇用形態[社員],年齢[39],給与[250000]
名前[柳瀬],雇用形態[パート],年齢[48],給与[150000]
名前[佐藤],雇用形態[パート],年齢[34],給与[150000]
名前[井上],雇用形態[社員],年齢[28],給与[200000]
名前[吉田],雇用形態[社員],年齢[46],給与[260000]
名前[小林],雇用形態[社員],年齢[47],給与[220000]
名前[野中],雇用形態[パート],年齢[58],給与[170000]
名前[中田],雇用形態[パート],年齢[33],給与[150000]
名前[大島],雇用形態[パート],年齢[51],給与[170000]
名前[吉田],雇用形態[社員],年齢[49],給与[260000]
商品情報
商品名[酒A],売価[1000],原価[800],在庫数[10],販売数[26],売上[26000]
商品名[酒B],売価[2000],原価[1500],在庫数[24],販売数[17],売上[34000]
商品名[酒C],売価[2400],原価[1650],在庫数[29],販売数[25],売上[60000]
商品名[菓子A],売価[100],原価[60],在庫数[28],販売数[3],売上[300]
商品名[菓子B],売価[50],原価[10],在庫数[17],販売数[23],売上[1150]
商品名[文具A],売価[100],原価[80],在庫数[23],販売数[29],売上[2900]
商品名[鮮魚A],売価[400],原価[150],在庫数[1],販売数[25],売上[10000]
商品名[鮮魚B],売価[980],原価[700],在庫数[29],販売数[7],売上[6860]
商品名[肉A],売価[398],原価[240],在庫数[15],販売数[8],売上[3184]
商品名[肉B],売価[1000],原価[800],在庫数[8],販売数[23],売上[23000]
商品名[野菜A],売価[298],原価[150],在庫数[7],販売数[6],売上[1788]
商品名[野菜B],売価[398],原価[300],在庫数[23],販売数[20],売上[7960]

objectからMessagePack(byte[])に変換する拡張メソッドを作成する
MessagePackは最終的にバイト配列が出来上がります。JSONやXMLは割りとライブラリ任せな部分があったけど、MessagePackはstreamを作ってあげます。
objectクラスにobjectからMessagePackへのPack,Unpackを行う拡張メソッドを作りたいと思います。

        /// <summary>
        /// object -> MessagePack(byte[])
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static byte[] Object2MessagePack<T>(this object obj)
        {
            var serializer = MessagePackSerializer.Create<T>();
            var ms = new System.IO.MemoryStream();
            serializer.Pack(ms, (T)obj);
            var ret = ms.ToArray();
            ms.Close();
            return ret;
        }

        /// <summary>
        /// MessagePack(byte[]) -> object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static T MessagePack2Object<T>(this byte[] bytes)
        {
            var serialize = MessagePackSerializer.Create<T>();
            var ms = new System.IO.MemoryStream(bytes);
            var ret =  serialize.Unpack(ms);
            ms.Close();
            return ret;
        }

JSON <-> MessagePackやXML <-> MessagePackなどはメソッド同士を組み合わせることで可能ですが、変換処理のオーバーヘッドが大きいので必要なければ作成しなくてもいいと思います。

JSON,XML,MessagePackに変換する
次のソースではテストデータをJSON,XML,MessagePack(byte[])で出力し、MessagePackのデータに関しては出力したデータを読み取って、元データと読み取りデータの文字列(JSON)を比較し、値が同じであるか確認しています。

       static void Main(string[] args)
        {
            var manage = new Format.管理();

            /// テストデータ作成
            SetData(out manage);

            /// テストデータ表示
            manage.Disp();

            /// JSON形式で出力
            System.IO.File.WriteAllText("manage.json", manage.Object2Json());
            
            /// XML形式で出力
            manage.Object2XDocument().Save("manage.xml");

            /// MessagePack形式で出力
            System.IO.File.WriteAllBytes("manage.bin", manage.Object2MessagePack<Format.管理>());

            /// MessagePack形式で読み込み
            var readData = System.IO.File.ReadAllBytes("manage.bin").MessagePack2Object<Format.管理>();

            /// 読み取りデータ表示
            readData.Disp();

            /// 元データと読み取りデータのJSON(文字列)を取得
            var str1 = manage.Object2Json();
            var str2 = readData.Object2Json();

            /// 比較してみる
            if (str1 == str2)
            {
                Trace.WriteLine("Equals");
            }
            else
            {
                Trace.WriteLine("Not Equals");
            }
            Console.ReadKey();
        }

実行結果

Equals

ファイル出力結果
驚嘆すべきはファイルのサイズです。※テストデータはランダムに作成されるので毎回同じサイズが出来るわけではない。


Img20121220143456

manage.bin 5,934 バイト
manage.json 22,314 バイト
manage.xml 56,078 バイト

XML対比10分の1の大きさにまでダイエットできました。全てをバイナリに置き換えているので小さくすむという単純な理由ですが、同じデータを抱えているのにこんなに小さくなるなんてー。
XMLのデータ

<?xml version="1.0" encoding="utf-8"?>
<管理情報>
  <店舗情報>
    <店舗名>本店</店舗名>
    <店長>
      <雇用形態>0</雇用形態>
      <名前>森</名前>
      <年齢>23</年齢>
      <給与>250000</給与>
    </店長>
    <メンバー>
      <雇用形態>1</雇用形態>
      <名前>小森</名前>
      <年齢>18</年齢>
      <給与>190000</給与>
    </メンバー>
    <メンバー>
      <雇用形態>0</雇用形態>
      <名前>森</名前>
      <年齢>23</年齢>
      <給与>250000</給与>
    </メンバー>
    <メンバー>
      <雇用形態>1</雇用形態>
      <名前>大島</名前>
      <年齢>54</年齢>
      <給与>170000</給与>
    </メンバー>
    <メンバー>
      <雇用形態>1</雇用形態>
      <名前>中田</名前>
      <年齢>35</年齢>
      <給与>160000</給与>
    </メンバー>
    <メンバー>
      <雇用形態>1</雇用形態>
      <名前>篠田</名前>
      <年齢>23</年齢>
      <給与>180000</給与>
    </メンバー>
    <メンバー>
      <雇用形態>1</雇用形態>
      <名前>佐藤</名前>
      <年齢>37</年齢>
      <給与>160000</給与>
    </メンバー>
    <メンバー>
   ・・・

まとめ
MessagePackはデータをバイナリとして扱うので軽量かつ高速で使用できることがわかりました。データがバイナリで保存されるとメモ帳などでファイルを開いた時にわけわかめ状態ですが、テキストとして開く必要のない場合には導入してみるのも面白いです。

様々なプラットフォームに対応
MessagePackはRuby,Python,Perl,C/C++,Java,PHP,JavaScript,Objective-C,C#,etc..様々な言語で使用することができます。データのフォーマットとしては統一されているので、試していませんが互換性があるようですので、通信フォーマットとしても使用するのも面白いと思います。

あー、今度使ってみようっと。

[C#]FormにFormをくっつける。

Formに操作用のFormを常にくっつけたい場合があります。

操作パネルFormがついてるコントロール
Img20121217165137

くっつける対象のFormが動いたりサイズが変更したりした場合、くっつくFormのポジションも当然変わりますので、イベントを追加してあげます。
Locationを変更する方法はいろいろなやり方があるので、一例を挙げます。

様々なポジションにFormをくっつける

位置別のLocationの求め方
Form1のウィンドウの周りにForm2の子ウィンドウを任意の場所に表示させる例を挙げます。Form1が移動したり、サイズが変更された時に、Form2にはその動きに追随するようにします。
Form1でForm2を生成した際に、Form2のOwnerにForm1を設定します。Form2でOwnerプロパティを使用し、Form1の位置(Boundsプロパティ)を参照し、自分が表示されるべきポジションを決めてやります。

Form2インスタンス生成時のOwnerの設定

            var childForm = new Form2[16];
            
            for (int i = 0; i < 16; i++)
            {
                childForm[i] = new Form2(i);
                childForm[i].Owner = this;
                childForm[i].Show();
            }

Form2内からOwner(Form1)の位置(Bounds)を参照し、新しい位置を求める
Form2を表示させるべき位置はPointクラスで指定します。OwnerのBoundsプロパティで得られるForm1の位置はウィンドウ上の座標(X,Y)と幅(Width)と高さ(Height)です。これらとForm2の幅と高さを組み合わせることでForm2を表示すべき座標(X,Y)を求めます。

        private void SetLocation()
        {
            var pLocation = this.Owner.Bounds;
            var myLocation = new Point();

            switch (_formNo)
            {
                case 0: /// 左上角
                    myLocation.X = pLocation.X - this.Width;
                    myLocation.Y = pLocation.Y - this.Height;
                    break;
                case 1: /// 上辺左端
                    myLocation.X = pLocation.X;
                    myLocation.Y = pLocation.Y - this.Height;
                    break;
                case 2: /// 上辺真ん中
                    myLocation.X = (pLocation.X + (pLocation.Width / 2)) - (this.Width / 2);
                    myLocation.Y = pLocation.Y - this.Height;
                    break;
                case 3: /// 上辺右端
                    myLocation.X = pLocation.X + pLocation.Width - this.Width;
                    myLocation.Y = pLocation.Y - this.Height;
                    break;
                case 4: /// 右上角
                    myLocation.X = pLocation.X + pLocation.Width;
                    myLocation.Y = pLocation.Y - this.Height;
                    break;
                case 5: /// 右辺上端
                    myLocation.X = pLocation.X + pLocation.Width;
                    myLocation.Y = pLocation.Y;
                    break;
                case 6: /// 右辺真ん中
                    myLocation.X = pLocation.X + pLocation.Width;
                    myLocation.Y = (pLocation.Y + (pLocation.Height / 2)) - (this.Height / 2);
                    break;
                case 7: /// 右辺下端
                    myLocation.X = pLocation.X + pLocation.Width;
                    myLocation.Y = pLocation.Y + pLocation.Height - this.Height;
                    break;
                case 8: /// 右下角
                    myLocation.X = pLocation.X + pLocation.Width;
                    myLocation.Y = pLocation.Y + pLocation.Height;
                    break;
                case 9: /// 下辺右端
                    myLocation.X = (pLocation.X + pLocation.Width) - this.Width;
                    myLocation.Y = pLocation.Y + pLocation.Height;
                    break;
                case 10: /// 下辺真ん中
                    myLocation.X = (pLocation.X + (pLocation.Width / 2)) - (this.Width / 2);
                    myLocation.Y = pLocation.Y + pLocation.Height;
                    break;
                case 11: /// 下辺左端
                    myLocation.X = pLocation.X;
                    myLocation.Y = pLocation.Y + pLocation.Height;
                    break;
                case 12: /// 左下角
                    myLocation.X = pLocation.X - this.Width;
                    myLocation.Y = pLocation.Y + pLocation.Height;
                    break;
                case 13: /// 左辺下端
                    myLocation.X = pLocation.X - this.Width;
                    myLocation.Y = pLocation.Y + pLocation.Height - this.Height;
                    break;
                case 14: /// 左辺真ん中
                    myLocation.X = pLocation.X - this.Width;
                    myLocation.Y = (pLocation.Y + (pLocation.Height / 2)) - (this.Height / 2);
                    break;
                case 15: /// 左辺上端
                    myLocation.X = pLocation.X - this.Width;
                    myLocation.Y = pLocation.Y;
                    break;
                default:
                    break;
            }

            this.Location = myLocation;
        }

位置イメージ
Img20121217170442

OwnerのMoveイベントとSizeChangedイベントに処理を追加する
Fomr1へのイベントハンドラへの追加はForm2を生成した時に,Form2のコンストラクタが呼ばれた後にForm2のOwnerプロパティへの設定が行われるため、Form2のコンストラクタに処理を書くことはできません。なので、Loadイベントに記述します。

        private void Form2_Load(object sender, EventArgs e)
        {
            SetLocation(); /// 1回実行しておく
            this.Owner.Move += (sarg, earg) => SetLocation();
            this.Owner.SizeChanged += (sarg, earg) => SetLocation();
        }

今回作成したソースはこちら

[C#]拡張メソッドが定義されているスタティッククラスとスタティック変数を使って、拡張プロパティ?っぽいことをやってみる。

staticな関数が複数あって、その中で使用されるある型のリストから、ある要素だけ除外したいなぁって考えた時、拡張メソッドが定義されているstatic classとその中のstatic変数を使って除外することができました。

stringの配列から”BLACK”のみ除外する例

stringの拡張メソッド用staticクラス

    public static class Extensions
    {
        /// <summary>
        /// 除外リスト
        /// </summary>
        private static List<string> _list = new List<string>();

        /// <summary>
        /// 除外リストに追加する
        /// </summary>
        /// <param name="str"></param>
        public static void AddList(this string str)
        {
            _list.Add(str);
        }

        /// <summary>
        /// 除外リストを取得する
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static List<string> GetList(this string str)
        {
            return _list;
        }
    }

使用例

    class Program
    {
        static void Print(List<string> strs)
        {
       /// 除外リストに登録されていない文字列のみ表示する
            foreach (var p in strs.Where(p => !p.GetList().Contains(p)))
            {
                Console.WriteLine(p);
            }
        }

        static void Main(string[] args)
        {
            var str1 = "RED";
            var str2 = "BLACK";
            var str3 = "BLUE";
            var str4 = "WHITE";

            var list = (new[] { str1, str2, str3, str4 }).ToList();

            Console.WriteLine("Print 1");
            Print(list);

            /// "BLACK"を除外したい
            str2.AddList();

            Console.WriteLine("\nPrint 2");
            Print(list);

            Console.ReadKey();
        }
    }

実行結果

Print 1
RED
BLACK
BLUE
WHITE

Print 2
RED
BLUE
WHITE

Print関数はstaticな関数ですが、class Program内で例えば除外リスト用のstatic変数を用意していません。stringクラスの拡張メソッド(AddList)を使用することにより拡張メソッドが定義されているクラス内のstatic変数に値が保持されますので、拡張メソッドが有効なスコープ内であればその値を参照できることになります。
Print関数自体が拡張メソッドでもいいし、ここでは文字列ですが自分の好きな条件で除外リストを作成してみても面白いです。
今までは除外リスト用の変数を例えば引数に追加するなどしていたのでちょっと便利ですね。

[C#]Microsoft Expression Encoderを使用して簡易画面キャプチャソフトを作る、その1。

画面キャプチャいろいろ調べていたらMicrosoft Expression Encoderが面白そうだったので、いろいろいじってみた。
動画の画面キャプチャについて、かなり簡単にできるようだったので、ちょっと作りこんでみた。無償版なら10分まで動画のキャプチャが可能。音声も録音できるので便利。

Expression Encoder 4
http://www.microsoft.com/japan/products/expression/products/encoder_overview.aspx

なお、こちらの方のエントリーを参照した。
http://code-life.net/?s=Expression+Encoder


実行画面

画面キャプチャのAPIがはじめから用意されていて、範囲を指定すると緑の枠が表示され、その範囲がキャプチャされる。キャプチャされたファイルは Expression Encoder 画面キャプチャ ファイル (.xesc) というファイル形式で保存される。このままではWindowsMediaPlayerでしか再生できないので、Expression Encoderの機能を使用して、wmv(VC-1コーデック)に変換して終了。これで他の動画プレイヤーやYoutubeにアップできる形式になる。
詳細はSDKをインストールした時にできるSDKフォルダの中のサンプルを参照すると良い。

動画キャプチャソース

                _scrJob = new Microsoft.Expression.Encoder.ScreenCapture.ScreenCaptureJob();
                _scrJob.CaptureRectangle = GetLocation();
                _scrJob.ScreenCaptureVideoProfile.FrameRate = 25;
                _scrJob.OutputScreenCaptureFileName = @"C:\Movie\aaa.xesc";
                var devs = Microsoft.Expression.Encoder.Devices.EncoderDevices.FindDevices(Microsoft.Expression.Encoder.Devices.EncoderDeviceType.Audio);
                _scrJob.AddAudioDeviceSource(devs[3]);  /// ここはデフォルトに設定されているデバイスがくるようにする
                _scrJob.Start();

キャプチャ対象位置の指定
キャプチャ画面の幅と高さかは偶数値を指定しないとだめなので、プロパティCaptureRectangle に渡す数値を決めるGetLocation関数は次のようになる。
※Formに貼り付けているPanelControlのサイズをキャプチャ対象とする場合。

        /// <summary>
        /// キャプチャ対象画面の位置を取得
        /// </summary>
        /// <returns></returns>
        public Rectangle GetLocation()
        {
            var rc = new Rectangle(panel1.PointToScreen(panel1.ClientRectangle.Location), panel1.Size);

            rc.Width = (rc.Width % 2 == 0) ? rc.Width : rc.Width + 1;
            rc.Height = (rc.Height % 2 == 0) ? rc.Height : rc.Height + 1;

            return rc;
        }

録音デバイスの指定
録音デバイスの指定で音声も録音できるようになる。複数の音声デバイスがある場合、デフォルトの音声デバイスが指定されるようにする。
さて、デフォルトの音声デバイスはどうやってわかるのやら。。。

var devs = Microsoft.Expression.Encoder.Devices.EncoderDevices.FindDevices(Microsoft.Expression.Encoder.Devices.EncoderDeviceType.Audio);
_scrJob.AddAudioDeviceSource(devs[3]);  /// ここはデフォルトに設定されているデバイスがくるようにする

wmv(VC-1)へのエンコード

MediaItem mItem;
mItem = new MediaItem(@"C:\Movie\aaa.xesc");
Job job = new Job();
job.MediaItems.Add(mItem);
job.Encode();

動画を連結する場合
複数の動画を連結させてからエンコードをかける事ができる。MediaItem()には引数なしのコンストラクタが無いので、初めのファイルをコンストラクタに指定し、後のファイルはMediaItemのSourcesに追加する。

var files = System.IO.Directory.GetFiles(_dir).ToList();
/// MediaItemのコンストラクタに1つめのファイルを指定
MediaItem item = new MediaItem(files.First());

/// 後のファイルをAddする
foreach (var file in files.Skip(1))
{
    item.Sources.Add(new Source(file));
}
Job job = new Job();
job.MediaItems.Add(item);
job.Encode();

動画を連続してキャプチャする
無償版は10分までキャプチャが可能であるが、10分を超えた場合自動でキャプチャが停止する。キャプチャが自動で終了した時に発生してくれるイベントがある。これを利用することで、次のキャプチャを開始すれば良い。
後はキャプチャ動画を連結してエンコードをかけることで、10分を超える動画の作成が可能。

イベントを利用して継続してキャプる例
キャプチャ動作を手動で停止した際にもイベントが発生してしまうので、状態管理を施しておく。Microsoft.Expression.Encoder.ScreenCapture.ScreenCaptureJob型のインスタンスを2つ(0,1)持っておき、交互に動作を切り替える。

            /// 動画キャプチャが自動停止した場合、新規JOBを自動的に開始
            job.ScreenCaptureFinished += (s, e) =>
                {
                    /// 状態がRECODEのみ新規JOB開始
                    if (_stat == MediaStatus.RECODE)
                    {
                        /// カレントのJOB番号を入れ替える
                        _currentJob = _currentJob == 0 ? 1 : 0;
                        StartNewJob();
                    }
                };
            
            /// 動画キャプチャ開始
            job.Start();

今回作成したソースはこちら※ExpressionEncoderとNewtonsoft.Jsonの参照が必要。

[C#]コントロールの値をずばっとまるごと保存、展開する。

画面コントロールを作成している時、設定ファイルにコントロールの値を保存したり展開したりする場合があります。
一つのコントロールを追加した時、そのコントロールに対しての処理を書かないでなんとかならないか考えました。

標準の画面コントロールはSystem.Windows.Forms.Controlから派生していますが、それぞれコントロールが持つ値のプロパティは異なっています。
その異なっている部分をDynamicを使用して吸収し、ControlのTypeから判定して設定すべきプロパティに値を入れたりします。

コントロールに対し値を保持するデータクラス

    /// <summary>
    /// Control's property.
    /// </summary>
    public struct Property
    {
        public string Name { get; set; }
        public dynamic Value { get; set; }
    }

Dynamicを使用したアクセッサ

    /// <summary>
    /// GetValue() Value is get.
    /// SetValue() Value is set;
    /// </summary>
    public struct GetSet
    {
        public Func<dynamic, dynamic> GetValue;
        public Action<dynamic, dynamic> SetValue;
    }

Type毎にDynaimcのアクセッサのDictionaryを作成

取得したいコントロールに合わせてアクセッサの定義をします。ここに記述されていないコントロールは対象外になります。

        public static Dictionary<Type, GetSet> GetTypeList()
        {
            /// Register Dictionary.
            /// SetValue for control type.
            /// GetValue for control type.

            /// ex)
            /// dic.Add( typeof(Control type), 
            ///     new GetSet() { GetValue = (p) => (Control setter),
            ///                    SetValue = (p) => (Control getter) });
             
            var dic = new Dictionary<Type, GetSet>();

            dic.Add(typeof(TextBox), new GetSet() { GetValue = (p) => p.Text, SetValue = (p, v) => p.Text = v });
            dic.Add(typeof(RadioButton), new GetSet() { GetValue = (p) => p.Checked, SetValue = (p, v) => p.Checked = v });
            dic.Add(typeof(ComboBox), new GetSet() { GetValue = (p) => p.SelectedIndex, SetValue = (p, v) => p.SelectedIndex = (int)v });
            dic.Add(typeof(DateTimePicker), new GetSet() { GetValue = (p) => p.Value, SetValue = (p, v) => p.Value = DateTime.Parse(v) });
            dic.Add(typeof(CheckBox), new GetSet() { GetValue = (p) => p.Checked, SetValue = (p, v) => p.Checked = v });
            dic.Add(typeof(ListBox), new GetSet() { GetValue = (p) => p.SelectedIndex, SetValue = (p, v) => p.SelectedIndex = (int)v });
            dic.Add(typeof(TrackBar), new GetSet() { GetValue = (p) => p.Value, SetValue = (p, v) => p.Value = (int)v });
            dic.Add(typeof(VScrollBar), new GetSet() { GetValue = (p)=> p.Value, SetValue = (p, v) => p.Value = (int)v });

            return dic;
        }

このDictionaryを使用して、値を保存、展開する関数を作成します。

全てのコントロールの値を取得する

        /// <summary>
        /// Control value is get
        /// </summary>
        /// <param name="coll">Control collection</param>
        /// <returns>Control value</returns>
        public static Property[] Get(System.Windows.Forms.Control.ControlCollection coll)
        {
            var dic = GetTypeList();
            var ret = new List<Property>();

            /// Control scanning
            foreach(Control ctrl in coll)
            {
                /// Child control recurisve call
                if(ctrl.Controls.Count > 0 )
                {
                    var c_ctrl = Get(ctrl.Controls);

                    ret.AddRange(c_ctrl);
                }
                /// know control, value is get
                else
                {
                    if (dic.ContainsKey(ctrl.GetType()))
                    {
                        ret.Add(new Property() { Name = ctrl.Name, Value = dic[ctrl.GetType()].GetValue(ctrl) });
                    }
                }
            }

            return ret.ToArray();
        }

全てのコントロールの値を設定する

        /// <summary>
        /// Control value is set
        /// </summary>
        /// <param name="coll">Control collection</param>
        /// <param name="val">Configuration file of a value</param>
        public static void Set(System.Windows.Forms.Control.ControlCollection coll, Property[] val)
        {
            var dic = GetTypeList();
            var ret = new List<Property>();

            /// Control scanning 
            foreach (Control ctrl in coll)
            {
                /// Child control recursive call
                if (ctrl.Controls.Count > 0)
                {
                    Set(ctrl.Controls, val);
                }
                /// know control, value is set
                else
                {
                    if (dic.ContainsKey(ctrl.GetType()))
                    {
                        var currentValue = val.Where(n => ((n.Name.Any()) && (n.Name == ctrl.Name)));
                        if (currentValue.Any())
                        {
                            dic[ctrl.GetType()].SetValue(ctrl, currentValue.First().Value);
                        }
                    }
                }
            }
        }

Formに貼りつけた全てのコントロールに対し値を保存、展開する処理を記述します。
コントロールの値はProperty型のデータクラスを使用し、この値はJSON形式にして保存・展開することにします。

JSONについてはこちらのエントリーを参照してください。
コントロールの値をファイルに保存

        /// <summary>
        /// Save all control value.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Save_Click(object sender, EventArgs e)
        {
            /// Get all control value by ControlProperty method.
            var ctrlList = ControlProperty.ControlProperty.Get(this.Controls);

            /// Write all control value use JSON file.
            System.IO.File.WriteAllText("ctrl.json", DynamicJson.Serialize(ctrlList.ToArray()));
        }

ファイルからコントロールの値を設定

        /// <summary>
        /// Set all Contorl value.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Load_Click(object sender, EventArgs e)
        {
            /// Read all control value by JSON file.
            ControlProperty.Property[] val = DynamicJson.Parse( System.IO.File.ReadAllText("ctrl.json"));

            /// Set all control value by ControProperty method.
            ControlProperty.ControlProperty.Set(this.Controls, val);
        }

これでコントロールを追加しても何もしなくても値が保存・展開されます。


実行画面

<追記>
このソースを公開致します。
https://github.com/motoki-kimura/ControlProperty

DynamicJsonを参照に加えることで、コンパイルが成功します。