手続き型音楽の日常

関数型音楽に乗り換えたい

コレクション初期化子とオブジェクト初期化子はどちらが優先されるのか

ちょっと気になったのでメモ。

結論

オブジェクト初期化子のほうが優先される

実験

No.1

まず、普通にオブジェクト初期化子を書く。

using System.Collections.Generic;

namespace ClassInheritedListSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Hoge hoge = new Hoge { A = 1, C = 2 };
        }
    }

    class Hoge
    {
        public int A { get; set; }
        public string B { get; set; }
        public int C { get; set; }
    }
}

オブジェクト初期化子に違いない。

No.2

次に、 List<int> を継承してみる。

using System.Collections.Generic;

namespace ClassInheritedListSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Hoge hoge = new Hoge { A = 1, C = 2 };
        }
    }

    class Hoge : List<int>
    {
        public int A { get; set; }
        public string B { get; set; }
        public int C { get; set; }
    }
}

オブジェクト初期化子のままだ。

f:id:yuzutan_hnk:20170923022100p:plain:w600

No.3

次に、 Program クラス側に同様のプロパティを定義する。

using System.Collections.Generic;

namespace ClassInheritedListSample
{
    class Program
    {
        public static int A { get; set; }
        public static string B { get; set; }
        public static int C { get; set; }

        static void Main(string[] args)
        {
            Hoge hoge = new Hoge { A = 1, C = 2 };
        }
    }

    class Hoge : List<int>
    {
        public int A { get; set; }
        public string B { get; set; }
        public int C { get; set; }
    }
}

オブジェクト初期化子のままだ。

f:id:yuzutan_hnk:20170923022359p:plain:w600

No.4

Program に用意したプロパティの型を変えてみる。

using System.Collections.Generic;

namespace ClassInheritedListSample
{
    class Program
    {
        public static int A { get; set; }
        public static int B { get; set; }
        public static int C { get; set; }

        static void Main(string[] args)
        {
            Hoge hoge = new Hoge { A = 1, B = 2 };
        }
    }

    class Hoge : List<int>
    {
        public int A { get; set; }
        public string B { get; set; }
        public int C { get; set; }
    }
}

これもオブジェクト初期化子のまま。 Hoge.B の型が int じゃないと怒られる。

f:id:yuzutan_hnk:20170923022737p:plain:w600

No.5

かっこで括る。

using System.Collections.Generic;

namespace ClassInheritedListSample
{
    class Program
    {
        public static int A { get; set; }
        public static int B { get; set; }
        public static int C { get; set; }

        static void Main(string[] args)
        {
            Hoge hoge = new Hoge { (A = 1), (B = 2) };
        }
    }

    class Hoge : List<int>
    {
        public int A { get; set; }
        public string B { get; set; }
        public int C { get; set; }
    }
}

やっとコレクション初期化子になった。

f:id:yuzutan_hnk:20170923022932p:plain:w600

どうしてこうなるのか

C# 5.0 の言語仕様 7.6.10.3 項を見ると、書いてあります。

コレクション初期化子は、"{“ トークンと ”}“ トークン内のコンマで区切った要素初期化子のシーケンスから構成されます。各要素初期化子は、初期化対象のコレクション オブジェクトに追加する要素を指定し、”{“ トークンと ”}“ トークン内のコンマで区切った式のリストから構成されます。単一式の要素初期化子は、 かっこなしで作成することができますが、メンバー初期化子とのあいまい性を防ぐため、代入式にすることはできません 。non-assignment-expression の生成は 7.18 で定義されています。

ここでいうメンバー初期化子とは、たぶんオブジェクト初期化子で使用される A = B の形式のものだと思われます。明確な定義が見つけられないのですが、オブジェクト初期化子の説明にもメンバー初期化子という言葉が使われています。

オブジェクト初期化子では、メンバー初期化子の形式は次のようにあります。

オブジェクト初期化子は、"{“ トークンと ”}“ トークン内のコンマで区切った一連のメンバー初期化子から構成されます。各メンバー初期化子では、 初期化対象のオブジェクトのアクセス可能なフィールドまたはプロパティを指定し、その後に等号と式、またはオブジェクト初期化子かコレクション初期化子を指定する 必要があります。オブジェクト初期化子に、同じフィールドまたはプロパティに対する複数のメンバー初期化子を含めるのは誤りです。オブジェクト初期化子は、初期化対象の新しく作成したオブジェクトを参照することはできません。

上記二つから考えるに、コンパイラ的には

  • 名称 = から始まる要素が1つでもあれば、オブジェクト初期化子
  • のみで構成される要素しかなければ、コレクション初期化子

と分類している気がします。

dynamic なオブジェクトに IntelliSense を表示する

ネタです。ホントは出ちゃいけないんじゃないの?という記事。

Visual Studio 2015 で最初確認したんですが、家に帰ってきて 2017 でやってみても同じ結果でした。

Roslyn のせいなのかな?

コード

namespace dynamicIntelliSenseTest
{
    class Program
    {
        static void Main(string[] args)
        {
            (0 as dynamic) // この後ドットをつけると補完が出てしまう
        }
    }

    static class Hoge
    {
        public static void ホントは出ちゃいけないんじゃないの(this object hoge){
            return;
        }
    }
}

f:id:yuzutan_hnk:20170908012633p:plain:w600

dynamic の実態は object 型なので間違ってはいないのですが、入力した後は定義を紛失するようです。

f:id:yuzutan_hnk:20170908013007p:plain:w600

OWASP Nagoya Chapter Meeting 1st に参加してきた話

f:id:yuzutan_hnk:20170903144157j:plain:w300

よりによって普段セキュリティなどまったく気にしない私が、よりによってセキュリティの世界的コミュニティ OWASP の名古屋チャプター、よりによって設立後初の勉強会に、よりによって参加してきた話。

先日の Xamarin 勉強会の懇親会でお世話になったまるちゃんさんが宣伝していたり、前々から connpass で見ていたりして知ってはいましたが、直前まで参加するかどうかは悩んでいました。

でも、何事もやらなきゃはじまらないな、と思い立ち参加してきました。

私のセキュリティレベル

前の Xamarin の記事を読んでいただければわかると思いますが、ソフトウェアエンジニアやっていながらこういった問題に関しては全くの無知です。

CVE?とか監視してないし、 Java のアップデートとかセキュリティに特に重要になってくると思いますが、何か月前から放置してるし。

Xamarin の場合は、 Mono とか .NET とか前提知識があったので何とかついていけましたが、セキュリティに関しては本当にわからないことだらけ。というか何を勉強すればよいのかもわからない。

でも、せっかくの OWASP Nagoya Chapter 設立後初の勉強会だし、きっかけ作りとして行ってみてもいいかなと思って行ってみることにしました。

とりあえず前日の夜から CTF を少しやってみたんですけど、 SQL インジェクションできずに諦めて寝ました。朝6時に。

迎えた当日

会場は、名古屋駅からほど近い愛知大学名古屋キャンパスの 8 階の教室。

あの 109 シネマズの目の前にある大学で、設備がもはやオフィス並み。エスカレータはおろかエレベータまでついてる。というか 8 階まであるって、とんでもない大学ですね…

名古屋駅側から地上を歩いていくと、誘導係の方が数人。ちょっと陰になったところに入り口がありましたが、わかりやすい説明のもと迷わず行くことができました。

実は 100 人規模の勉強会だけあって、教室は超満員。ひろい講義室にプロジェクタの画面 3 つとでっかい BOSE のスピーカー。

大学の講義を聴くような感覚ですが、なかなか面白そうな雰囲気で勉強会がはじまりました。

勉強会スタート

OWASP Nagoya Chapter Leader 坂梨 さん

まず初めに、 OWASP Nagoya Chapter の Chapter Leader を務めていらっしゃる、坂梨さんから OWASP についての簡単な説明がありました。

OWASP とは

OWASP - Open Web Application Security Project は、 セキュアなソフトウェア開発を促進する技術、プロセスに関する情報を普及・啓発するオープンコミュニティ 。もっと詳しいことを話されてましたが、早すぎてメモれず…。

OWASP

コミュニティ内でのノウハウを集めたドキュメントや、ツールなどが無料で使えるというものらしいです。

世界各地に チャプター というローカルな活動拠点があり、その数 276 チャプターが現在あるそう。

さらに、 OWASP Project というプロジェクトがいくつもあり、代表的な

  • OWASP TOP 10
  • OWASP Zend Attack Proxy (ZAP)

をはじめ全部で 93 もあるそう。活発ですね。

OWASP Nagoya の使命

日本には OWASP Japan をはじめ、全部で 10 のチャプターがあり、 OWASP Nagoya はその 記念すべき 10 番目。

世界中で議論されているセキュリティ情報を名古屋から広め、名古屋から世界に向けて新たな情報を発信していくことが OWASP Nagoya Chapter の使命だそうです。

Nagoya といえど名古屋に限らず、東海地区を中心に活動していくんだそうです。確かにこの辺りにセキュリティ関連の団体ってなかなかないですよねー、って思ったんですけど、

  • 名古屋情報セキュリティ勉強会
  • ISACA 名古屋支部
  • CISSP 東海コミュニティ

と、案外セキュリティに関する団体はあるみたいで、連携を取りながら活動をしていきたいそう。

OWASP Kansai Chapter とのテレビ通話

OWASP Kansai も同日に勉強会が開催されていたそうで、ビデオ通話で会場を繋げました。

どこかのカフェなのか、おしゃれな丸テーブルが並び 4, 5 人グループになって座っているような形で、なかなか気楽なムードでした。

あちらも 100 人越えの規模で、熱気はどちらも劣らず。コミュニティってすごいなぁと思いました。

ただ、サブ画面でビールサーバからビールを注ぐ映像が淡々と流れていて、めっちゃラフな勉強会だな!?と驚いたりしたんですが、後々聞いてみたら大阪はいつもあんな感じらしいです。さすが大阪…。

感想とか

connpass で OWASP を初めて見たとき、セキュリティに関してだからすごく高度で、ちょっとおかたい雰囲気なのかなと早とちりしていたのですが、実際に来てみるとそうでもなくて、とても楽しそう。

肩の力が抜けたというか、何の変哲もないただの紹介のお話だったのに、どこか面白かった。

こういう空気もいいなぁ、と思いました。

OWASP Nagoya Chapter Leader 岡田さん

株式会社 アスタリスク・リサーチ 代表取締役。 OWASP Japan Chapter Leader でもあるすごい人。

2012 年 3 月に OWASP Japan ができる前、 OWASP Tokyo なるものがあり、そこで OWASP Developer Guide を翻訳したりしていたらしいです。もはや大御所ですね…。

スペアリブ美味しそう。

OWASP Japan への思い

OWASP Tokyo ができる少し前、1年ほどかけていろんなサイトのセキュリティに関しての対策を、サイトの設立者のもとまで足を運んてヒアリングして回ったことがあるそう。

その結果、パスワードをグループで使いまわすシステムだとか、 HTTPS に対応していなかったりだとか、システムを停止させたくないのでアップデートをしないだとか、そうしたセキュリティへのおざなりな姿勢が浮き彫りになったそうです。

もっと気軽にセキュリティに対して興味を持ってもらえる環境を作っていきたいと思っていた矢先、 OWASP Tokyo が設立からわずか 3 ヶ月で解散してしまいます (もともと企業絡みだったため) 。

そこで、日本全国に門戸を開こうと、 OWASP Japan Chapter を設立 (再設) されたそうです。

現在では全国に 9 つのローカル Chapter が存在していますが、 OWASP Japan ではそうした Chapter がない場所でも積極的に活動を行っていく方針だそうです。

聴いていて、心にグッとくる内容でした。

OWASP について

坂梨さんが説明されていましたが、もっと詳しく説明をされました。

Who is OWASP

「OWASP が~~と言っているから」とネット上に書き込んでいる人がいるみたいだそうですが、コミュニティの名前であって、人の名前ではない。

OWASP のメンバー

米国にいる中心メンバー数人以外は、全員ボランティア。

ボランティアだからこそ、プライスレスに活動ができることも多々あるそう。スポンサーがついてくれるということでしょうか?

OWASP’s DNA

OWASP のモットーは、「 やってから謝る 」こと。

いつかの APSEC というカンファレンスで Facebook の CIO がこんな発言をしたそうで。

最善のセキュリティを尽くせないのであれば、ほかのことをやれ

たとえば Facebook のユーザの 1/3 は、端末が SHA-1 ハッシュ化しか対応しておらず、高度な暗号技術を利用できない状態にあるのですが、それをセキュリティを重視するからといって切り捨ててしまうのはおかしい。

つまりセキュリティ管理者は、そうした 最善のセキュリティを尽くせないユーザ に対応するため、 プランB を考えなければならない。

たとえ先進国では最善の策を講じるとしても、ほかの地域では強制的に対応することはせず、ほかの認証や技術によってそれを補う形でセキュリティを担保する。

こうしたときに重要になってくるのは、 いろんなやり方を否定しない ことであり、発言したり実行したりした後から「この問題は考えていませんでした」というパターンも当然ありうるそう。

だから、 やってから謝る 。なんだかこのあたりは終始考えさせられる内容でした。

OWASP の活動

OWASP Japan だけでも Chapter Leader はたくさんいるし、世界ではもっとたくさんいるそう。 Leader がたくさんいるということは、他のメンバーもたくさんいるわけで。

世界中で様々な議論がされ、その成果物が無料でネット上にアップされているそう。ちょっとわからないことがあれば OWASP で検索すればなんでも出てくる、というレベルで。

基本的に英語で書かれている (英語で議論された) ものですが、日本のメンバーによって翻訳されているものがいくつもあるので、 (チートシート、 IoT プロジェクトのドキュメントなど) どんどん活用していってほしいそうです。

さらに今回のような勉強会で集まれば、同じ興味を持った仲間の顔が見れるからいいね、とおっしゃっていました。やっぱこうして集まるといいですね。

すごい!飲み会

すごい!飲み会

セキュリティへの 3 つの視点と、シフトレフト

このセクションの冒頭前後、異常な腹痛に襲われてしまい席を外していたので、当時の TL と戻ってきてからの話でちょっとだけ推測して書きます。間違ってたら容赦なくご指摘ください。

Shift Left とは

セキュリティを考えるうえでまず思い立つものといえば、一般的に 何が盗まれたか、何が壊されたか 。しかし、セキュリティはそんなに甘くはない。

顧客のクレジットカード番号が漏洩した、という問題について、それはなぜ起きたのかを考える必要がある。これは、どうして漏れたのか、すなわち どんな準備が不足したから問題が起こったのか という、問題が起こる前のことを考えるということ。

さらに言えば、 なぜ不足してしまったのか という根本的なことも考えていく必要がある。

そうした、時系列にして前に々にと考えていく (一般的に時系列は左から右に進むため、その逆である 左に進む ) ことを シフトレフト というらしい。

そして、シフトレフトを考えるうえで重要な 3 つの視点について解説してくださった(この部分が一番わからない、完全なる予想)

Attack Surface

単純に、 どこから攻められるか 。アプリケーションで言えばどの画面から攻められるか、システム的に考えればどの機能から攻め込まれるか。

日本語で言えば 攻撃面 。穴が露出していればそこから入ってきてしまうということですね。きっと。

攻撃側はそうした 攻撃面の調査 、それを踏まえた 脆弱性の発見 を売買するエコシステムを成立させていて、一度見つけられてもすぐには利用されない。いつかどこかでその情報を買った人が、自分が作ったウイルスに組み込んで使う、ということが一般的だそう。

(ここら辺から復帰)

つまり根本的には、脆弱性の情報が売られないため、攻撃面 (弱い面) を見つけられないということが重要だそうです。

攻撃面を意識すれば、おのずと脆弱性は防がれ、攻撃されない、という根本的解決につながる。これがシフトレフト的視点。

OWASP には Attack Surface脆弱性、それによって盗まれる可能性のあるデータが関連付けられて掲示されているみたいです。気になったときには調べてみたい。

IoT Attack Surface Areas - OWASP

コンポーネント

昨今では、エンジニアが製作するシステムのうち、 OSS の割合がほとんどを占めていて、実際にエンジニアが作成しているシステムはほんの一部になっている。

つまり、自分で書いたプログラムやシステム以外の場所に脆弱性がある可能性が非常に高く、実際にそれらを知らずに使うことが多いそうです。

天才がプログラムを書けば 10000 行につき 2,3 個のエラーで済むものの、普通のプログラマが書けば 1000 行に 1 個の割合でエラーが含まれるそうで、そう考えると OSS脆弱性がある可能性ってものすごく高いんですね。

自分ですべての責任を持っているプログラムを出荷しているプログラマって、本当に少ないそう。

使うコンポーネントはしっかりと選ぼう、ということでした。

例として挙げられていたのは、とある超有名な OSS のソースの中に、 パスワードが入力されていなければ ××× にする というロジックが含まれているのを見たことがあるらしい。つまり、パスワードがばれているようなもの。

恐ろしいことに、これが脆弱性として報告されていない (正規のプログラムとみなされている) んだそうです。

こうした問題を許せるようなプロダクトであればいいのですが、そうでないプロダクトの場合は言語道断でしょうし、しっかりと調べたうえで使わなければいけないさそうですね。

プロセス

名古屋では有名なかんばん方式、これはプログラムには当てはまらない。なぜなら、プログラミング自体が設計に値するから。

けれど、プログラムを作るにも設計は必要。スケジュールとか納期とかそういったものだけではなく、セキュリティに対しても重要だそうです。

OWASP TOP 10OWASP Proactive Controls のようなドキュメントには、 どうすれば脆弱性が止まるか というベストプラクティスが書かれている。日常生活で言えば、手洗いやうがいにあたるらしい。そう考えると、たしかに簡単な予防を続けることで、大きな事故にならなくて済むということは、よくありますね。

システムを作る初期の段階から、運用を見据えて設計していき、脆弱性を潰していく。常に前段階でユニットテストを行い、盤石なシステムを築いていくということが大事。

OWASP で公開されている Open SAMM を使うと、プロジェクトの成熟度を定量的に測ることができ、どこに力を入れているのか、そしてどこが弱いのかを視覚的に表せるため、おすすめだそう。

www.opensamm.org

まとめ

岡田さんの SHIFT LEFT やセキュリティへの熱い思いがとても伝わってきました。

OWASP Nagoya で初めてだからなのかとても概念的な話が多かったのですが、 OWASP がどういう方向性で、セキュリティを考えるとはどういうことかという、とても基礎的なものが学べたと思います。

学校でも仕事でも、これまで具体的な技術とか攻撃の方法だけとか、いわゆる 即戦力 になる知識しか得てこなかったのですが、ちょっと視点を変えるだけでセキュリティに対してこんなにも熱くなれるんだという、 経験力 を見ることができて、幸せです。

なかなかこういう機会、私は持ち合わせていないので新鮮…。

OWASP Japan Advisory Board 徳丸さん

徳丸浩のWebセキュリティ教室(日経BP Next ICT選書)

徳丸浩のWebセキュリティ教室(日経BP Next ICT選書)

とてもすごい人だそうです。お名前だけは伺っております。

先のお二方とは違い、格段にレベルの高い実践的なお話をしていただきました。

脆弱性とは

脆弱性、と一言で言っても多種多様。たとえば、

  • パスワードを推測され、 認証を突破される
  • マルウェアによって 認証を突破される
  • 基盤ソフトの 潜在的な弱みを突かれる
  • 自分で書いたプログラムの 弱みを突かれる

簡単に言えば、そうした脆弱性というのは 悪用されるバグ のことであって、 バグを減らせばセキュリティ向上がある程度見込める のだそう。

国際的な分類

脆弱性の情報を国際的な基準で分類する仕組みがある。

  • CVE
    • Common Vulnerabilities and Exposures
    • ソフトウェア個別の脆弱性情報
    • 米国政府の支援を受けた MITRE 社が採番
    • 既知の脆弱性を対策するということは、 CVE を参考にすることが多いらしい
  • CWE
    • Common Weakness Enumeration
    • 汎用的な脆弱瀬瑛を識別する
    • 体系化されている

信頼境界とは

Java セキュアコーディングスタンダードの例を解説されていました。

分類番号 IDS00-J は、もともと「境界を越えたデータを信頼しない、無害化しなければならない」という意味のコードだったそう。

ここでいう境界とは 信頼境界 と呼ばれるもので、システムの外側との境目、つまりシステム設計時に不明な領域との境目ことらしい。つまり外側の領域は、簡単に言えばユーザからの入力やディスク環境。

解説ではデータベースのやり取りにおいて、信頼境界外から受け取ったデータをデータベースと連携して利用するときは、必ず無害なデータに変換しなければならない(フィルタにかける)としているそう。

この説明を聞いて、誰もが システムの外側 (ユーザからの入力など) を自身が実装しているシステムの信頼境界で無害化し利用する必要がある と解釈するだろう、と徳丸さんは言う。

そんなこと本当にできるの?と徳丸さんは続ける。

実際、無害かどうかはそれを解釈する側の実装や仕様による。つまりデータベース側の実装によって無害かどうかの基準が変わってくるということだ。このことは Java セキュアコーディングスタンダードも把握して書かれているようで、 解析器側に無害化アルゴリズムが存在しているのであればそれを使うのが望ましい と解説されているそうだ。

多くのデータベース (またはそのやり取りを担保するフレームワーク) には、 プレースホルダ という無害化する実装がある。 SQL を穴あきの状態にしておき、そこにデータを埋め込む作業をデータベース側が行うというものだ。 ※動的プレースホルダについては割愛

もはや、 システムの信頼境界を考えるのは無意味だったのだ 。というか、データベース側の信頼境界をそのまま流用しているような気がする (私たちのコード自体が信頼境界外)。

この話のオチは、 Java セキュアコーディングスタンダードの IDS00-J につけられたタイトルが「 SQLインジェクションを防ぐ 」になったこと。解説にももはや信頼境界などという言葉は出てこないそう。

でも、徳丸さんはこれもセキュリティの進化の結果だと捉えているそうだ。こうしてセキュリティは進化していくんだなぁ…? (わかってない)

信頼されるデータとは

IPA が公開している「安全なウェブサイトの作り方」にも、信頼境界という言葉は出てこないけれど、概念自体は登場するそう。

というか、自分たちにとって信頼が必要なデータは何かを考えることが大事だそうだ。

例えば、

  • プログラムコードそのもの
  • SQL 文、 eval 文
  • 設定ファイル名
  • 正規表現
  • オブジェクト

といったものは、プログラムが動いていくうえで信頼されていなければならない。こうしたものを、いわゆる信頼境界の外側、 外部から入力できないようにする ということが重要。

どうしてもこれらを外から指定しなければならないとき (掲示板で HTML の装飾タグを使いたいとか) は、フィルタを使って無害化を行う。

  • ログイン済みユーザ名 (認証)
  • 管理者が入力できるようにした SQL (認可)
  • HTML タグを制限 (フィルタリング)
  • 外部からのファイル名 (basename)

上記のように、信頼するしかないデータ、信頼できなければ無害化すれば良いデータ、はじめの方に出てきた信頼しなくても良いデータ (プレースホルダに入れる場合など) と、データの意味を理解してセキュリティ対策を行う必要がある。なるほど深い。

よくわかるPHPの教科書

たにぐちまこと著、『よくわかるPHPの教科書』という本を参考に、セキュリティ攻撃の実践を行ってくださいました。

さて、皆さんお待ちかねの時間ですね。誰だ祭りだなんて言ったのは。

SQL インジェクション攻撃

セキュリティ攻撃の代表格とも言える、 SQL インジェクション。サーバサイドプログラミングに多少の実装差異や漏れがあると、悪用されてしまう。

たとえば $id に 「 88-1 」という文字列が入っていたとする。

  • UESR_NUMBER = sprintf('%d', $id) とすると USER_NUMBER = 88 と解釈される
  • UESR_NUMBER = $id とすると USER_NUMBER = 88-1 となり USER_NUMBER = 87 と解釈される

と、ほんの些細な実装差異によって、本来意図した挙動を示さなくなります。

この脆弱性を利用して、簡単な掲示板サイトで SQL インジェクション攻撃を実演されました。記事を削除するときにユーザ ID を偽装し、いともたやすく他人の投稿を消すことができてしまいました。

このようにエスケープ処理を施さず SQL 文に入力文字列を直接連結してしまうのは大いに危険だと言います。

応用的な SQL インジェクション攻撃として、 ブラインド SQL インジェクション も紹介されました。これは、 SQL が副問い合わせに対応していることを悪用し、 ANY(SELECT USER_NUMBER FROM T_USER) などと入力して攻撃を行うことだそう。これが恐ろしいのは、たった一つのデータではなくすべてのデータがヒットしてしまうということ。

実演として、たった20行足らずのプログラムで実際にデータベースの中身をすべてかっさらっていかれました。ユーザ名とパスワードのハッシュ値が駄々洩れの状態に。

世間にはこうした SQL インジェクションを行いやすくする、通称 SQL 注入工具というものが出回っているそうで、中にはセキュリティ企業が販売しているものもあるそう。誰でも簡単に行えるようになっているみたいです。

ますます SQL インジェクションが恐ろしいものだということがわかってきました。ちゃんとプレースホルダを使おう、文字コードを指定して文字化け攻撃を食らわないようにしよう、ということでした。

CSRF 攻撃

日本でもこの手口を用いて、なりすまし犯行予告事件が起きました。

細工されたサイト…具体的には iframe が埋め込まれたサイトを用意して、その中に action 属性に爆撃先サイトを指定した form タグを配置します。 iframe の高さを 0 にしておけば、見た目はわかりません。

そうすると、それを開いたブラウザが iframe の中身を解釈し、そこにある任意のコードを実行してしまいます。実行するのは閲覧者のブラウザですので、完全になりすましができてしまいます。

このような攻撃への対策としては、セッション毎にハッシュ値トークンを比較して認証を行うことで、不正に CGI へコマンドを送っても処理しないようにする、という対策が有効だそうです。

XSS (クロスサイトスクリプティング)

ブラウザで見ているドメイン (オリジン) 上の他のサイトで任意の javascript を実行できる、という脆弱性を活かした攻撃。

ウイルスではないとされているそうですが、ワームには分類されるそう。

少し前までは (学校で習ったような内容としては) 、プログラム文字列が掲示板に登録されてしまいそれが潜在的にブラウザに残り、別のサイトを閲覧中に発動してしまう、ということが多かったそうですが、最近はもっと手口が多くなっているそう。

今回紹介されていたのは、掲示板におけるアイコンファイルの細工。アップロード時に画像ファイルかどうか判別するためにファイル名の下 3 桁をチェックしていましたが、ピリオドまではチェックしていないため、拡張子なしのファイルがアップロードできてしまいました。

サーバ設定によっては拡張子なしのファイルを text/html とみなして実行することができるそうで (規定では OFF になっているけど) 、アイコンとして表示しているにもかかわらずブラウザ上では通常のサイトと同じ状態で解釈し、 javascript を実行してしまいます。

ブラウザ上で実行されるだけでなく、二重拡張子を使うなどしてサーバ上で実行可能なプログラムがアップロードされると、サーバサイドがそもそも危険にさらされることになります。とくに実演では webshell というブラウザからサーバ上で UNIX コマンドを実行できるという チートな プログラムをアップロードし、サーバを完全に乗っ取っていました。本当に怖い。

感想など

実演や時折笑いを交えながらわかりやすく解説され、セキュリティの重要性や危険性が身に染みてわかりました。

数々の脆弱性は局所的な対策の積み重ねで食い止めることができる、と最後におっしゃっていたのですが、私のようにセキュリティに疎いとその重大さに気づいていないから、おざなりな姿勢になってしまうのかなと思いました。

中途半端な憶測や推測をせず、しっかりと地道に対策すれば、セキュアなシステムは実現できる。改めて実感しました。

OWASP Nagoya Chapter Leader 村井さん

勉強会の最後に、 OWASP Nagoya のこれからの活動について紹介してくださいました。

OWASP Nagoya Local Chapter Meeting / OWASP Day

スクール形式の勉強会や、外部講師を招待しての講演、 OWASP で公開しているドキュメントの紹介など、 至って真面目な イベントのようです。とはいえ今回のムードからしてそこまでお堅くはなさそう。

直近では、 11月に OWASP Nagoya Local Chapter Meeting #2 が開催されるそう。行かなくちゃ!

OWASP Nagoya Hands-On

ハンズオン形式でのセミナー、 OWASP で公開しているツールの紹介など、 至って真面目な イベントのようです。座学だけではわからないことも、ここでしっかりと理解できそう。

直近では、 2018年 3月に OWASP Nagoya Hands-On #1 が開催されるそう。行かなくちゃ!

OWASP Night in Nagoya

すごい!飲み会

すごい!飲み会

セミナーや、 LT で行う カジュアルな イベントのようです。わいわいと盛り上がりながら、セキュリティへの意欲を高めていくのだそう。

直近では、 2018年 1月に新年会も兼ねた OWASP Night in Nagoya #1 が開催されるそう。行かなくちゃ!

OWASP World Tour 2017 Tokyo / Nagoya Satellite

一般募集だと思った?残念、来場特典でした!

2017 OWASP World Tour Tokyo - OWASP

9月 30日に行われる OWASP World Tour 2017 の東京講演を名古屋で視聴できるサテライトイベント。今回の勉強会のまさにその場で connpass に公開していただき、真っ先に応募することができました。

会場は今回と同じ愛知大学で最大 40人 のところ、公開直後すぐ満席状態になってしまいました。私はとりあえず確保できました。

OWASP の Wiki を見ると会場変更されるかも?定員増えるかも?と示唆されているので、応募すればもしかしたら優先的に確保してもらえるかも。行かなくちゃ!!

懇親会に参加してみて

f:id:yuzutan_hnk:20170903144159j:plain:w300

勉強会終了後、懇親会も行ってきました。

まともに飲み会も懇親会も行ったことがない中この規模に終始圧倒されていましたが、いろいろな話ができて面白かったです。

隣に座っていた某すごい方が岡田さんと対等 (?) に話をされていたのがとてもうらやましい。というか、自分の力不足さにとても失望した。もっと頑張らなければ、と改めて自覚しました。

おわりに

今回の勉強会は、初めは軽い気持ちで参加したけれど、振り返ってみると参加してよかったなぁと感じます。

セキュリティに対する関心を持てましたし、これからプログラムを作っていくうえでどれだけセキュリティが重要かを再確認できた気がします。

信頼境界のくだりは最近自分でも考えていたテーマで、ユーザをどこまで信用するか、どこで信用度チェックをするのか、という問いにひとまず結論がついたように思えます。たぶんすべてではないのですが。

自分の中で多くのきっかけができました。本当にありがとうございました。

最後になりましたが、長々と最後までお読みいただきありがとうございました。そして、お疲れ様でした。

なごやで Xamarin 開発者と Xamarin 勉強会 に行ってきた話

先日 8/19、「なごやで Xamarin 開発者と Xamarin 勉強会」という勉強会に参加してきました。

jxug.connpass.com

Xamarin 経験者向けの、ちょっと踏み込んだ勉強会です。

たまたま connpass を漁っていたら引っかかって、「 名古屋じゃん! 」「 超有名なちょまどさん来るじゃん! 」「 Xamarin じゃん! 」というノリで参加を決定。

抽選枠も絶対参加したい枠も満杯で、かろうじて 立ち見枠 が1席(?)空いていたので、そこに滑り込ませていただきました。

というわけで、行ってみた感想や学んだこと含め、僭越ながらまとめさせていただきます。

私の Xamarin スキルレベル

実は私、 Xamarin の経験は 全くない状態 で参加させていただきました。

一応、募集要項には「Xamarin にすでに触れたことのある方向け」とありましたが、まあ私一人わからない人が行ってもいいかなぁと考えてました。

とりあえず前日には Visual Studio で Xamarin プロジェクトを作って、 Xamarin.Forms 製の電卓アプリっぽいものを途中まで作って うわぁ Xamarin ってすごいなー なんてはしゃいではいましたが、要はその程度だということです。

f:id:yuzutan_hnk:20170820161736p:plain:h300f:id:yuzutan_hnk:20170820161739p:plain:h300

ちなみに UI に3時間、ロジック(C, CE, 各数字, 演算途中まで)に2時間という多大な時間を割いてます。こんな低レベルなものなのに…泣ける…。

というわけで当日

f:id:yuzutan_hnk:20170820162126j:plain:h300f:id:yuzutan_hnk:20170820162212j:plain:h300

Microsoft中部支社に行ってまいりました!

21階という、超高い場所にきれいなオフィスがあって、やっぱり一流企業は違うなぁと感じました。

入って早々、名刺をお渡しして懇親会用のお布施を納め、会議室へ。

ドタキャンが相次いだようで普通に座ることができました。とはいえもともと立ち見の分際なので、一番後ろの机のないところに こっそり 座りました。

セッションは、ちょまどさん、くぅさん、えのさんの順。

私以外にも初心者の方が大勢いたようで、そこまで難しい話はしない方向でいきますよー、という感じで13時にスタートしました。

ちょまどさんのセッション

タイトルは「私は Xamarin が好きだ」です。異論は認める。

ちょまどさんはもともと Xamarin でアプリを作る会社に勤めていらっしゃって、その後 Microsoftエバンジェリストになられたという経歴をお持ちで、それ故 Xamarin 愛がすごいです。

終始 Xamarin 愛に満ちたセッションでした。

そして初めてエバンジェリストという職業の方のプレゼンを見させていただきましたが、さすがと言わざるを得ないような、初心者でもわかりやすいプレゼンでした。

プレゼン力つけなきゃなぁ…。

Xamarin についての紹介

Xamarin については予習していきましたが、やっぱり直に人から話を聞くと違うんだなぁと感じます。

ネットを見てもあんまり実感がわかなかった “共通化” というものが、ここにきてようやくわかってきた気がします。

普通にカメラが使えたり、普通にダイアログが出てきたりと、細かい制御が簡単に共通化で来ていて、しかも C# で。

実際に動いてる様子を見てみると、ネットに載っているコード例とか説明云々を読み漁ってもわからないような、感動を覚えました。

ちょまどさんは一番初めということもあり、本当に触りの部分しか説明されませんでしたが、これまで以上に Xamarin について理解ができたかなぁと思います。

名前空間の汚染問題

コミュニティ向けのネタセッションということもあり、初めからネタ満載で面白かったのですが、まさかこんな問題が出てきているとは思ってなかったです。

www.city.zama.kanagawa.jp

神奈川県座間市マスコットキャラクターの名前が「ざまりん」という、なんとも狙ったような名前…。

実際に画像検索を実演されてましたが、「ざまりん」で画像をbingると完全に浸食されきってる状態でした。

まあ後発とはいえ公認キャラクターのようですので、ちゃんと検索に引っかかるのはとても良いことなのですが、うーん、なんだか複雑な気分…。

ネタアプリ「松屋警察」

ネタアプリという名の宗教戦争ざまりんの扱い方…

松屋牛めし吉野家の牛丼かわからないときは、これで判別しようってアプリ。

吉野家の牛丼のときはちょまぎょが渋い顔してお知らせしてくれる機能までついてます。すごい。

iOS で実演されていましたが、Xamarin で作られているので、 これがソースそのままに Android でも UWP でも動くようになるのかぁと考えるとすごいことだなと思います。

1日で作ったというのですからなお驚き。マルチプラットフォームの(ネタ)アプリをたった1日で作れるなんて、本当に夢のようですね。

Custom Vision Service の紹介

松屋警察の正体、というか今回のサブメイン?いやサブメインは牛めしか…。

Microsoft が提供する、 画像分類の機械学習サービス の一つ。まだプレビュー版のようです。

https://customvision.ai/customvision.ai

画像をアップロードしてタグで分類するだけで、簡単に機械学習ができてしまうという、これもまた夢のようなサービス。

さらにすごいことに、最低 5枚 の画像があれば、機械学習ができてしまうというのです。公式には 30枚 程度あれば十分な精度が出るそう。

実際、松屋警察では松屋吉野家ともに15枚程度しか画像を学習していないのにもかかわらず、ほぼ百発百中で当てています。精度やばいっすね。

さらにさらに、画像のアップロードから学習結果のテストまで すべてブラウザ で行え、外部から使用するときは REST API を使うという、使い勝手が良すぎるサービスです。

機械学習が初めてでよくわからないという人でも、画像をアップロードして「 電車 (Train)」ボタンを押すだけで簡単に学習できるので、ぜひ使ってくださいね!という紹介でした。

ちなみに、REST API は 1日1000回 まで叩けるようです。画像も1000枚までだそうです。まだプレビュー版なので仕方ないですね。

WindowsVisual Studio の初耳機能

たぶんちょまどさんのセッションの中で、参加者に一番の驚きを与えたのはこれかと思います。

私も知らなかったのですが、 Windows 版の Visual Studio だと、 JSON の生文字列をクリップボードにコピー した状態で 形式を選択して貼り付け をすると、その JSONシリアル化可能なクラスを自動生成 してくれるそうなのです。

REST API の説明をしているときに「これ、帰ってくる JSON から作ることができるクラス…、あ、これ Windows 版の Visual Studio の機能なんですけど…」って切り出して、さらっと会場を沸かせました。

かっこいい。

本当にできるのかと、その場で早速試してみた画像を Twitter に流したら、結構反響がありました。

クラスを生成できることまで確認したはいいものの、肝心の 生成したクラスをスクショし忘れるという痛恨のミス を犯してしまい、なんだか微妙な感じになりました。はぁ。

第三の勢力(への対応)

松屋警察は松屋吉野家しか対応していませんでしたが、質疑応答で会場から「すき家に対応してほしい」という要望が飛び出しました。

まさかの第三勢力の登場に、会場がざわめく…。

そこでちょまどさん、試しに5枚ほどすき家の画像を登録し実行されたのです。しかし、なんと 吉野家と判定して誤認逮捕してしまいました

いやまあ、逮捕したことには変わりないので良かったとは思います(

質疑応答の時間が十分にとれたのでその場で対応されたのだと思いますが、それにしても、ものの10分程度で機械学習をつかったアプリを仕様変更できてしまうなんて、本当にびっくりします。

それも、 Xamarin をつかっているのでマルチプラットフォームでの改修。本当にすごい技術ですよね…。

まとめ

初心者向けの内容でありながら、実演を交えてとても奥の深いセッションでした。

Xamarin についての知識だけでなく、実際に動くという実感が何よりも大きかったと思います。

さらに、改修という普通では見ることができない作業があったので、なんとなくではありますが、利便性やメリットを感じることもできたかなぁと思います。

くぅさんのセッション

タイトルは「AIをアプリに取り込む!」です。

くぅさんは 大同大学 の 情報学部 2年生、東京の某社でインターン中の Microsoft MVP 。何という豪華な肩書き…。

自作アプリ「 Keya Lens 」

熱烈な欅坂46のファンだそうで、欅坂46の誰とどのくらい似ているかを判定するというアプリを自作されたそう。

写真を撮ると、何%の確率でこの人と似てますよっと教えてくれるみたいです。

飲み会とかで盛り上がりますよねってさらっと言っていたのですが、やっぱりあのレベルになると 怖い 大人の人達と一緒に飲み会に行ったりするんだなぁと遠い目。

同年代なのになんだろうこの格差って痛感しました、始まって早々。

やっぱり時代は機械学習! Cognitive Services!

時代はモバイルを通り越して、もはや AI! 機械学習!

私も機械学習やってみたいなーと思いますが、家にある GPU はメインPCについてる GeForce 1050 だけだし、サブPC用に買っても非力だし…。

というか、理系とか文系とか隔たりなく趣味の範囲でしか基本的にやってないので、詳しいことは全くわからない。

そんな私でも簡単に機械学習できるような、Microsoft が提供する 機械学習サービスのブランド (ここが当日よく理解できてなかった) があるみたいです。

azure.microsoft.com

ちょまどさんが紹介された Custom Vision Service もこの内の一つで、他にも顔を読み取る Face API や Emotion API機械翻訳を行う Translator Speech API や Translator Text API など様々なサービスがある模様です。

あの有名な、 How-Old.net には、男性を逆鯖読みする機能を強化して使われているみたい?

how-old.net

Intelligent Kiosk

github.com

GitHub に公開されている UWP アプリプロジェクトで、 Cognitive Services でどんなことができるかを体験できるアプリだそう。

主に画像認識系の API を利用しているようで、カメラで撮った写真を使っていろんな機械学習成果を試すことができるみたいです。

帰ってから実際に試してみましたが、 Cognitive Services のキーを入力する必要があるのでちょっと保留。

でもすごくわかりやすい設計になっているので、 Xamarin プロジェクトにも簡単に移植できそう。

Custom Vision Service の紹介 (再登場)

この API 、 Cognitive Services の中でも特に注目されているようで、くぅさんもこれを紹介されてました。

「 Keya Lens 」もこれで実装しているみたいで、各メンバ 30枚 ほど写真を用意して登録しているみたいです。

ただ、メンバーの人数が人数なので、1人30枚も手作業で入れると相当な苦労を強いられることになる。そこを Cognitive Service の Bing Image Search API で半自動で登録されたそう。

qiita.com

Cognitive Service だけでこんなに自由に機械学習が扱えるんですね…素晴らしい。

まとめ

Xamarin のセッションのはずが、ほぼ終始 AI や Cognitive Services の話で埋め尽くされていた感があります。

これはこれで、今流行りの2大ワードである、 モバイルAI っていう、素晴らしい活用方法を勉強できてよかったと思います。

えのさんのセッション

タイトルは「Embeddinator-4000 から学ぶ Xamarin の基礎」。基礎ですよ、基礎。

speakerdeck.com

えのさんは Xamarin の開発に携わっている、いうなれば Xamarin の第一人者。もはや Xamarin のことを知らないわけがない人物。

後から見直してみたら、ネットで調べて読んだ Xamarin の文献のほとんどがえのさんのものだった…。恐るべし。

Xamarin のセッションで C言語

まずはじめに、 Embeddinator-4000 とは何をするもの何かの概要の説明。

Xamarin (C#) の DLL ライブラリをネイティブから利用できるよう、 C や JavaObjective-C のライブラリを生成する技術、だそうです。

ほぼすべてのコードを変換することが可能で、スライドにもあるように Xamarin.Forms でさえもネイティブから呼べるようになるということです。

えっ、急に難易度高くなりすぎじゃないですか

わからない単語が次々に出てくる…

NuGet で Embeddinaor-4000 をインストール後、コマンドポチポチ…。

引数によってプラットフォームを分けることが可能で、 Android の場合は aariOS の場合は dylib が生成される……。

aar には monodroid ランタイム が含まれ、 Hello World! プログラムでだいたい 10MB ほどのサイズになる………。

というのが、スライドからの抜粋なのですけど、そもそも aar って何? monodroid って何? と会場でハテナばかり浮かんでくる。

丁寧に解説してくださるえのさん

今回の勉強会に参加しているのが初心者ばかり、それもネイティブ関連は全く分からない人が大勢いるということで、わかりやすいように解説してくださるえのさん。

  • 実際にビルドした DLL を参照するよう指定して Embeddinator-4000 を実行すると、それを元に DLL の中身を呼び出せるクラスライブラリを生成してくれる。
    • 基本的には C 言語のライブラリが生成される。どのプラットフォームでも C 言語ライブラリを使える環境はあるから。
    • Java を出力する指定にすると、 C# で書かれたクラス構成そのままのライブラリが生成される。 C#Project1.Common.AppTitle と呼び出せるものは、 Java でも Project1.Common.AppTitle で呼び出せるようになる。
  • aar とは、 jar や Assets などを含むアーカイブ。というか、 Android Studio で作る Android プロジェクトの生成物をそのまま zip 圧縮したもの。
  • monodroid は、ビルドした C# の実行コード (MSIL) を解釈する JIT コンパイラや、クラスライブラリなどを含めた Mono のランタイム本体。組み込みコンパイラ

ほかにもいろんなことを説明されていたような気がするのですが、タイピングが遅いのでメモれず。無念。

そして話は Xamarin の 基礎

Embeddinator-4000 の動作原理を理解するためには、 Xamarin の動作原理も理解する必要があるとのこと。もはや中級者でもついていけない気がする。

スライドにもあるとおり、ネイティブアプリの理解ができればなんとなく理解できるそうなのですが、私は作ったこと無いのでさっぱり。

とはいえ、「 Android NDK って知ってる?」とか「 iOS 向けの Xamarin プロジェクトは AOT コンパイルされるの知ってる?」とかの質問には大体の人が手を上げていたので、多分断片的に理解されてる方は多いんだろうなぁと。

私は全く理解できなかったよ。とほほ。

詳しいことまとめ

学校で全く未知の授業受けてるかのような状態に陥って会場全体もそこまでなごやかなムードでもなかったので、勉強できたところをまとめていきます。

Embeddinator-4000 は一体何なのか

上記にもあったとおり、 DLL の中身を呼び出せるクラスライブラリを生成してくれる もの。

そもそも、 C# を普通にビルドすると (Xamarin に限らず) MSIL と呼ばれる仮想マシン用 (.NET Framework 用) のアセンブリ が生成されます。いわゆる中間言語ってやつですね。

AndroidiOSmacOS も MSIL は解釈できません。そこで、 Embeddinator-4000 は MSIL を解釈し、中の機能を呼び出せる C/Java ライブラリを生成 するというわけです。

Xamarin と何が関係するのか

Embeddinator-4000 の GitHub には次のような文があります。(投げやり)

github.com

It is a tool that takes a .NET assembly and generates the necessary glue to surface the .NET API as a native API. The goal is to surface .NET libraries to all ecosystems where Mono/Xamarin run, and for each platform we provide an interface that is native to that platform as well as the tools needed to turn a .NET library into something that can be consumed on that platform.

そもそもなぜ Xamarin は .NET ライブラリを各プラットフォームで動かせるのか

Xamarin は Mono というシステムを使って、 MSIL を各プラットフォーム上で実行できるようにしています。

Mono はオープンソースで開発された .NET Framework 互換技術の総称です。もともと Linux 上で MSIL を実行できるようにしていたものです。

えのさんは、各プラットフォーム別に MSIL を実行する手順を解説してくれました。

Xamarin.Android の場合

Android の すべてのアプリは Java VM で実行します 。例外はないそうです。

アプリが VM で動くということは、実は Android のアプリは常にもっと低レベルなネイティブバイナリに JIT コンパイルされながら動いているということになります。

C/C++ で直接これを呼び出せる技術が、 Android NDK として確立されています。

一方で、 Mono はかなり前から MSIL を実行するための JIT コンパイラを含む VMLinux 上で実装しています。C言語で。

AndroidLinux ベースですから、ちょっと手直しすれば動くようになります。

そこで、 Android NDK を使って、 MSIL の VM とその他のランタイムをすべて含んだ monoランタイム を作成 し、それを Xamarin で作成する Android アプリに直接埋め込んでいる そうです。

これが、スライドで解説されている Embedding Mono のことだそうです。

Android アプリを実行するとまず Java のエントリポイントから実行が開始されますが、 Java/C 相互運用技術を使って monoランタイム のコードを呼び出し、それが MSIL を実行する、という流れになっています。(スライドの図参照)

Xamarin.iOS の場合

iOS の場合はそんな簡単にはできないみたいで、 動的なコード生成が禁止 されているため JIT コンパイラが使えまません

例外的に、 Safari に使われている JavaScriptVM なんかは JIT が認められているそうですが、サードパーティでは無理だそうです。

そこで、 AOT コンパイラを使い事前にネイティブコードにコンパイルしておくという仕組みが採られています。

すべてネイティブコードに変換されるということは、わざわざ JIT コンパイルする VM を必要とせず、 ガーベジコレクションなどの機能を実装すれば良いことになります。

スライドにある

embedded mono は Mono.framework になっている

というのは、リンクするだけでそうした VM の機能を利用できるようになっている、ということだそうです、

Embeddinator-4000 はどうなっているのか

Xamarin が MSIL を実行する手順を応用しただけのようです。

MSIL で書かれたライブラリを呼び出すように mono ランタイムを操作できる C 言語 API を作成 するのだそう。

スライドの図には e4k C API から直接 C# コードを呼んでいますが、多分 mono ランタイムも C 言語のためコンパイルすると Embeddinator-4000 の API との境目がなくなる、という認識でいます。間違ってたらご指摘ください。

何がすごいのか、何がわかるのか

Embeddinator-4000 も Xamarin も、実は同じ技術の応用をしているだけ。

というか Mono を使って MSIL をコンパイルして、各プラットフォーム対して最適な API を自動で生成している。

Xamarin の場合はそれを一つのアプリとして、実行可能な形式にしてくれる (実行できるような Java コードなどまで自動で生成する)。

Embeddinator-4000 の場合は、 C# で書いたクラス構成そのままの API を公開して、ユーザ (開発者) がそれを自由に使うことができる。

感想まとめ

本当に詳しく解説していただいたおかげで、本筋から外れたことでも勉強になることはたくさんあったし、そもそも Xamarin の動作原理を概念的でありながら理解できたと思います。

もっと詳しく突き詰めていくと、たぶん実際のコードを見たほうが早いってことになりそうですし、もっとわからないことがたくさん出てくると思います。

マルチプラットフォームで動かすためにいろいろな方法を採って最適解を模索して、そしてそれを地道に実装する。そうした作業をがあるからこそ、 Xamarin は手軽にアプリを開発できるんだということを、なんとなく感じることが出来たと思います。

個人的には、とてもぐっと刺さる内容でした。永遠に Xamarin のことについてえのさんからお話を聞いていたい。うん。

勉強会全体を通じて

この記事書くの 本当はスプラトゥーンにうつつを抜かしてて 時間がかかってしまいましたが、なにはともあれお疲れ様でした。そしてありがとうございました。

本当にたくさん勉強することがありました。同時に勉強することが増えました。

初めて勉強会記事なんて書くし、そもそもこうした勉強会自体、参加するのが初めてで、なんか大げさかもしれませんが。

もっと頑張らなきゃな、とつくづく感じました。

VB.NETの構造化例外処理と非構造化例外処理の話

どうせプログラミング言語でブログを書いても、基本的に既出だし、私が書いてもわかりにくいだけで皆さんのためにならないんだろうなと思います。

でも、今回の記事には自信があります。自身があるというのは、調べた中であまり見かけない話題だからです。

なぜ触れれらていないのか私にはわかりません。しかし、この機会にブログのアクセスアップ あっごめんなさい嘘です…

というわけで今回は、 VB.NET の構造化例外処理と非構造化例外処理について、色々と調べた結果をまとめてみます。

非構造化例外処理

10.10.2 非構造化例外処理ステートメント

まず非構造化例外処理とはなんぞやというところから話をしたいと思います。

非構造化例外処理とは、ざっくり言うと On Error ~Err オブジェクトを用いた例外処理です。

Visual Basic がまだ .NET化する前、有名なところでいえば VB6 等の時代に使われた文法を踏襲し、 VB.NET での例外を処理できるように改造された仕組みです。

未だに Office などのマクロを組むために使用されている VBA は、 VB6 の構文がほぼそのまま残っているため、当時の仕様のまま使うことができます。

簡単な例を見れば、この構文の方向性がなんとなくわかると思います。

Module Module1

    Sub Main()

        On Error GoTo ErrProc1
        Debug.Print("A通りますよー")

        On Error GoTo ErrProc2
        Debug.Print("B通りますよー")

        On Error GoTo ErrProc3
        Debug.Print("C通りますよー")
        Err.Raise(5)

        On Error Resume Next
        Debug.Print("D通りますよー")

        On Error GoTo 0
        Debug.Print("おわり")

        Exit Sub

ErrProc1:
        Debug.Print("ErrProc1でっせ")
        Exit Sub

ErrProc2:
        Debug.Print("ErrProc2でっせ" & vbNewLine & Err.Number & ":" & Err.Description)

ErrProc3:
        Debug.Print("ErrProc3でっせ")
        Resume Next

    End Sub

End Module

要するに、 エラー処理をラベルで指示する というものです。この構文は、現在ではほぼ 禁忌 とされています。

なぜ禁忌かというと、まず一点は、スパゲッティコードになるから。あからさまに GoTo を使う構文なので、それはまあ仕方ありません。

そしてもう一点。これは、世にも恐ろしい Resume という機能があるからです。

Resume は、エラーが起きた場所から処理をやり直すという意味の命令です。 エラーが起きても、元の正常処理を続きから再開できるというとても強力で忌々しい命令です。

確かに、使ってもさほど影響がない部分というのはもちろんありますが、闇雲に多用してしまうとあとからどうしようもないコードが生まれてしまうので、現在では意図的に避けられています。

詳しい説明はここでは省きますので、上記のコードがどのように動作するかを簡単にまとめます。

  • ケース1
    • エラー場所 - MsgBox "A通りますよー" の直後
    • 出力結果
      A通りますよー
      ErrProc1でっせ
  • ケース2
    • エラー場所 - MsgBox "B通りますよー" の直後
    • 出力結果
      A通りますよー
      B通りますよー
      ErrProc2でっせ
      5:プロシージャの呼び出し、または引数が不正です。
      ErrProc3でっせ
      C通りますよー
      D通りますよー
      おわり
  • ケース3 - MsgBox "C通りますよー" の直後
    • 出力結果
      A通りますよー
      B通りますよー
      C通りますよー
      ErrProc3でっせ
      D通りますよー
      おわり
  • ケース4 - MsgBox "D通りますよー" の直後 A通りますよー
    B通りますよー C通りますよー
    D通りますよー
    おわり

※なお、ケース2中のエラーメッセージは、例外の種類で異なります。

構造化例外処理

10.10.1 構造化例外処理ステートメント

On Error に対して、もっとオブジェクトな指向の例外処理があります。おなじみ Try Catch Finally です。

「構造化例外処理」の名の通り、文法の構造的に例外処理が保証される仕組みです。

これは完全に C# の流れに押されて出現したもので、 VB6 などの時代には存在しませんでした。 (C# に押されたというか .NET 化した影響?)

今では JavaECMAScript (またはその派生) などあらゆる言語に同様の構文が定義されており、非常に扱いやすい構文だと思います。

Module Module1

    Sub Main()

        Try
            Debug.Print("A通りますよー")

            Try
                Debug.Print("B通りますよー")

                Try
                    Debug.Print("C通りますよー")

                    Try
                        Debug.Print("D通りますよー")

                    Catch
                        Throw New Exception()
                    End Try

                    Debug.Print("おわり")

                Catch
                    Debug.Print("ErrProc3でっせ")
                    Throw New Exception()
                End Try

            Catch ex As Exception
                Debug.Print("ErrProc2でっせ" & vbNewLine & ex.Message)
            End Try

        Catch
            Debug.Print("ErrProc1でっせ")

        Finally
            Debug.Print("おわり")
        End Try

    End Sub

End Module

構造化するということは、つまりブロックをネストするという意味ですので、先程のコードよりかなりインデントが深くなっています。

また、非構造化例外処理の Resume 機能に該当する機能は一切ないですし、任意に例外処理の実行順を入れ替えることもできません。

一見できることが制限されたように思いますが、この制限があるからこそ、スパゲッティコードが生まれにくいという恩恵をうけることができます。

  • ケース1
    • エラー場所 - MsgBox "A通りますよー" の直後
    • 出力結果
      A通りますよー
      ErrProc1でっせ
      おわり
  • ケース2
    • エラー場所 - MsgBox "B通りますよー" の直後
    • 出力結果
      A通りますよー
      B通りますよー
      ErrProc2でっせ
      プロシージャの呼び出し、または引数が不正です。
      おわり
  • ケース3
    • エラー場所 - MsgBox "C通りますよー" の直後
    • 出力結果
      A通りますよー
      B通りますよー
      C通りますよー
      ErrProc3でっせ
      ErrProc2でっせ
      プロシージャの呼び出し、または引数が不正です。
      おわり
  • ケース4
    • エラー場所 - MsgBox "D通りますよー" の直後
    • 出力結果
      A通りますよー
      B通りますよー
      C通りますよー
      D通りますよー
      ErrProc3でっせ
      ErrProc2でっせ
      プロシージャの呼び出し、または引数が不正です。
      おわり

※なお、ケース2,3,4中のエラーメッセージは、例外の種類で異なります。

構造化例外処理と非構造化例外処理の共存

これら2つの文法は、お互いに排他的に使わなければなりません。噛み砕いて言えば、 同時には使えません

厳密には、同じプロシージャ内、いわば一つのメソッド内に両方記述してはならないという言語仕様になっています。

たとえ Try Finally 文のみを使用して Catch を使っていなくても、 On Error を書いた時点でコンパイルエラーになります。

例外処理の概要 (Visual Basic)

本当に?

あまり知られていない構造化例外処理

上記では、構造化例外処理として Try Catch Finally を紹介しました。

最近まで、私はこれ以外の構造化例外処理を知りませんでした。

次の記事では、もう一つ、構造化例外処理を利用したコードを紹介しています。

code.msdn.microsoft.com

少々古めの記事ですが、読んでみましょう。私は自分の目を疑いましたよ。

VB.NET には Using という便利な構文があります。 IDisposable インターフェイスを実装したクラスのインスタンスに対し、構文上でその破棄を保証するものです。

これも、 VB6 や VBA には無い機能です。

この構文は、コンパイラを通すとなんと、 Try Finally 句へ変貌するというのです 。嘘みたい。

実際、 C# にも同様の機能を持つ using というステートメントがありますが、このリファレンスには次のような説明がされています。

using ステートメント (C# リファレンス) | Microsoft Docs

using ステートメントを使うと、オブジェクトでのメソッドの呼び出し中に例外が発生した場合でも Dispose が必ず呼び出されます。 オブジェクトを try ブロックに配置し、finally ブロックで Dispose を呼び出しても、同じ結果が得られます。実際には、コンパイラは using ステートメントをこのように変換します。 前のコード例は、コンパイル時に次のコードに展開されます (オブジェクトのスコープの範囲を定義する中かっこが加えられています)。

{ Font font1 = new Font("Arial", 10.0f); try { byte charset = font1.GdiCharSet; } finally { if (font1 != null) ((IDisposable)font1).Dispose(); } }

C# がこのようにコンパイルするのですから、 VB も必然的に同じような動作になるのでしょうね。Try Finally を使うのですから、 Using も構造化例外処理の仲間となっていまいます。

何がおかしいのか

先程の私の紹介でもそうでしたが、改めて言語仕様やリファレンスをよく読むと、構造化例外に Using が出てきていません。

むしろ、

Finally セクション内のコードは、Catch ブロック内のコードが実行されたかどうかに関係なく、常に最後 (エラー処理ブロックがスコープを失う直前) に実行されます。 Finally セクションには、クリーンアップ コード (ファイルを閉じたりオブジェクトを解放したりするコードなど) を配置します。 例外をキャッチする必要はないけれども、リソースをクリーンアップする必要がある場合、Finally セクションではなく、Using ステートメントを使用します。 詳細については、「Using ステートメント (Visual Basic)」を参照してください。

とあり、 Using は構造化例外ではないかのような書き方がされています

じゃあ、 On ErrorUsing は同時に使えるのか? という疑問が生まれます。

こうなったら、実際どうなるのか試すしかありませんね。

テストコード

Module Module1

    Sub Use_Try_Finally()
        Dim objA As DisposableObject = New DisposableObject("A")
        Try
            Dim objB As DisposableObject = New DisposableObject("B")
            Try
                Dim objC As DisposableObject = New DisposableObject("C")
                Try
                    Debug.Print("すべてインスタンス化されました")
                Catch ex As Exception
                    Debug.Print(ex.Message)
                Finally
                    objC.Dispose()
                End Try
            Catch ex As Exception
                Debug.Print(ex.Message)
            Finally
                objB.Dispose()
            End Try
        Catch ex As Exception
            Debug.Print(ex.Message)
        Finally
            objA.Dispose()
        End Try
        Debug.Print("すべてDisposeされました")
    End Sub

    Sub Use_Using_OnError()
        On Error Resume Next
        Using objA As DisposableObject = New DisposableObject("A")
            Using objB As DisposableObject = New DisposableObject("B")
                Using objC As DisposableObject = New DisposableObject("C")
                    Debug.Print("すべてインスタンス化されました")
                    If Err.Number <> 0 Then Debug.Print(Err.Description)
                End Using
                If Err.Number <> 0 Then Debug.Print(Err.Description)
            End Using
            If Err.Number <> 0 Then Debug.Print(Err.Description)
        End Using
        Debug.Print("すべてDisposeされました")
    End Sub

    Sub Main()
        Debug.Print("--- Use_Try_Finally -------------")
        Use_Try_Finally()
        Debug.Print("--- Use_Using_OnError -----------")
        Use_Using_OnError()
    End Sub

End Module

え?これだけじゃ実行できない?

そうです。 DisposableObject を実装します。

なんで別クラスにしたかといえば、このクラスのほうが短くかけるので、例外とかの挙動を書きやすいかなと思ったんですね。

うん。察してくれ。

通常の挙動を見てみる

では、まず通常の挙動から。 DisposableObject の実装は次のとおりです。

''' <summary> IDisposeableを継承するオブジェクト </summary>
Public Class DisposableObject
    Implements IDisposable

    ''' <summary> インスタンスの名前 </summary>
    Private myName As String
    ''' <summary> Disposeがすでに呼ばれたか </summary>
    Private disposedValue As Boolean

    ''' <summary> DisposableObject をインスタンス化します </summary>
    ''' <param name="name">インスタンスのわかり易い名前</param>
    Public Sub New(name As String)
        myName = name
        Debug.Print(myName & "がインスタンス化されました")
    End Sub

    'Privateにして隠蔽
    Private Sub New()
    End Sub

    '内部用Disposeメソッド
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                Debug.Print(myName & "がDisposeされました")
            End If
        End If
        disposedValue = True
    End Sub

    ''' <summary> リソースを破棄します </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
    End Sub

End Class

インスタンス時にインスタンスの名前を表示して、Disposeするときも表示するだけです。

これを実行すると、次のような結果が得られます。

— Use_Try_Finally ————-
Aがインスタンス化されました
Bがインスタンス化されました
Cがインスタンス化されました
すべてインスタンス化されました
CがDisposeされました
BがDisposeされました
AがDisposeされました
すべてDisposeされました
— Use_Using_OnError ———–
Aがインスタンス化されました
Bがインスタンス化されました
Cがインスタンス化されました
すべてインスタンス化されました
CがDisposeされました
BがDisposeされました
AがDisposeされました
すべてDisposeされました

普通ですね。

例外時の挙動を見てみる

次に例外を発生させてみます。 DisposableObject の実装は次のとおりです。

''' <summary> IDisposeableを継承するオブジェクト </summary>
Public Class DisposableObject
    Implements IDisposable

    ''' <summary> インスタンスの名前 </summary>
    Private myName As String
    ''' <summary> Disposeがすでに呼ばれたか </summary>
    Private disposedValue As Boolean

    ''' <summary> DisposableObject をインスタンス化します </summary>
    ''' <param name="name">インスタンスのわかり易い名前</param>
    Public Sub New(name As String)
        myName = name

        'Cのときは例外
        If myName = "C" Then Throw New Exception("例外!!!!")

        Debug.Print(myName & "がインスタンス化されました")
    End Sub

    'Privateにして隠蔽
    Private Sub New()
    End Sub

    '内部用Disposeメソッド
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not disposedValue Then
            If disposing Then
                Debug.Print(myName & "がDisposeされました")
            End If
        End If
        disposedValue = True
    End Sub

    ''' <summary> リソースを破棄します </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
    End Sub

End Class

インスタンスを作るときに名前が「C」だった場合、エラーになるというだけの分岐が一つ増えただけです。

これでどうなるでしょうか。

— Use_Try_Finally ————-
Aがインスタンス化されました
Bがインスタンス化されました
例外がスローされました: ‘System.Exception’ (vb_using_test.exe の中)
例外がスローされました: ‘System.Exception’ (vb_using_test.exe の中)
例外!!!!
BがDisposeされました
AがDisposeされました
すべてDisposeされました
— Use_Using_OnError ———–
Aがインスタンス化されました
Bがインスタンス化されました
BがDisposeされました
AがDisposeされました
すべてDisposeされました

例外の情報が・・・消えた!

Try-Catch のほうは、問題なく例外を捕捉できています。

OnError Resume NextUsing のほうは、エラーを出すロジックはいくつもあるというのに、それらのどこでも例外の情報が捕捉できていません。

これは一大事です。

例外を補足できるようにする

後者で例外を補足できるようにするためには、 Use_Using_OnError を次のように修正する必要があります。

    Sub Use_Using_OnError()
        On Error Resume Next
        Using objA As DisposableObject = New DisposableObject("A")
            Using objB As DisposableObject = New DisposableObject("B")
                Using objC As DisposableObject = New DisposableObject("C")
                    Debug.Print("すべてインスタンス化されました")
                    If Err.Number <> 0 Then Debug.Print(Err.Description)
                End Using
                If Err.Number <> 0 Then Debug.Print(Err.Description)
            End Using
            If Err.Number <> 0 Then Debug.Print(Err.Description)
        End Using
        If Err.Number <> 0 Then Debug.Print(Err.Description)
        Debug.Print("すべてDisposeされました")
    End Sub

先ほどとのコードの違いは、 最後の End Using のあとに例外処理を入れているだけ です。

これで、どのような結果になるでしょうか。

— Use_Using_OnError ———–
Aがインスタンス化されました
Bがインスタンス化されました
BがDisposeされました
AがDisposeされました
例外!!!!
すべてDisposeされました

やっと例外が取得できました。

先ほどのコードを比較すると、 Resume NextUsing を併用したコード内で例外が発生したとき、一番外側の Using の外側でないと取得できないという結果になりました。

なお、 On Error Goto を使用しても、 Resume Next 同様、一番外側の Using の外側まで抜けたあと GoTo 先へジャンプするようになっています。

まとめ

結果的に何が言いたいかといえば、

Using 構造化例外処理 であり、 On Error と併用できないはずが なぜかできでしまう

ということです。そして、

併用してしまうと 必ず一番外側の Using まで抜けてしまう

のです。さらに、

この情報が どのリファレンスにも明記されていない のです

というか、ネットで探しても全く出てこない。

英語が読めないので英語のサイトは全く読んでいないのですが、少なからず日本語のサイトではこの現象を取り上げているサイトが少ない。というかない。

一応、私が実際に出会った問題として書いておきますが、この現象について参考になるサイトを知っている方がいたら、ぜひ教えていただきたいと思います。

長文になってしまいましたが、最後までお付き合い頂きありがとうございました。

ちなみに私は Form.ShowDialog() するとき using 使ってるコード見ると指が勝手に Delete キーを あっごめんなさい嘘です…

AVT-C878 というキャプチャボードが認識してくれない話

ついに買いました。Splatoon2に向けて。

巷で “最強” とも称されるキャプチャボード

随分と物騒な噂ですが この程、Nintendo Switchをつなげて録画をしようとこんなものを購入しました。

こちら。USBでつないで、1080p/60fpsで録画ができるという素晴らしいもの。

という謳い文句を 鵜呑みにして 買いました。

前から何度も店に足を運んではいろんなキャプチャボードを見てきましたが、値段的にも扱い方的にもこれがいいのかなと思って購入しました。

ガチなボードだとパソコンを起動しないといけないという手間もありますしね、これがいいかなと。

何度も言います。 これがいいかな、と思って。

思い込みで買うのは本当に怖いものですね。

人生の99%は思い込み―――支配された人生から脱却するための心理学

人生の99%は思い込み―――支配された人生から脱却するための心理学

買ってつなげて初めて分かるもの

PC録画の推奨環境が 「Core i5-3xxx 以上 / NVIDIA GeForce GTX 650同等以上」 とあるので、結構なスペックが要求されるようです。

私が持っているPCの中ではメイン機だけが使えそう。

f:id:yuzutan_hnk:20170705010604p:plain:w300

OS: Windows10
CPU: Core i7-6700
メモリ: 16GB
グラボ: NVIDIA GTX 1050

使えそうというか、これ絶対動くでしょ、って感じで甘く見てました。

……。

認識しない

PCとUSBでつなぐと、電源が入る。HDMIパススルーが機能して、普通にゲームも遊べる。

しかし、PC側にはなんの反応もない。いや、実際には少しだけ認識したけど、認識できないデバイスと言われ、ドライバにも黄色い三角マーク。

仕方なくドライバを削除して再起動。とりあえず標準ドライバを使うとWebカメラとして認識するみたいなので、それでやってみようと試みる。

が、何分経っても青点滅(準備中)が消えない。何度抜き差ししてもだめ。全く認識しない。

死亡フラグが立ちました! (宝島社文庫) (宝島社文庫 C な 5-1)

死亡フラグが立ちました! (宝島社文庫) (宝島社文庫 C な 5-1)

Ubuntuならどうか

サブ機(サーバー)のUbuntuにつなげてみて、Webカメラとして使えたら使おうと考えた。

グラボが載っていないので、もし認識したら安めのものを買ってきてつけようかとも考えた。

しかし、全く同じ状態が続く。青点滅が消えない。

挿した瞬間、HDDアクセスを示すLEDランプが一瞬光るので繋がった認識はしてるんだろうけど、そこから先動いてるように見えない。

OBSとかも全く反応してくれない。

カードリーダも使えない

AVT-C878はカードリーダーモードも備えていて、普通にmicroSDの中身を読み書きできるそう。

なのに それも動いてくれない 。やっぱり青点滅が消えない。

完全にUSBでPCとやり取りができていない。AVT-C787側も初期化が終わらないってことは、完全にハンドシェイクが切れてる。万事休す。

無理やりFWアップデート走らせたせい?その前から現象はあるけど…

ファームウェアが古いせいなのかなとか思って、microSDにデータをダウンロードして無理やりアップデート。

マニュアルにはカードリーダモードで転送するよう書いてあったけど、前述の通り使えないので、普通に別のカードリーダで転送。

入れて起動すると自動的にアップデートが始まって、 ゆっくり青く点滅 するらしい。が、実際にやってみるとなぜか 素早く青く点滅 していた。

アップデートが終わると自動でランプが消灯すると書いてあって、実際しばらくしたら消えたので、たぶんアップデートできてるはず。

実際バージョン情報見るには、後述のソフトがAVT-C878を認識しないといけないから、結局どうなったのかわからないんだけど…。

ファーム・ルーツFirm Roots ?

ファーム・ルーツFirm Roots ?

RECentral すら起動しない

連携ソフトというか、無料で配布されているキャプチャソフト兼コンフィグレーションソフトの RECentral なるものがあるのですが。

これ、起動すると5秒もしないうちに落ちる。

ウィンドウは出てくるけど、全く何も使えない。

それどころかボードを認識している様子すらない(Windowsが認識してないから当たり前か…)

調べてみると確かに、同じような現象に陥っている人はいるみたい。再インストールすれば治る人もいるけど、私みたいに全く治らない人もいそう。

単体録画は正常

これだけPC録画がボロボロなのに、単体録画はしれっと何事もなかったかのようにできている。

空のmicroSDを挿して、PCのUSBから電源だけ拝借する。MOVなので画質は粗めだけど、解像度はちゃんと1080p出てる。

ハードエンコードなので圧縮とか高度なことはしてくれないのですぐ容量なくなってしまうけど、とりあえず当面は使えるレベル。

とはいえもともとPC録画を目的に買ったから本末転倒って感じがする。。。

結局

しばらく単体モードでやりますが、そのうちお金に余裕ができたら別のボード買うかもね…。うーん辛い。

お兄ちゃん!そこは MemoryStream の出番だよ!

タイトルは釣りです(お約束)

MemoryStream のススメ

みなさん、 System.IO.MemoryStream 使っていますか。私はよく使いますよ。

MemoryStream クラス (System.IO)

リアクティブプログラミングだったり、Java の Stream API だったり、いろんな Stream がありますが、今回はC#MemoryStream に注目してみます。

MemoryStream のイロハ

そもそもC#には Stream クラスがあり、 MemoryStream はその派生クラスです。同じような派生クラスには FileStreamCryptoStream があります。

似て非なるものですが、これらは共通して データを順次読み出したり、順次格納したりできる という特徴を持っています。

たとえば FileStream は、ディスク上のファイルを読み書きするクラスです。ファイルを開くと FileStream にはそのファイルサイズと カーソル位置 が保持されます。

カーソル位置は Stream のデータを操作する位置 を表します。これを使うと、例えばファイルの80バイト目から60バイト分読み出したい、といった場合に、カーソル位置を80バイト目に移動させ、そこから60バイト分読み出すことができます(同時にカーソルも60バイト動くため、次に60バイトを読み出すと140バイト目から60バイトを読み出します)。

これは、次のように書くことができます。

// ファイル名 を元に FileStream を作成
var stream = new FileStream("C:/hoge.txt", FileMode.Open);

// 80バイト目から60バイト読み出す
var puts = new byte[60];
stream.Position = 80;
stream.Read(puts, 0, 60);

同様に MemoryStream も、ある byte[] のサイズ(配列の長さ)とカーソル位置が保持されます。

そして、例えばある byte[] の80バイト目から60バイト分読み出したい、といった場合に、カーソル位置を80バイト目に移動させ、そこから60バイト分読み出すことができます。

これは、次のように書くことができます。

var array = new byte[300];

// --- ここに本来はデータの操作が入る --- //

// array を元に MemoryStream を作成
var stream = new MemoryStream(array);

// 80バイト目から60バイト読み出す
var puts = new byte[60];
stream.Position = 80;
stream.Read(puts, 0, 60);

なお、以下の配列へのアクセスをするコードで、同様の puts を得られます。

var array = new byte[300];

// --- ここに本来はデータの操作が入る --- //

// 80バイト目から60バイト読み出す
var puts = new byte[60];
for (int i = 0; i < 60; i++)
    puts[i] = array[80 + i];

え、じゃあ何に使うん

MemoryStream は byte[] へのアクセスを簡単にします。本当に?

ひどい夢を見ました。すべての byte[] 配列へのアクセスを、 MemoryStream を通じて行うようにするという、お達しが出たのです。まったく、とんだ災難です。このプロダクトは文字列や数値としては扱えないデータが山ほどあり(画像や音声、もしかしたら地球外生命体のDNAの解析結果かもしれない)、それらはすべて byte[] で表すことになっています。だから、それら全てのアクセスを、 MemoryStream に置き換えなければなりません。たった1要素の読み込みでさえ、長ったらしく2,3行を書き連ねなければならないのです。

実際にそんなことがあるはずはありません。安心してください。 ところで次の例を見てくれ、こいつをどう思う?

// AESで暗号化するためのオブジェクトを初期化
var aes = new AesManaged();
aes.GenerateIV();
aes.GenerateKey();

// MemoryStreamを作成
var memStream = new MemoryStream();

// CryptoStreamを作成
var cryStream = new CryptoStream(memStream, aes.CreateEncryptor, CryptoStreamMode.Write);

// CryptoStreamに書き込み
cryStream.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);

// MemoryStreamから読み出し
var read = byte[3];
memStream.Position = 1;
memStream.Read(read, 0, 3);

AesManaged クラス (System.Security.Cryptography)

CryptoStream クラス (System.Security.Cryptography)

ちょっと難解ですが、 ある MemoryStream を参照する CryptoStream にデータを書き込むと、 MemoryStream に暗号化されたデータが書き込まれる コードです。

上記の例では read に暗号化されたデータの一部が代入されることになります。

さてこれをちょっとだけ改変しましょう。

// AESで暗号化するためのオブジェクトを初期化
var aes = new AesManaged();
aes.GenerateIV();
aes.GenerateKey();

// FileStreamを作成
var filStream = new FileStream("C:/hoge.enc", FileMode.Create);

// CryptoStreamを作成
var cryStream = new CryptoStream(filStream , aes.CreateEncryptor, CryptoStreamMode.Write);

// CryptoStreamに書き込み
cryStream.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);

だいたい想像がつくと思いますが、これは ある FileStream を参照する CryptoStream にデータを書き込むと、 FileStream に暗号化されたデータが書き込まれる(暗号化されたデータがファイルに書き込まれる) コードです。

驚くべきことに、このコードを先ほどのコードと比べると、 MemoryStreamFileStream にすり替えただけなのです。

MemoryStream は世界を救う

つまり、 MemoryStream は、 byte[]FileStream 、すなわち 変数操作とファイル操作と同等に扱えるようにするクラス ということなのです。

C# では、とくにデータの変換系の処理を Stream で行うような風潮があるように見えます。

例えば暗号化や、巨大なバイナリファイルの符号化など。JSONシリアライズにも Stream クラスを引数に受けるメソッドを用います。

こうすることで、対象がファイルでもメモリでも、同じ操作でデータを扱えるという素晴らしい恩恵を享受することができます。

単体テストもコードの再利用もどんとこい、な機能ですね。

さいごに

素人が生意気にすみませんでした 。ちょっと魔がさして書き始めたら収拾がつかなくなってしまいました。申し訳ありません。反省はしていません。

この間 JSONC#のクラスにシリアライズしたりデシリアライズするときに MemoryStream を使う機会があったのですが、正直な話

なんでクラスで管理できる情報量をわざわざ MemoryStream で書く必要があるのか、変数でいいのではないか

と思いながらバリバリ書いてたんですね。

そしてふと、思いついたんですよね。JSON ってファイルの可能性があるよなぁ、と。

自分の中ではとても面白い発見だったので、ついつい長ったらしく書いてしまいました。本当に申し訳ありませんでした。

最後まで見てくださって本当にありがとうございました。

P.S.

本来書きたかったネタもとりあえず置いておきます。 MemoryStreamRead メソッドが超絶使いにくい件について。

public static class MemoryStreamExtention
{
    public static byte[] ReadAllBytes(this System.IO.MemoryStream target)
    {
        byte[] ret = new byte[target.Length];
        target.Position = 0;
        target.Read(ret, 0, (int)target.Length);
        return ret;
    }
}