rider.お品書きサクッと書いて公開して、後でちまちま修正する事がママあります。なんじゃこりゃ?と思ったら、数日してから見て貰えると嬉しいです。「スクラムの実践方法を分かりやすくマンガも交えて解説。AICHI”を開催・登壇したので、その話をまとめます。Coreの非同期処理”最初の方はキーノートのまとめ的なものにしようと思っていたのですが、その話はまた後で。8.0で.NETの非同期処理が一応の完成を見たと思っているので、改めて全体を網羅するような内容にしようと思ったものです。非同期処理の誤用が話題に登ったり、辛しみのあるコードを目撃することから「こんな」記事を書いたりしていたのですが、もっと網羅的にまとめたいなーと思っていたので良い機会として共有しようというところを目標にしました。(SlideShare)セッション50分でこの分量は無理ゲーなのはわかっていたので、スライドは後で読み返すことが出来るような構成にして、如何に喋りで補えるかに注意しました。セッションを聞きながら見ていない場合は、少し足りない(わかりにくい)部分があるような気はしています。セッションビデオと両方で補完してもらえればと思います。最近twitter上で非同期の事について話ししたものをいくつか置いておきます。みたいなところから、ちょっとずつネタを拝借。普段、この方面の話は書かないのですが、楽屋ネタを書きます。Hanselmanが、日本のコミュニティ向けに何か喋ってくれるかもしれない、という話がもちあがり、それならその方向から構成を考えるために、決定するまで待つかと思っていました。みなさんご存知の通り、大変に忙しいキーパーソンなので、この試みがキャンセルされるという事態も考えられましたが、関係者が頑張って調整に入っているようなので、ギリギリまで待つつもりでした。運営は本当に頭の痛い問題なのですが(これで折れてしまう方もいるので、私は自分が出来る範囲で出来ることしかやらない事を守るスタンスです)、登壇する側としても、セッションネタを考える場合に、上記の制限条件からネタ構成を考える必要も出てきます。10名・30名・50名・100名かそれ以上、によって、セッションの構成(しゃべる内容や構成、進行方法)を変えたほうが良い。人数が多くなると、後ろの方の席の人は、スライドが読みにくいだとか、声が聞こえないとか、没入感が得られない、といった潜在的な問題が発生しそう。スライドの色校正(たいてい黒地だとハマる会場は多いが、パリッとした映像を出せる会場なら、逆に黒地が映える。動画を流す場合は音量に注意しないと、うるさいとか音が小さいとかの問題につながる。参加人数が多いと、特別に設計されたWiFiアンテナなどを備えた会場でないと、話にならない事態になります。私はあまりオンライン向きのネタを扱わないので良いのですが、最近だとクラウドアクセスをデモの前提にする必要があったりして、非常にやりにくいでしょうね。逆に、ネットワークの信頼性が高い事が前提にできるなら、クラウドだろうがYouTubeだろうが、セッションの子道具としてバリバリ使えます。細かいところを挙げるとキリがないのでこのぐらいで。(人にもよりますが)、セッションネタ一つ決めるのにも、こういう要素が絡んできます。登壇する側は事前にこれらの要素が早く確定していれば、それだけ登壇者や参加者にとって、実りのある内容に詰めていける可能性があるわけです。冒頭の話につながるのですが、今回は会場を日立ソリューションズさん(HISOLさん)にお借り出来たのですが、その前にもいくつか候補があって調整していました。全ての会場が完全に同一条件なら、「まあ確保できるところでやればいいよね」と、問題の一つが片付くのですが、現実はそうじゃないのでヤキモキします。結局、検討していた会場は確保できなかったのでHISOLさんに打診したところ、快諾得られたので良かったのですが、確定までに2週間前までかかったこともあって、なかなか会場を公表できませんでした。登壇者のみなさんごめんなさい。いう点で、名古屋は非常に苦労するというのはなかなか理解してもらえないところでツライです。東京に近いところは楽そうだなと思うし、もっと地方ではそもそも会場になる場所さえ無く、勉強会というものを想像するのも難しいと言うのが現実でしょう。問題は上記の会場環境の話に繋がります。HISOLさんの会場では、残念ながらパブリックなWiFi設備を借りる事が出来ません(もちろん有線LANも)。すると例えば、クラウドベースのデモとかかなり厳しい(デモの悪魔に取り憑かれる可能性が高い)わけです。これはイベントの一体感もあるので、ある程度の規模のイベントではやりたくなることの一つです。実際、もしかしたらHanselmanさんとTeamsで完全オンラインでの相互コミュニケーション、というのも検討の一つとしてありましたが、まあ会場的に無理なので、実現するとなったら有償で会場を確保するかということも考えなければなりません。(そうすると参加費500円とかでは無理ですし、そもそもそんなギリギリで会場を予約できるか怪しいし、堅苦しい契約書をかわさなければならないですよ、個人で。請求や領収、今だと税金をどうするかみたいな話も出てくるかも)なので、主催が本当に小規模な勉強会を小規模な会場でやる分には良いんですが、ちょっとでも規模が大きくなり始めると、非常に辛くなります。そこを踏ん張ってやるからには、それを超える動機づけが必要になるでしょう。私の場合は自分の技術的な知見をシェアする、まあ淡良くば技術ポートフォリオとして誰かの目に止まるかどうか、ぐらいで、これで食っていく気は全く無い(食える気がしない)ので、必然的に掛けられる労力もそのぐらいになります。いうわけで、様々な軸での考えがあるため、これらをどこにバランスさせて、セッションのネタにするのか、というのは、真面目に考えるととんでもなく難しいです。Confを拾い上げるにしても、様々なセッションから何をネタにするのかは、上で挙げたような要素が、特に会場確保によって大きく変わるので、まずはどこの会場が使えるのかが最大の課題。第一としていた候補はダメ。あと2~3考えていたが、それらはどれも環境的に厳し目なため(WiFiが無いとか、ちょっと会場の母体の趣旨と合わないとか)、この時点でセッション内容についてはかなり幅が狭くなる。Hanselmanさんがビデオメッセージを送ってくれる(あるいはリアルタイムで!)かも??という打診すごい話だけど、運営側としては、完全に確定だからあとはやれるように組む、というのと違い、不確定だとどう動けば良いのかわからずツライ(それもギョムの合間を縫って行動する必要があるので)。早く...別の会場を打診アテの一つは早々に辞退(上記の後者側)、もう一つを打診。Confのセッションを拾い上げるという案は、会場の自由度があるから考えられる手法であって、もうそれは無理なので、結局自分でネタをひねり出すしか無い。非同期の話をしたいから、まあ2枠って事になるかな。問題はもうほかの登壇者さんの募集とか打診とかして枠が埋まりつつ合ったので、もしHanselmanさんの話が現実化する場合、どこの枠(時間帯)をその話にするのかを考える必要がある。Hanselmanの生の話が聞けるなら、みたいな期待で参加者が押し寄せて、出来ませんでしたテヘッ、みたいな事態を想定しなきゃいかんのか、とか...非常にツライ...正直なところは、予定が決まらなかったら、こちらから辞退することも視野に入れていました。自分のネタを考えているはずが、全体の進行まで気になり始めて、それどころじゃない感じ。最悪、キャンセルになって自分で全部仕切る(つまり2枠考えて喋る)ということを想定して準備。なんか色々頑張っても無駄に終わりそうで、それもつらい。Confのキーノートを読み込んで概要にまとめて、面白そうなトピックを実演を交えて喋る、みたいに構成するしかないかな(もはやキャパオーバーだけど、主催自分だから仕方ない)。3日前の夜に、Hanselmanさんのビデオが送られてきた!!もうこの時点では、8割方断る方向で心中決まっていて(でないと間に合わない)、手間が空いたときに断りのメッセージを送るしか無いか、と、散々たる気持ちでいたのですが、こっちからメッセージを送る前に向こうから来た。VideoIndexerで字幕データを作り、それを補正(わりと全自動では駄目な感じの字幕なので、手修正する)して、ffmpegで合成して英語版字幕付きビデオを作る(成果物はここ、但しビデオそのものは含まれていません。時間の関係で、訳を直したりタイムラインを調整したりと言ったところはまだ細部が荒いです。関係者はエンハンスして欲しいです、是非に)。2日前ビデオの内容を読み込み、字幕がアレだったことから日本語字幕化する事を考え始めた。Indexerで日本語訳出せばいいじゃん、と思うかもしれません。機能的にはありますが、まあ、察してください...)それと同時に、セッションをどう進行するかを考えていたけど、ビデオ流しながら補足を入れつつ、(50分枠なので)後半は小さいコードをライブで何かデモするか、そうだねぇ、あんまりどこにも話題にかかっていないgRPCとか良いかもしれない、と考えて、概要を調べる。自分でパース出来るのでは?と思いつく。APIを使って日本語に翻訳(4G経由の通信なので、ここにリスクはあるが)して日本語字幕ファイルを出力するツールを書き、ffmpegで合成して、日本語字幕のついたビデオをその場でみんなに見てもらう、というのは、かなりエキサイティングじゃないか?というのを思いついたので、パーサーを書き始める。パーサーは簡単だった。3としてもふさわしい。API使っているのかって、自問してましたよ...しかしここで字幕特有の難しさにブチ当たる。それは、1センテンス(日本語なら読点まで)が必ず1画面に収まるわけではなく、分割されたセンテンスに推測不能(一定時間ではない)時間タグがついて分割されている、ということなのです。Matsui.”“私の名前は松井幸治です。46/54なら、「は」の後ろで丁度切れるかもしれない。net20とか古すぎる。NMeCabをベースにチャッチャと移植して、これはこれで別の機会にネタとしてまとめよう、と思いついて移植を始める。netstandard2.1対応のNuGetパッケージ、しかも標準のIPADIC同梱でありながら、自分の辞書を使う事も出来るように構成可能にして公開しました。寝る時間が無くなって本番の運営に支障をきたしそうなので、無念だけどここでピボットする事に。MeCab.DotNetのことと日本語字幕ツールの話は、また後日のネタにする事にして、当日はHanselmanの英語字幕付きビデオを流してコメントを入れつつ、その後にHanselmanのデモと同じことを私が再現して、多分言い足りない部分を補う感じにすれば、丁度50分に収まるだろうと考えました。(今回のセッションではなく)不特定の人に英語技術ビデオを見た後に感想を聞くと、なんか腑に落ちない顔をしていることがあります。英語や英語の字幕のせいでピンときていないのかも知れないし、ビデオでは簡単にやって見せているけど、周到に準備してるからじゃないの?というような疑念があるんじゃないかと思っているので、同じことを目の前でやって見せることで払拭したかったのです。(Hanselmanさんのデモ自体は、上記のような不安要素は殆どなくて、押さえるべきところが押さえられていて流石だなと思っています。)ポイントはそんなに多くなくて、実際やって見せればシンプルな話だと思っていて、目の前で動いているプロセスの状態を見たりとか、今まさにWindows側でビルドしたコードが、WSLのubuntu上でそのまま動きましたよね(間違いなく)、みたいなことじゃないかなと。いうわけで、これで50分のセッションを構成することにしました。この内容の利点は、資料が一切不要なこと。自分の喋りと進行をその場で工夫して乗り切ればOKだ。この時点でもう23時なので、他の手段は取れそうにない...妄想ということで、今回のイベントの裏がどうなっていたのかが何となくわかっていただけたかと思います。半分は運営全体の話、半分は個人的な事情によるものですが、こうしてみるとやっぱり運営に割ける時間が足りない、運営に手間を取られすぎる、ということが大きかったと思います。コミュニティへの貢献は色んな方法が考えられます。私が一番して欲しいと思っていることは、ぜひ、自分の時事ネタでも興味のあることや知見でもなんでも良いので、発表・シェアしてほしいです。コミュニティに物理的な(生々しい話をするなら金銭的な)貢献をして頂ける場合は、運営の裏舞台、特に発表とは関係のない諸々の作業(これは私の場合ですが、それぞれのコミュニティの事情を読み取って)の負担を軽減してもらえて、結果的に時間を削減出来ると、本当に助かります。時間を確保出来るようになれば、発表する内容をより吟味したり、面白く楽しい内容に出来るようになり、より良いコミュニティイベントを開催・継続できることにつながると思います。(あえて挙げるとすれば、名古屋の場合はやっぱり会場ですかね。リッチな環境が整備された会場が提供されると、悩みの半分は解決します。場合によっては7割ぐらい楽になる感じ。今や募集とかはconnpassのようなサービスで出来るし、あとは当日そこに行って発表する事を考えるだけなので。マルチプラットフォームの本質」に対応するサンプルコードと解説を公開しています。原本はGitHubにあります。このブログの記事は、タグ1.0のコピーです。GitHubのドキュメントに対する検索性の悪さのため、ここに転載するものです(ブログスタイルに合わせるため、HTMLは多少変更していますが、記載内容は同じです)。MVPパーソナルスポンサー」として提供するものです。2019のオフィシャルドキュメントではありません。本記事執筆時には、セッションの概要のみ公開されているため、内容が密接に連携しない場合があります。内容は、原稿執筆時の情報によります。(15.9.11)です。2019でも動作すると思われますが、都合により未確認です。(2.2.204-1)で検証しました。400より多少低いところを出発点とし、章を追うことでセッションの基準難易度に近づくようにしています。できるだけ抽象的な解説を避け、具体例や図による解説、既存のコードへの参照を含めています。reserved.本コンテンツの著作権、および本コンテンツ中に出てくる商標権、団体名、ロゴ、製品、サービスなどはそれぞれ、各権利保有者に帰属します。ライセンスは外部参照を除き、MITとします。コードにはいかなる保証もありません。ご自身のプロジェクトに適用する場合は、各自の判断で行ってください。この資料はコミュニティコンテンツです。requestを受け付けます。サンプルコードこのリポジトリ内に配置され、ドキュメント内からリンクされているので参照してください。PipelinesでCIしています:概要.NETにおけるマルチプラットフォーム対応を支えるための、アセンブリレベルでの互換性の対処方法について、サンプルコードと解説を行います。章が進むにつれて、より細部についての解説を行っています。Injection)でしょう。この章では、インターフェイス分離と、それに似たもう一つのテクニックについて解説します。インターフェイス分離インターフェイス分離は、最も一般的で基本的なOOPのテクニックです。.NETのインターフェイス型を使用して、異なる実装の詳細を、同一のインターフェイスで分離する事により、マルチプラットフォームに対応した処理として共通に扱えるようにします。Part1_SplitByInterfaceプロジェクトは、以下の機能を持ちます:複数の値を入力すると、計算結果が出力される。入出力の対象は、コンソール(コマンドライン)と、GUI(WPF)である。計算を司る共通のライブラリ「Calculator.Core.dll」と、それらを使用する各アプリケーションをインターフェイス定義で分離します。コンソールアプリケーションの場合:GUIアプリケーションの場合:この図のように、右側の計算ライブラリには、プラットフォーム固有の実装は全く含まれず、左側のアプリケーション側に固有の実装を用意しておき、インターフェイス定義経由で計算ライブラリから操作できるようにします。ConsoleCalculatorの実行結果:WpfCalculatorの実行結果:ここでは、手動での依存注入(メソッド引数で直接注入する)を行っています(より発展した例として、MEFや各種依存注入ライブラリを併用することが考えられますが、割愛します)。このサンプルコードで重要な点を以下に挙げます:計算を実行する本体(Calculator.Core)ライブラリは、プラットフォーム中立であり、netstandard2.0としてビルドしている。WpfCalculator)にのみ含まれる。同じ手法を用いて、その他のプラットフォームにも移植できる。その際、計算ライブラリに手を入れる必要がない。switchテクニックこのテクニックは、以下のような二種類のアセンブリを用意しておき、コードのビルド時と実行時で異なるアセンブリを使用するものです:ビルド用アセンブリ:ユーザーのアプリケーションやライブラリのビルドに使用するためだけのアセンブリ(「参照アセンブリ」と呼ぶ)。アセンブリ内の実装コードは、まったく存在しないか部分的にしか存在しない。コンパイラが、型のメタデータ定義(クラス名やメソッド名、型や引数群など)さえ確認できれば良い。実行時用アセンブリ:実行時に使用するアセンブリ。それぞれのプラットフォームに応じたアセンブリ(但し、アセンブリの厳密名とインターフェイス定義は上記と同一)を用意する。アセンブリ内部の実装はそれぞれのプラットフォームによって異なる。これは.NET標準に含まれるライブラリでも活用されています。「System.Runtime.dll」について考えます。ビルド時に参照するアセンブリ(SDKに含まれる)は、実は内部に実装が存在しません。型のメタデータ定義は存在するため、ビルド自体は成功します。その結果、アプリケーション内部には、System.Runtime.dllを使用している、という参照情報だけが残ります。Coreのランタイムディレクトリに配置されています)。このアセンブリにはプラットフォーム環境に依存した実装が入っていますが、型のメタデータ定義は同一であり、アセンブリローダーは問題なくロード出来るため、アプリケーションが実行できます。参照アセンブリとメタデータが異なっていた場合(例えば、あるクラスのメソッドの引数の型が違っているなど)は、実行時にMissingMethodExceptionなどの例外が発生する可能性があります。そのため、参照アセンブリと実行時アセンブリのメタデータが同一となるように、注意深く実装しなければなりません。switchテクニックを使って実装したものです:参照アセンブリとして、Host.Referenceプロジェクトを用意する。実装は空で、クラスとメソッドが定義されているだけである。上記参照アセンブリの定義と同一だが、それぞれコンソールとWPFに対応する実装を含んだ、Host.ConsoleプロジェクトとHost.Wpfプロジェクトを用意する(アセンブリのファイル名はすべて同一で、Host.Core.dll)。他のアセンブリはすべて共通で、アプリケーション実行前にHost.RefereceをHost.ConsoleまたはHost.Wpfに入れ替える。Console.batから起動すると、Host.Consoleに置き換えて実行する。Wpf.batから起動すると、Host.Wpfに置き換えて実行する。置き換えずに実行すると、Host.Referenceを使うことになるため、実行時例外が発生する。switchテクニックは、実装を入れ替えることができると言う点で、インターフェイス分離とよく似ています。インターフェイス分離の場合は、設計者が最初から適切に分離設計をする必要があります。switchは、CLRがアセンブリを同一視する方法に乗じて、コードに全く手を入れずに、プラットフォーム依存処理を入れ替えることを可能にします。OOPの一般的な手法。switch手法で互換性を実現しました。ここまでの解説で、PCLが何十というプロファイルを持っていたことを考えると、互換性を維持することがいかに困難であったかが想像出来ます。switchを適用する場合は、メタデータの不一致を何らかの方法で開発時に検証できるようにするか、あるいはテンプレートコードの自動生成や合成ができるようにするなどの手法を用意し、開発の困難さを軽減することが重要です。アンマネージ連携P/Invokeは、.NETのアンマネージドコード連携の手段の一つです。一つはCOM連携ですが、本稿では割愛します。P/InvokeP/InvokeはCLRに組み込まれている、ネイティブコードライブラリとの連携機能です。C#などのコンパイラは、P/Invokeを簡便なシンタックスで利用可能にしています。P/Invoke!");}ネイティブコードのライブラリがダイナミックリンクライブラリとして用意されていれば、全く同じ手法でコードを記述することが出来ます。LinuxやMacOSといったプラットフォーム向けのコードは、それぞれで利用可能なネイティブコードライブラリが異なることが多々ありますが、P/Invoke自体の使用方法は同じです。Coreやmono向けのライブラリも、同じように作ることができます。ネイティブコードライブラリがクロスプラットフォームで(dll、dylib、soなど、ライブラリの形式が異なっても)同じAPIを提供していれば、同一のP/Invokeコードがそれぞれのプラットフォームで実行できます。switchテクニックをP/Invokeと組み合わせると、.NETでマルチプラットフォームの一貫したライブラリ設計を行うことができます。前章の依存コードの部分をP/Invokeを使って実装すれば、そのプラットフォーム固有のAPIがネイティブコードであったとしても、部品として使える共通のアセンブリを構築できる、と言うことです。Part3_PInvokeプロジェクトは、これまでの説明を踏まえた、マルチプラットフォームアプリケーションです:コマンドライン引数に指定した文字列が、デバッグメッセージとしてシステムに送信される。switchテクニックを使用する。Windows用とLinux用で、実装は異なるが共通のメタデータを持つアセンブリを用意する。APIを使用して出力する。これはSysinternalsのDebugViewユーティリティで確認できるほか、各種デバッガがアタッチされていれば、デバッガ上で確認できる。APIを使用して出力する。switchテクニックを使用する理由は全くありません。switchで実装を行っています。DebugMessageプロジェクトは、DebugMessage.Referenceの参照アセンブリを使ってビルドします。実際に必要なのは、実行用のアセンブリ、DebugMessage.Win32とDebugMessage.Linuxです。前章のサンプルと同じく、これらを実行時に置き換える必要があります。startでrsyslogを動かしておく必要があります):ガベージコレクションの影響P/Invokeを初めて使う場合、ネイティブコード実行中の.NETインスタンスがどのように扱われるのか、疑問に思う方も居ると思います。.NETで生成したインスタンスをP/Invokeを通じてネイティブコードに渡した場合、ガベージコレクタが意図せず回収する可能性は無いのか、という点です。byte配列は自動的にマーシャリングされる。buffer[0];}ガベージコレクタは、引数・ローカル変数・フィールドなどからインスタンスが参照されているかどうかを追跡しています。この場合、bufferのインスタンスが回収されてしまうことはありません。一般的に、APIが実行を終えて戻ってきたとき(A)、当然bufferにアクセス出来ることが期待できます。(一般的には、マーシャリングという用語は、データ形式の相互変換とスレッドなどのコンテキスト切り替えの両方の意味で使いますが、本稿では前者のデータ形式の相互変換にのみ使用します。)ガベージコレクタがインスタンスを回収するかどうかの他にも、ネイティブコードとして考慮すべき点があります。それは、インスタンスの物理的なアドレス(ポインタ値、但し仮想メモリ上の)が変わるのか同じ位置にあるのか、ということです。CLRの場合は、ヒープコンパクションと呼ばれる機能により、必要に応じてインスタンスが移動する事があります。Weakネイティブコードライブラリを設計する場合は、CLRの伺い知れないところで動作するため、通常、インスタンスが維持され、かつインスタンスが移動しないこと(Pinned)が要求されます。稀にインスタンスが存在し続けて欲しいが、インスタンス自体にはアクセスしない(Normal)、という場合もあります(Weakについては本資料では割愛します)。先程説明したように、インスタンスが追跡可能な状態であれば、何もしなくてもNormalと同様に維持されます。インスタンスがどこからも追跡できなくなるような状況下では、明示的にNormalまたはPinnedとしてCLRに通知する必要があります。GCHandle構造体を使用すると、追跡可能かどうかにかかわらず、PinnedやNormalの状態を作り出すことができます。Pinnedとした場合は、インスタンスへの生のポインタを取得できます。不要になったら解放する必要がある。解放しなかった場合はメモリリークする。}}このコードは、先程の例とほぼ同じです。P/Invoke呼び出しのためのポインタへのマーシャリングを自動で行うか、手動で行うかの違いです。ガベージコレクタに誤って解放されたり、メモリリークしないように注意を払う必要がある例えば、ネイティブライブラリの関数によっては、引数として渡されたポインタを保持して、ずっと後になってから参照して使うこともあります。そのような場合には、最初に明示的にNormalとし、明らかにネイティブコードで使用されなくなるまで保持し、最終的に解放する必要があります。バッファへのポインタが頻繁に必要になる場合は、自動マーシャリングするよりも、一度だけ手動マーシャリングしてPinnedとし、連続してポインタを使用し、最後に手動で解放したほうが効率的かもしれません。APIは指定されたバッファアドレスに、数列を書き込みます。APIを呼び出します。この呼び出しの、自動マーシャリングと手動マーシャリングの例を示します。両者の処理時間差を、簡易的に計測します。手動マーシャリング、となっている事を確かめることが出来ると思います。両者の差はかなり小さいはずです。配列のマーシャリングコストはそれほど高くないからです。この例では、非常に単純なデータ構造(配列)ですが、実際のマーシャリングシナリオはもっと複雑です。場合によっては、手動マーシャリングを行わないと、現実的なパフォーマンスを引き出せない可能性があります。マーシャリングの詳細については、「プラットフォーム呼び出しによるデータのマーシャリング」を参照してください。以下に、P/Invokeを使用する際に、暗黙または意図して注意する必要のある事項をまとめます:マーシャリングを自動で行うのか、手動で行うのか、及びパフォーマンスへの影響。ガベージコレクタによって意図せずインスタンスが移動・解放されないようにする、あるいは正しく解放されるようにする。ランタイムサイドここまでは、一般の開発者がマルチプラットフォーム対応アプリケーションを設計する場合において考慮すべき点を、如何に依存コードを分離して実現するか、という視点で解説しました。この節では、.NET自身が、マルチプラットフォーム対応するためにどのような手段を使用しているのかを述べます。Coreにおいて、coreclrやcorefxがどのようにマルチプラットフォームを実現しているのかという点に注目します。基本的に、今まで述べてきたテクニックが利用できる部分では、そのまま利用できます。System.IO.FileStreamクラスは、ファイルへのアクセスにWindowsとLinuxで異なるAPIを使用するはずです。switchを利用して切り分け、P/InvokeでそれぞれのAPIを呼び出している、と想像出来ます。library)を、「mscorlib.dll」という単一のアセンブリで担っていました。Frameworkの歴史で巨大化してしまったmscorlib.dllを分割するために、前節で例示した「System.Runtime.dll」と「System.Private.CoreLib.dll」、その他の細かいアセンブリ群に細分化しました。CoreCLRランタイムと密接に絡む実装が含まれるアセンブリが、System.Private.CoreLibアセンブリです。この図は、Part1_SplitByInterfaceプロジェクトのCalculator.Core.dllをILSpyで確認したものです。左上がCalculator.Coreアセンブリで、netstandardアセンブリに依存しています。netstandardアセンブリはかなり多くのアセンブリに依存していますが、その中にSystem.Runtimeアセンブリが存在します。System.RuntimeアセンブリはSystem.Private.CoreLibアセンブリに依存し、ここから先に依存する.NETのアセンブリはありません。System名前空間を確認すると、見慣れたArray,Boolean,Byte,Charといった型が定義されていることがわかります。Frameworkとの互換性を実現していました。マルチプラットフォーム動作の実現については、後述するQCallやFCallに類似する仕組みを用いて、同一の実装アセンブリで実現していました。monoの実装は、corefxのコード取り込み作業が継続的に進んでいることもあって、流動的です。System.Private.CoreLibアセンブリ内には、かなり多くのクラスや構造体が定義されています。これらの型のメソッドは、そのプラットフォームに依存した処理が実装されているはずです。}}付加機能のためのコードが前後にありますが、結局s_WriteCore(message)でメッセージを出力しているようです。このフィールドはActionデリゲートで、初期化時にはWriteCore(string)メソッドを指しています。2に収まる範囲、とかそういう事かもしれません)。}}見慣れたOutputDebugStringというシンボルが出てきました。APIを直接呼び出す実装が含まれていることが確認できました。APIを直接呼び出すようになっていましたが、Linuxの方はSystem.Nativeと付けられたネイティブライブラリのSystemNative_SysLogメソッドを呼び出しているようです。layer)」と呼んでいます。APIのインターフェイスを模すように作られて、今に至るようです。以上を踏まえて、ソースコードがどうなっているかを確認してみます。System.Diagnostics.Debugクラスのコードはここにあります。classで分割されていて、WriteToDebuggerメソッドの実装は、DebugProvider.Windows.csと、DebugProvider.Unix.csの2つのファイルに分割されています。ビルド時に、Windows版とLinux版で使い分けるのでしょう。内容はILSpyで見たものと全く同じです。CoreはLinuxだけではなく、FreeBSDもサポートしています。DebugProvider.Unix.csは名前の通り、Unix環境で共通に使われるようです。ここではこれ以上掘り下げませんが、LinuxとFreeBSDで実装に違いがあるとすれば、以下の図のようにネイティブライブラリのPALの方で吸収しているのではないかと思います。FEATURE_PALと言うプリプロセッサ指令で切り分けられていることがあります。Frameworkのソースコードを参照できる「referencesource.microsoft.com」でも、ところどころで見ることが出来ます。Frameworkの頃から、PALによるネイティブ実装の切り替えを想定(あるいは使用)していた可能性があります。残念ながら、サイトで見ることができるソースコードはマネージド側だけなので、PALが何に移植されていたのかはわかりません。switchで実行アセンブリが差し替わっていることを確認しました。CLRの内部機能を呼び出す場合は、P/Invokeではなく、別の方法を使用する必要があります。HCallについて示します。それぞれの手法は異なり、目的によって使い分けられています。同じ機能を別の呼び出し方法で呼び出すことは出来ません。QCallQCallは、P/Invokeにかなり似ています。P/Invokeで想定できるマーシャリングは、プリミティブ型であればそのまま使えます。wchar_t*)でマーシャリングされます。P/Invokeの場合、文字列の返却にStringBuilderを使いますが、代わりの低コストな手段が用意されています。それに加えてP/Invokeでは実現しない、ネイティブからの例外のスローが可能です。(P/Invokeと似ているが異なる。returnValue;}第一に、QCallはP/Invokeと同じようにDllImport属性を使います。ライブラリ名は固定的に”QCall”を指定しておきます。ネイティブ側の実装は、マネージド側に値を返したい場合(この例のように、戻り値として文字列を返す想定)は、スタック上の文字列参照を追跡できるようにするStringHandleOnStackという型を使います。同様に、任意の参照型(objref)には、ObjectHandleOnStackを使います。これらを使うことで、P/Invokeで必要であったマーシャリングのコストを削減できます。そもそもStringHandleOnStackや、そのインスタンスを取得するJitHelpersは、publicではありません(ソースコードはこちら)。QCallを処理するメソッドは、ほぼ公開メソッドにはなり得ず、System.Private.CoreLibアセンブリ内に閉じている必要があります。したがって上記の例のように、QCallメソッドはprivateとし、publicなファサードメソッドを定義する必要があります。CLR内部に用意しておく必要があります。(つまり、文字列を示すポインタがGCによっていきなり移動したりしない。TRUE;}このメソッドを呼び出せるようにする(マネージド側のQCall宣言との結び付け)には、ecalllist.hヘッダファイルに対応を記述する必要があります(次に解説するFCallの宣言も、ここに記述します)。ところで、QCallで実装したネイティブコードを実行中は、ガベージコレクションが起きる可能性が常に存在します。P/Invokeのときと同様に、ヒープに確保されたインスタンスに生ポインタでアクセスする場合は、ガベージコレクションによって移動したり回収されたりしないように、実装者が管理する必要があります。FCallQCallの概要を見ると、その他の呼び出し手段は必要ないように見えます。FCallがQCallと決定的に異なるのは、FCallの呼び出しを実行している間、自動的にガベージコレクタが停止することです。P/Invokeの場合、ガベージコレクションによる破壊を回避するには、GCHandleを使ってインスタンスをPinned状態にするかNormal状態に強制することでした。QCallの場合は、QCallのためのヘルパー型を使うことで、安全に処理することができますが、そこには少なからずコストを伴います。FCall呼び出しを使用すると、その間ガベージコレクタが動かないため、いつでも生ポインタをPinnedされているものとして扱えるようになります。当然ですが、FCallで呼び出されたメソッドの処理時間が長引くと、.NETプロセス全体に悪影響を及ぼします。get;}ILSpyを定常的に使っている方は、メソッド呼び出しをたどっていくと、この属性に行き当たるのを見たかも知れません。CLR内部のFCallメソッドへの扉、と言うわけです。(FCallのためのFCIMPLマクロの終端定義)FCIMPLENDこの例では、対象のSystem.Stringインスタンス(つまり文字列のthis)は、str引数で参照されますが、これは生ポインタです。FCallなら、ガベージコレクタがインスタンスを移動したり回収したりする心配をすることなく、生ポインタを直接操作することが出来ます。インデクサアクセスは、上記の通りO(1)です。FCallではガベージコレクタが動かないと言っても、コードのサイズは小さいため、処理中にそのタイミングが重なる可能性はかなり低いと思われます。FCallで呼び出されたメソッドが長時間の処理を必要とする場合は、途中で(問題ないタイミングで)一時的にガベージコレクションを行わせることができます。この事をGCポーリングと呼びます(上記コードでは使用していません。ここでは詳細を省きます)。FCallとQCallの使い分けについての決定的な差は、上記の通り、ガベージコレクション操作とマーシャリングの影響を最小限に抑えたいかどうかが、判断の基準となります。非常に密接にCLRの内部操作と連携する必要がある場合(その間にガベージコレクションが実行されては困る場合)にも、FCallを使うことがあります。HCallHCallはFCallとほとんど違いがありません。HCallとして実装したネイティブメソッドから例外をスローした場合に、記録されるスタックフレームにHCallのメソッドが含まれなくなります。これは、主にJITが生成したコードが使うヘルパーメソッドに使われ、例外をスローする際に内部的なメソッドを記録しないようにしています。JIT_ChkCastArrayは、配列がキャスト可能かどうかの判定を行うHCallメソッドです。キャストできない場合に例外をスローしますが、例外のスタックトレースにこのメソッドは含まれません。まとめマルチプラットフォームの環境によっては、表面的に同じ操作を行うメソッドであっても、内部のネイティブコード連携では、QCall,FCall,HCall(場合によってはP/Invoke)を使い分けるということがあるかもしれません。switchテクニックで、System.Private.CoreLibアセンブリが差し替わることで、柔軟に対応できることがわかります。2018の23日目のダブルエントリーです(遅刻)。開発用SDKのことです。リンクはこのSeeed公式のページからでも買えますが、国内にも代理店がいくつかあるので、今ならそちらから買うほうが入手性も良いでしょう。上記のSeeedさんが販売しているボードはMT3620を載せた、評価用の開発ボードです。語弊あるのですが、この評価ボードの事を、以降MT3620と呼びます。若干意味不明に思うかも知れないので、以下に背景を書いておきます。関する記事が沢山出てきますが、これは私が始めた「.NETのバイナリをC言語のソースコードに変換する」オープンソースのプロジェクトの事です。完成していなくて、開発途上です。開発を始めたのは、丁度1年半ぐらい前からです。いくつかの勉強会やグロサミやらで細部とか進捗とかを発表していました。あと、YouTubeに大量にIL2Cを解説したビデオを公開しています。資料などはそれぞれの勉強会サイトやGitHubをあたってもらえると良いと思いますが、ざっくり言うと、この図のとおりです。C#でプログラムを書いて(もちろん、MT3620向けに)、それを普通にC#のプロジェクトとしてビルドします。すると、”SphereApp.dll”のような.NETのバイナリが出来ます。これをIL2Cに掛けると、C言語のソースコードに変換されたファイルが生成されます。SDKが提供するgcc)に掛けて、デバイス用のネイティブバイナリを生成します。使ってコードを書く」と言うことになるわけです。Coreのネイティブビルドやngen、monoの極小ビルドと比較しても、到底不可能なぐらいにフットプリントを小さく出来て、内部構造がシンプルで、ネイティブコードとの連携をコントローラブルにする、というものです。ILを変換したネイティブコードを、インタプリタではなくAOTコンパイルしたコードで動かせるという実証や、UEFIやWDMなどの極めて特殊な環境でも動作するという実証実験をしています。Sphereを加えるという目標です。いうことで、MT3620のセットアップの流れサンプルコードの構造の確認とC#での実現方法の大きく二章立て構成で解説します。(ところで、来年から仕事無いので、これをやらせてくれる仕事ありましたら、凄く嬉しいのでDMください)MT3620のセットアップの流れMT3620は割と早い時期に入手していたのですが、(某人がこれの環境構築に四苦八苦してたのを横目で見てたこともあり...)色々忙しくて放置してました。今の時期にデバイスクレーム(MT3620の登録作業)からやる話は、同様にこれから始める人向けにも良いんじゃないかと思ってます。非常に参考になりました。この通りで出来ましたが、私固有の問題が少しあったので、ログ含めてセットアップの記録を残しておきます。SDKもそこそこバージョンアップされているので、あまりバージョンがかけ離れると、ここに書いてあることも無意味になるかも知れません。AD面倒くさいんですが、組織アカウントを作っておくところまでは、記事通りやっておきます。私の場合、個人的にいくつかのサブスクリプションと組織が紐付いている状態で実験したりしていた関係で、シンプルに無料チャージから始めた方とは事情が違ってしまっている可能性があります。消さないようにしなければ)。Sphereテナントを作ります。SDKのコマンドプロンプトで作業します。ここからの操作はAzureのダッシュボードは不要です。00:00:23.2329116.(そう言えばMatsuoka氏も最初の頃そんな事を言っていた事を思い出した)ただ、上記のように、私のMT3620はファームウェアが古すぎだと言われた(まあ、放ったらかしにしてたしね)ので、早速suggestされたコマンドで更新をかけました。00:02:41.2721537.難なく、更新されたようです。MCU対応デバイスは、デバイスクレームを一度しか実行できません。詳しくはググってみてください)さて、気を取り直して、WiFi出来るようにしておきます。00:00:01.0424492.いい感じです。一度動き出せば、管理は非常に簡単です。WiFi構成はいつでも上記コマンドで変更できます。最後に、MT3620を「開発モード」にします。いうことで、この操作は意味不明に思われるかも知れません(特にArduinoやESP32のようなプラットフォームを触っていると)。MCUは、様々な組み込み機器のCPUとして使われることを想定しており、真にIoTデバイスとして使う場合は、逆に開発モードと言う危なげな状態にならないほうが良いわけです。この辺は、最近のスマートフォンでも同じように制限を掛けていると思うので、それと同じように考えればわかりやすいと思います。00:00:32.3129153.これで開発モードに切り替えできました。さんのLチカの記事通り、Blinkサンプルを実行してLチカ出来る、言い換えればDevKitの用を成しているかどうかをを確認しておきます。これで、MT3620が使える状態になったかどうかの確認は完了です。Wiki・日本語)を参照すると良いでしょう。0;}なんだか、至って普通の、なんのヒネリもないmain関数です。あと、いくつか気がつくことがあります:Log_Debug関数でログが出せそう。InitPeripheralsAndHandlersとClosePeripheralsAndHandlersでデバイスの初期化処理と終了処理らしきことをしている。これはSDKの関数ではなく、このサンプル内にコードがあるので、すぐ後で見てみます。ど真ん中にポーリングのようなコードがある。コメントに”SIGTERM”とかコメントがある。