古賀 信哉のWindows CE Blog

MCP Logo MVP Logo


OS の起動時間短縮(2/2)~ビルトインのデバイスドライバを遅延ロード

■はじめに
昨年9月のエントリで、「今回と次回は、WEC の起動時間を短縮する方策を、二つ紹介します。」と述べて一つめの方策を書いた後、一年近く時間が空いてしまいましたが、二つめの方策を書きます。

前回紹介した方策では、RAM レジストリを永続化した構成において、RAM レジストリの初期化時間を短縮する、というものでした。今回紹介するのは、ビルトインのデバイスドライバのうち、起動直後ないし起動中に動作していることが必須ではないものを、OS 起動中にはロードされないようにする(必要になった時点で後からロードする、つまり遅延ロードする)、という方策です。

この方策により、WEC が起動して初期画面(標準シェルを組み込んだ場合は、デスクトップ画面)が表示されるまでの時間を若干短縮することが可能です。短縮される時間は、遅延ロードするドライバ群の初期化処理に要する時間の合計です。従って、初期化処理に要する時間が長いドライバがあれば、遅延ロードさせる対象の候補となります。

注意:WEC 2013 には、標準シェル(explorer.exe)は付属しません。代わりに、最少機能のミニシェルを使う必要があります。

もちろん、遅延ロードさせるドライバは、WEC の起動直後ないし起動中に動作していなくても構わないものに限られます。たとえば、ディスプレイドライバは、WEC のウィンドウシステムである GWES が必要としますので、遅延ロードさせるわけにはいきません。また、LCD ディスプレイを接続した端末であれば、LCD バックライトのドライバも、WEC の起動中に動作を開始しなければいけません(そうでないと、LCD の画面が真っ暗で、初期画面が表示されませんからね)。逆に、USB や SD のホストコントローラのドライバは、遅延ロード対象の候補になるでしょう。

USB や SD の、対向デバイスごとのドライバ、つまりクラスドライバは、対向デバイスが接続された時点で初めてロードされる Plug & Play ドライバですが、それらをロードするホストドライバを遅延ロードさせても、問題ないケースは多いでしょう。他の候補としては、バッテリードライバも挙げられます。これら、ビルトインのデバイスドライバ(※USB や SD のクラスドライバのような Plug & Play ドライバではなく、OS の起動時にロードされるドライバ)のうち、遅延ロード可能なものを洗い出して遅延ロードさせることにより、WEC が起動して初期画面が表示されるまでの時間を、数10[ms]~数100[ms]、場合によっては数秒程度短縮できるかも知れません。

ビルトインのデバイスドライバを遅延ロードさせることによる、WEC の初期画面が表示されるまでの時間の短縮は、OS のコンフィグレーションや、遅延ロードさせるドライバが初期化時に行う処理内容、及びドライバが制御するハードウェアに依存します。つまり、どれくらいの時間が短縮されるかは、ケースバイケースです。そのため、ここでは具体値には触れず、上記の定性的な説明に留めます。

なお、今回の説明に通じる内容は、WEC 2013 のディベロッパーガイドでも触れられています。興味のある方は、MSDN にある、次のページもご覧になってみて下さい。

 Optimize Driver Load Time (Compact 2013)
 https://msdn.microsoft.com/en-us/library/dn194541.aspx

■ビルトインのデバイスドライバ
さて、遅延ロードの手順について説明する前に、ビルトインのデバイスドライバとは何を指すのかを、明らかにしておきましょう。上で、ビルトインのデバイスドライバを「OS の起動時にロードされるドライバ」と書きましたが、OS の起動時にロードされるかどうかは、レジストリ設定で決まります。具体的には、ドライバのレジストリキーが
 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn]
の下に設定されていると、OS の起動時にロードされるのです。このことは、WEC 2013 のディベロッパーガイドの次のページで説明されています:

 Device Manager (Compact 2013)
 https://msdn.microsoft.com/en-us/library/jj659831.aspx

上のページの中ほどにある、”Registry Settings” という項をご覧ください。ここに、デバイスドライバのレジストリ項目の説明もあります。

■デバイスドライバのロード防止設定
上述したように、レジストリキーを [HKEY_LOCAL_MACHINE\Drivers\BuiltIn] の下に配置したデバイスドライバは、OS の起動時に自動的にロードされます。この設定を変え、”BuiltIn” ではなく、他のキーの下にレジストリキーを配置すると、OS の起動時にはロードされません。そのような場合は、ActivateDevice[Ex]() を使って明示的にロードしない限り、ロードされることはありません。

たとえば、バッテリドライバのレジストリキーは
 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Battery]
ですが、これを変更し、
 [HKEY_LOCAL_MACHINE\Drivers\AddOn\Battery]
にすると、起動時にロードされなくなります。つまり、ビルトインのデバイスドライバを OS の起動時に自動ロードされないようにするいは、そのレジストリキーを変更し、[HKEY_LOCAL_MACHINE\Drivers\BuiltIn] の下ではない階層に移せばよいのです。

ただし、バッテリドライバのように、WEC 標準のデバイスドライバのレジストリキーを変更してしまうと、不都合を生む場合があります。そのドライバに対するレジストリ設定が、複数の .reg ファイルに分割して記述されている場合です。実際、端末が違っても変わらない共通的な設定は WEC 付属の共通設定を記述した .reg ファイル(%_WINCEROOT%/public/common/oak/files/common.reg)に記述し、端末ごとに変わる設定を、OS Design プロジェクトの .reg ファイルや、あるいは、BSP の .reg ファイルに記述する、ということは珍しくありません。その際、WEC 標準で決まっているレジストリキーを変更してしまうと、不整合が起きてしまう危険があります(※当該ドライバに対する、全ての .reg ファイルに記載された設定を変更できず、一部は BuiltIn 配下のままになってしまう、というケースが考えられます)。その危険を回避するには、レジストリキーは標準設定のまま(※つまり、ビルトインのデバイスドライバのレジストリキーは、 BuiltIn の下に配置する設定のまま)で、自動ロードされるかどうかだけを変更することが必要です。そして、そのためのレジストリ項目があるのです。

デバイスドライバの自動ロードは、Flags というレジストリ項目の設定値で防止できます。上で紹介したページの、”Registry Settings” という項にある表を見て下さい。表の4行目にある、DEVFLAGS_NOLOAD というフラグが、そうです。デバイスドライバのレジストリ設定で、Flags の値が DEVFLAGS_NOLOAD (0×00000004) を含むと、そのドライバはロードされません。つまり、BuiltIn の下にレジストリキーが配置されていても、OS の起動時に自動ロードされません。ただし、OS の起動時に限らず、ActivateDevice[Ex]() を使ってもロードできません。

■ビルトインのデバイスドライバの遅延ロード手順
デバイスドライバのレジストリ設定において、Flags に DEVFLAGS_NOLOAD (0×00000004) を含む値を設定することにより、ビルトインのデバイスドライバであっても、OS の起動時に自動的にロードされるのを防ぐことができるのは分かりました。しかし、そのままでは、ActivateDevice[Ex]() を使ってもロードできず、ロードできないままになってしまいます。では、どうすればよいのでしょうか?

解決方法は、簡単です。ドライバをロードする時に、レジストリ設定を一時的に変更すればよいのです。つまり、ActivateDevice[Ex]() を呼び出す前に、レジストリの Flags の値から DEVFLAGS_NOLOAD フラグを取り除き、ActivateDevice[Ex]() の呼び出しが終わったら、再度 DEVFLAGS_NOLOAD フラグを加えればよいのです。

つまり、ビルトインのデバイスドライバを遅延ロードするために必要な手順は、次の通りです:

1.) OS Design のレジストリ設定ファイル(OSDesign.reg)において、遅延ロード対象のデバイスドライバの Flags の値を、DEVFLAGS_NOLOAD (0×00000004) を含むものに設定するよう記述を追加する(※common.reg などで Flags が設定されていれば、OSDesign.reg で上書き設定する)。

2.) 必要になった時点で、次の処理を実行する:
 2-1.) RegSetValueEx() を使って、そのドライバのレジストリの Flags の値を変更する(DEVFLAGS_NOLOAD を含まない値に変更する)。
 2-2.) そのドライバのレジストリキーを指定して ActivateDevice[Ex]() を呼び出す。
 2-3.) 再び RegSetValueEx() を使って、Flags の値を変更前のものに戻す。

上の手順のうち、(2) は、WEC の起動後に自動実行されるようにするのが簡単でしょう。遅延ロード対象のデバイスドライバ群に対して (2) を実行するアプリケーションを作り、そのアプリケーションを、/Windows/Startup/ ディレクトリに配置すれば、WEC が起動して、標準シェルのデスクトップ画面が表示された直後に、遅延ロード対象のデバイスドライバ群がロードされて動作開始します。この時、画面表示を見ているユーザにとっては、それらのデバイスドライバが起動時に自動ロードされる場合よりも起動時間が短縮されたように見えるでしょう。

■おまけ
/Windows/Startup/ ディレクトリに配置されたアプリケーションを自動起動する処理は、標準シェルのソースコード(※WEC 2013 には付属しません)でいうと、
 %_WINCEROOT%/public/shell/oak/hpc/explorer/main/explorer.cpp
で実装されている ProcessStartupFolder() で行われます。この関数が、SHGetSpecialFolderPath() で Startup ディレクトリのパス(つまり、/Windows/Startup/)を取得して、その中に入っている実行ファイル全てに対して順次 ShellExecuteEx() を呼び出す、という仕組みです。ProcessStartupFolder() は、DoStartupTasks() を介して、標準シェル(explorer.exe)のメインルーチンである WinMain() から呼び出されます。

explorer.exe の WinMain() では、標準シェルの初期化動作を行った後、CDesktopWnd クラスのインスタンスを生成して Create() を呼び出し、デスクトップ画面を表示します。さらに、CreateTaskBar() を実行するスレッドを生成・始動して、タスクバーを表示します。それらが済んだ後で、DoStartupTasks() を呼び出すようになっています。従って、遅延ロード対象のデバイスドライバをロードする処理は、/Windows/Startup/ に配置したアプリケーションで行うようにしておけば、標準シェルの初期画面が表示された後で遅延ロードが実行されるので、遅延ロードのぶん、初期画面が表示されるまでの時間が短縮される、というわけです。

標準シェルを使わず、カスタムシェルを使う場合(WEC 2013 のミニシェルを使う場合など)も、同じような方策で対応できるでしょう。WEC 7 をお持ちで、興味のある方は、explorer.cpp の内容を眺めてみると、面白いかも知れません。

Add comment 2015/09/16 koga

Windows Embedded Compact の現在と今後の(勝手な)予想

今回は、もともと他の媒体に寄稿したものの、そちらでボツになってしまった原稿を載せます。Windows Embedded Compact (WEC) が現在どうなっていて、今後は、どうなるんだろう?という疑問に答える内容だったのですが、WEC の将来に対する僕の個人的な予想を載せるということが、その媒体の性格には合わないとうことでボツになりました。以下で書いている予想は、あくまでも、僕の勝手な予想であり、まったく見当はずれな可能性は大です。現時点で、公開されている情報から予想されることを書いてみました。とはいえ、少なくとも、WEC の現在の状態、特に、最新版の WEC 2013 のライフサイクルについては正確な情報です。

ちょうど、つい先日(2/1)、Rasberry Pi 2 で Windows 10 が動くようになる、との発表が出たばかりで、”Windows on Devices” や Windows Developer Program for IoT に対して、多くの方から関心が寄せられているようです。このあたりの動きに、これからも注目していこうと思います。

(以下、原稿です。)

 Windows CEやWindows Embedded Compactは現在どうなっているのでしょう。
 無くなったりしないのですか?

はじめまして。古賀です。

新年早々、一瞬ぎょっとしましたが、良い質問ですね。Windws CE、現在のブランド名 Windows Embedded Compact (WEC) は、健在です。

一昨年の6月にリリースされた、最新メジャーバージョンの WEC 2013 は、これから8年10ヶ月後の2023年10月までがサポート期間です。ライセンスの停止、つまり搭載製品を出荷可能な期間の終了(EOL)は、これから13年半後の2028年5月です。リリース後5年間のメインストリームサポート期間を見ても、あと3年半残っています。ですから、かなり短めに考えてみても、あと3年半は、安心して新規の製品開発に使えます。そして、13年半後の EOL までは、搭載製品を出荷できるのです。少なくとも、それまでの間は無くなりません。

WEC、および、その他の Windows Embedded ファミリーの製品ライフサイクルについては、次のページに記されています:

 製品ライフサイクルおよびサポート | マイクロソフト
 http://www.microsoft.com/windowsembedded/ja-jp/product-lifecycles.aspx

また、先ほど確認してみたところ、日本国内のライセンス販売代理店4社のうち、次の3社のサイトにも、WEC のライフサイクル期間が掲載されています。

 岡谷エレクトロニクス(株)(「サポート・供給期間一覧」)
 http://www.oec.okaya.co.jp/emb/microsoft-embedded/support

 東京エレクトロンデバイス(株)(「ライセンス供給スケジュール」)
 http://esg.teldevice.co.jp/product/microsoft/schedule.html

 菱洋エレクトロ(株)(「マイクロソフトエンベデッドOS」)
 http://www.ryoyo.co.jp/product/software/software/p56-01/p56-01-11.html

さて。では、10年後どうなのだろうか?ということを聞かれるとしたら、簡単な質問ではありません。今から10年後、WEC 2013 のサポート期間が終わっている2025年、それに続くバージョンがどうなっているか、ということです。僕自身、これまでに幾度か自分に投げかけてきた問いでもあります。どうなるんでしょうね。

僕はマイクロソフト社の人間ではありませんから、今後の製品開発のロードマップについては分かりません。現在得られる情報をもとに、予想してみるのは楽しいことですが、事実に近いものかどうかは神のみぞ知る、です。この原稿を書いている今は、お屠蘇気分が抜け切れておらず、好き勝手な推測を書いてみるのも面白いかな、という気もします。というわけで、ちょっと書いてみます。

・気になる動き
WEC に直接関係していないものの、これから関係するのかも知れない、ちょっと気になる動きが二つ、昨年ありました。一つは、”Windows on Devices”、もう一つは、Windows 10 です。

まず、Windows on Devices ですが、Intel の Galileo ボード(CPU 400MHz、RAM 256MB)で動く Windows 8 のサブセットが提供されており、microSD カードからブートさせることができます。サブセットですから、普通の Windows 8 が動くわけではなく、PC から telnet で接続して遠隔操作したり、ビルドしたプログラムを Visual Studio から転送して実行したりといった、”Arduino” などのマイコンボードと似たような使い方しかできません。実際、Arduino 用の “Wiring” ライブラリなど、Arduino と互換のものも提供されているようです。これは、電子工作とマイコンプログラミングを組み合わせたホビーを楽しむユーザや、そこから発展させて、簡単な組み込み機器を作る人たちに向けて、Windows 8 ベースの OS と、開発環境である Visual Studio を売り込んでみようとしているプロジェクトだと思います。

もし、自分でも Galileo ボードを購入して、この Windows on Devices を使ってみたい、と思った方は、Windows Developer Program for IoT のサイトに開発者登録して、Connect サイトから、Galileo 用の Windows イメージをダウンロードしてみて下さい:
 http://dev.windows.com/en-us/featured/Windows-Developer-Program-for-IoT
 http://ms-iot.github.io/content/
 http://go.microsoft.com/fwlink/?LinkID=513083&clcid=0×409

次に、Windows 10 です。Windows 10 のテクニカルプレビューの発表までの間に、”One Windows”、あるいは “One Core” というフレーズを耳にするようになりました。Xbox One や Windows Phone 8.x での、Windows 8 ベースのカーネル採用をさらに推し進め、ゲーム機からスマートフォン、PC やタブレットから、さらにはサーバに至るまで、共通の OS カーネルのコアとアプリケーション実行環境を提供する、という取り組みのようです。Windows Phone 7.x や、その祖先にあたる Windows Mobile では Windows CE カーネルが採用されていましたが、それが “8″ で変わりました。車載用に特化した Windows Embedded Automotive 7 は WEC 7 ですが、これの “8″ 以降の話は、耳にしません。そう。ふと気づけば、Windows CE の系譜の OS は、WEC それ自体だけになっています。今後、どうなっていくんでしょう。

・今後のバージョンアップ
WEC が、今後バージョンアップされるとしても、カーネルの劇的な改良・拡張や大きな機能追加が行われる可能性は、高くないのではないか、と僕は思います。それはたとえば、x64 及び ARMv8 アーキテクチャ対応、つまり 64bit 化や、あるいは、.NET Compact Framework における 2D グラフィクス機能の強化や .NET 4 ないし .NET 5 仕様対応です。これらの対応を行うためには、おそらく、WinCE 5.0 から 6.0 への進化と同じくらいの大規模な設計変更が必要になるでしょう。しかし、Windows CE の系譜の OS が、(最新版では)WEC 以外に存在しない現在、大規模な開発リソースを要する変更を加えることに意義があるかどうか、という問いには、肯定的な回答を出すことが難しいように思うのです。ですから、WEC 2013 以降にバージョンアップが行われるとしても、どちらかといえば、メジャーバージョンアップではなく、マイナーチェンジに留まるのではないかと僕は考えています(※WEC 7 から WEC 2013 への変更も、.NET CF が 3.5 から 3.9 に変わったこと、およびコンパイラの変更; C++11 対応と snap shot boot の導入程度で、大幅な機能強化、というわけではないと言えますし)。

とはいえ、OS 本体には大幅な改良や機能追加が行われないマイナーチェンジであっても、歓迎できます。たとえば、付属の BSP の追加です。昨年11月にリリースされた WEC 2013 のアップデートにおいて、Freescale 社の i.MX6 用 BSP が追加されました。これで、WEC 2013 付属の BSP は、x86 アーキテクチャのプロセッサ用のが二つ、ARM アーキテクチャのプロセッサ用のが二つで、合計四つになりました。一つ増えたと言っても、WEC 7 の七つ(x86 用のが二つと ARM 用のが五つ)に比べると、まだ少ないですから、ARM なプロセッサ用の BSP がもう少し増えると嬉しいところです。中国 Allwinner Technology 社のプロセッサ用の BSP が追加されたりすると、選択肢が増えて面白いかも知れません。

あるいは、他の Windows ファミリとの接続性の強化というところで、Windows 10 で標準搭載されるという AllJoyn 対応が、WEC にも追加されると面白いかも知れません。Windows 10 での AllJoyn 対応については、たとえば、次のページをご覧ください:

 Microsoft contributes to open source project AllJoyn, MS Open Tech Ensures Interoperability
 http://msopentech.com/blog/2014/11/12/msopentech-alljoyn/

現在の WEC では、UPnP を用いた相互接続が可能ですが(※WEC 7 では UPnP AV/DLNA もサポート)、より簡易な動作モデルという点で、僕は AllJoyn に興味を感じています。あるいは、AMQP や MQTT プロトコルを WEC で標準サポートしてくれると、Microsoft Azure の Service Bus との接続が簡単になるでしょう。そのような機能追加によって、いわゆる IoT の分野でも WEC を新規の製品開発に採用しやすくなると、嬉しいところです。

・Windows 10 への不安と期待
さて、10年後、WEC はどうなっているのでしょう?その時には、WEC 2013 のサポート期間は終わっています。WEC を採用した製品開発に携わっている方は、その時までには、WEC 2013 に続く、新規の製品開発に使える OS を必要としていることでしょう。Windows 10 で語られる “One Core” というフレーズを思うと、3年後や4年後に、WEC 2013 のカーネルソースを引き継いでメジャーバージョンアップしたものが出てくるということは、可能性が低いように思います。しかし、Windows Embedded ファミリーにおいて、現在 WEC が占めている位置は、今後も必要とされるのではないでしょうか。より小さいフットプリントで、ハードウェア構成に合わせたカスタマイズも可能な OS。そのニーズがあるのならば、3年後や4年後、WEC 2013 のメインストリームサポート期間が終わる頃までに、そこに対する解が Microsoft 社から提示されることを期待します。”Windows on Devices” は、もしかすると、そのヒントなのかも知れません。

10年後も、新規の製品開発における採用候補として、お客様に Windows Embedded 製品を提示できることを願っています。あ、もちろん、3年先、4年先までは、WEC 2013 を安心して使えるだろうというのは、冒頭で述べた通りですよ。

Add comment 2015/02/05 koga

OS の起動時間短縮(1/2)~RAM レジストリの初期化時間を短縮する

■はじめに
今回と次回は、WEC の起動時間を短縮する方策を、二つ紹介します。WEC 2013 では、Snapshot Boot が導入され、適切に実装・チューニングすれば、OS の起動時間を数秒以内に抑えることも可能になりました。しかし、WEC 2013 よりも前の版では、Snapshot Boot を行うことができません。そこで、WEC 7 でも対応可能な起動時間短縮の方策を紹介してみることにしました。なお、Snapshot Boot については、MSDN の次のページをご覧ください。

 Snapshot Boot Development (Compact 2013)
 http://msdn.microsoft.com/en-us/library/dn169259.aspx

日本国内の Windows Embedded MVP 有志が主催して不定期に開催している、「Windows Embedded Community Day」の第1回で僕が担当したプレゼンでも、Snapshot Boot について簡単に紹介しています:

 組み込みでもマルチコア。WEC2013 での対応
 http://www.slideshare.net/ShinyaKoga/2013-0719wemvp-kansai

 Windows Embedded Community Day 第 1 回 – 真夏の組み込み Windows 技術セミナー2013
 http://msdn.microsoft.com/ja-jp/dn521045

■RAM レジストリの初期化
2011/08/02 に書いたエントリ(「レジストリの永続化~RAM-Based の場合」)では、レジストリの永続化対応を行う場合、Hive-Based のレジストリよりも、RAM-Based のレジストリの方が、より堅牢だと述べました。しかし、RAM-Based のレジストリには、起動時間が長くなるという短所があります。これは、レジストリの初期化に要する時間が、RAM-Based のレジストリの方が長いことが要因のようです。

Hive-Based のレジストリは、レジストリの Hive ファイルを filesys.dll が memory mapped file としてオープンして内容にアクセスします。つまり、Hive ファイルの内容は、filesys.dll が RAM 上に構築するレジストリ(一種のオンメモリデータベース)と同じではないかと思われます。従って、OS の起動時にレジストリを初期化する動作というのは、OS イメージに収録されている Hive ファイルや、内容更新されて永続化記憶域(ストレージ)に配置された Hive ファイルを、単に memory mapped file としてオープンするだけなのでしょう。

一方、RAM-Based のレジストリは、filesys.dll が RAM 上に構築するレジストリとは内容(フォーマット)が異なるようです。このため、OS の起動時にレジストリを初期化する際、ストレージからレジストリデータを読み出し、その内容を解析して RAM 上にレジストリを構築する、という処理が必要になります。このため、レジストリの初期化に要する時間が長くなるのです。この時間は、レジストリ全体のサイズに依存しますから、一般的には、OS イメージに組み込む機能が多いほど、つまり、OS Design のカタログ項目の選択が多いほど長くなります。10秒以上に達することがあることを確認していますし、永続化した場合は、レジストリの初期化時間の合計で20秒を超える場合もあります。

■永続化された場合の RAM レジストリの初期化
上で、「永続化した場合は」と書きました。そうです。RAM レジストリを永続化した場合、永続化しない場合よりも、レジストリの初期化に要する時間が長くなるのです。これについては、2011/08/02 に書いたエントリでも紹介した、WinCE 6.0 のリファレンスにある、次のページの説明にヒントがあります。

 File System Initialization of RAM-based Registry (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-US/library/ee490364(v=winembedded.60).aspx

このページには、RAM-based レジストリの初期化の流れが説明されているのですが、1から8まであるステップのうち、5と6は、次の通りです:

  1. Filesys.dll initializes the registry by checking for a registry in RAM.
    If Filesys.dll does not find a registry in RAM, it restores the registry from ROM. Filesys.dll loads \Windows\Default.fdf, which was created using Makeimg.exe.
  2. Filesys.dll checks for the ReadRegistryFromOEM OEM adaptation layer (OAL) function. If Filesys.dll does not find ReadRegistryFromOEM, it keeps the registry restored from ROM.

つまり、OS イメージ(ROM イメージ)に収録されている Default.fdf というファイル内容をロードして初期化した後、OAL の ReadRegistryFromOEM() を(それが実装されていれば)呼び出し、ストレージに保存されたレジストリデータをロードして初期化し直す、というわけです。つまり、RAM-Based のレジストリを永続化した場合、レジストリの初期化処理が実質二回行われるのです。

■永続化された RAM レジストリの初期化時間を短縮
RAM-Based のレジストリを永続化した場合に、永続化しない場合よりもレジストリの初期化に要する時間が長くなるのは、レジストリの初期化処理が二回行われるからなのです。これを、一回にすることができれば、そのぶん起動時間を短縮できることになります。何かよい方法はないでしょうか?

あります。それは、2012/01/13 に書いたエントリ(「OS の部分アップデート方策~その2(2/2)」)で述べた、ROM イメージを複数に分割することです。このエントリで、ROM イメージを複数の region に分割した場合、同じ名前(パス)のファイルに対して、region 間で shadowing が作用すると書きました(※「Region ごとの部分アップデート」という項をご覧ください)。この shadowing を利用するのです。

方策は、次の通りです:

1.) OSDesign.reg において、最小限の内容(たとえば、[HKLM\init] キーだけ)が有効になるように設定して OS イメージをビルドして、生成された Default.fdf ファイルを採取する。

2.) (1) で採取した Default.fdf ファイルのみを収録する ROM イメージの region を作成する。
 これは、config.bib によるメモリマップの設定と、ブートローダによる複数 region のロード、および、OAL による複数 region の連結処理(OEMRomChain のリスト構築処理)が必要です。

3.) 通常のビルド時は、(1) の設定を解除して OS イメージをビルドする。
 この結果、本来のレジストリ設定内容は、OS 本体を収録した region(NK region)に Default.fdf という内容で収録され、(1) で作成した(最少サイズの)Default.fdf は、それ専用の region に収録されます。

4.) OAL における OEMRomChain のリスト構築処理を実行する前に、ストレージ上にレジストリデータが存在するかどうかをチェックし、存在しない場合は、OEMRomChain のリスト構築処理において、Default.fdf のみを収録した region を無視する。ストレージ上にレジストリデータが存在する場合は、Default.fdf のみを収録した region を、OEMRomChain のリストにおいて、OS 本体を収録した region よりも前に配置する。

この方策により、ストレージ上にレジストリデータが存在する場合は、(1) で作成した最小限の Default.fdf ファイルの内容で一回目のレジストリ初期化が実行され、その時間を最小限(相対的に 0)にできます。その後、ストレージに保存されたレジストリデータがロードされ、レジストリが初期化される、というわけです。

ストレージ上にレジストリデータが存在しない場合は、(1) で作成した最小限の Default.fdf ファイルを収録した region は OEMRomChain のリストには挿入されませんから、shadowing が行われず、OS 本体を収録した region(NK region)に収録された、通常の Default.fdf がロードされてレジストリが初期化されます。従って、レジストリを一度もストレージに保存していない場合や、ストレージに保存したレジストリデータを無効化した場合にも、問題なく動作します。上記方策を適用しない場合の動作と比べて、起動時間が短縮される以外は、見た目の動作は変わりません。

Add comment 2014/09/23 koga

WEC のネットワークフィルタドライバ(NDIS フィルタ)

今回は、ネットワークドライバに対するフィルタドライバについて述べます。既存のドライバに対してフィルタドライバを attach することにより、ドライバの動作診断に役立てることができます。あるいは、どのように呼び出されるのかという視点からドライバの振る舞いを解析し、既存のドライバを改修する際に役立てたり、また、サンプルドライバの動作を解析して新規にドライバを実装する際の参考にする、といった利用もできるでしょう。

■はじめに~WEC 7 での NDIS 6.0 の導入
NDIS フィルタドライバは、WEC/WinCE で NDIS 6.0 に対応した版、つまり WEC 7 で導入されました。リファレンスにもサンプルコードにも、NDIS フィルタドライバが登場するのは WEC 7 からで、WinCE 6.0 にはありません。WEC 7 と WEC 2013 のリファレンスの、NDIS フィルタドライバの説明は、それぞれ次のページです:

 NDIS Functions for Filter Drivers (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg158504(v=winembedded.70).aspx

 NDIS Filter Driver Reference (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg159366.aspx

NDIS 5.x だった WinCE 6.0 では、NDIS フィルタドライバに相当する機能は、NDIS 中間ドライバ(NDIS intermediate driver)として提供されていました。興味のある方は、WinCE 6.0 のリファレンスの次のページをご覧ください。

 Intermediate Drivers (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee483408(v=winembedded.60).aspx

NDIS の 5.x と 6.0 の違いについては、次のページが参考になるでしょう。

 Port Miniport Drivers from NDIS 5.x to 6.0 (Compact 7)
 http://msdn.microsoft.com/en-us/library/jj838857(v=winembedded.70).aspx

 Port Miniport Drivers from NDIS 5.x to 6.0 (Compact 2013)
 http://msdn.microsoft.com/en-us/library/jj838857.aspx

 Introduction to NDIS 6.0
 http://msdn.microsoft.com/en-us/library/windows/hardware/ff556026%28v=vs.85%29.aspx

■NDIS フィルタドライバのサンプルコード
NDIS フィルタドライバのサンプルコードは、WEC 7 も WEC 2013 も、次のディレクトリに収録されています:

 %_WINCEROOT%/public/common/oak/drivers/netsamp/lwfilter/

このサンプルコード(lwfilter)は、WEC 2013 のサンプルコードページにも WEC 7 のサンプルコードページにも、説明が載っていません。そのため、見落としていた方もいらっしゃるかも知れません。もし興味がわいたら、この機会に、ご覧になってみて下さい。lwfilter は、Windows Vista/7 用の NDIS 6.0 フィルタドライバのサンプルコードがベースになっているようで、同じ名前の Win7 用のサンプルコードが、次のページで提供されています:

 NDIS 6.0 Filter Driver
 https://code.msdn.microsoft.com/windowshardware/NDISLWFSYS-Sample-NDIS-60-42b76875

上述した、NDIS 5.x の NDIS 中間ドライバのサンプルコードは、WEC 7 以降でも提供されており、次のディレクトリに収録されています:

 %_WINCEROOT%/public/common/oak/drivers/netsamp/passthru/

NDIS 中間ドライバが WEC 7 以降も使えるのは、NDIS 5.x のサポートが含まれているからです。NDIS 5.x 関連のドライバインタフェースは、リファレンスの次のページで説明されています:

 NDIS 5.x Legacy Reference (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg158312(v=winembedded.70).aspx

 NDIS 5.x Legacy Reference (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg158312.aspx

ちなみに、WEC 7 と WEC 2013 のネットワーク関連のサンプルコードは、次のページで紹介されています。興味のある方は、こちらもご覧になってみて下さい。

 Networking Code Samples (Compact 7)
 http://msdn.microsoft.com/en-us/library/hh802406(v=winembedded.70).aspx

 Networking Code Samples (Compact 2013)
 http://msdn.microsoft.com/en-us/library/hh802406.aspx

■フィルタドライバの効用
最後に、フィルタドライバの効用について述べます。冒頭で、既存のドライバの動作診断や、振る舞いの解析に役立てることができると書きましたが、その他に、既存のドライバに対する機能追加、という効用があります。NDIS は、フィルタドライバが attach されたドライバを直接呼び出さず、フィルタドライバを経由して間接的に呼び出します。従って、フィルタドライバは、既存のドライバに対する NDIS からの呼び出しをフックして追加の処理を実行できるのです。つまり、既存のドライバの実装を変更せずに、フィルタドライバによって機能追加を行うことができる、というわけです。

フィルタドライバにより機能追加を行うという方策は、機能追加したい対象のドライバが複数存在する場合に、より効果的です。たとえば、NDIS フィルタドライバによってドライバに機能拡張したいデバイスが、複数のネットワークインタフェースを持つ場合のことを考えてみて下さい。有線 LAN のインタフェースと無線 LAN のインタフェースを両方持つデバイスは、近年珍しくありません。それら複数のネットワークインタフェースのドライバに対して、同じ機能追加を行う場合、個々のドライバの実装を変更して機能追加するよりも、NDIS フィルタドライバを使って機能追加する方が、実装変更箇所が一つで済みますから、より低コストで実現できるでしょう。既存のドライバの実装を変更しなくて済むというのは、既存のドライバのソースコードを入手できず、自分で改変できない場合にも役立つ点です。

NDIS は、ネットワークインタフェースのハードウェアを抽象化したレイヤですから、そのレイヤのインタフェースに対するフィルタドライバは、異なるネットワークインタフェースのドライバに対して共通に適用できます。機能追加の例として、「ネットワーク通信が行われている間は、WEC の Power Manager の activity timer のイベントを発火させ、自動サスペンドしないようにする」という機能があります。この機能は、個々のネットワークインタフェースのドライバの実装を改変して実装するよりは、NDIS フィルタドライバで実装し、フィルタドライバを各ネットワークインタフェースのドライバに attach する方がシンプルで良いでしょう。これは、あくまでも一つの例に過ぎませんが、フィルタドライバの役立て方として参考になれば幸いです。

Add comment 2014/09/07 koga

IE 7 での日本語表示と AC3 フォント圧縮

2012/07/13 に書いたエントリ(「WEC 7 の Meiryo フォント」)で、「Meiryo フォントを OS イメージに組み込む場合は、カタログ項目の “Monotype Imaging AC3 Font Compression” を選択しては、いけません。」と書きました。この Monotype Imaging AC3 Font Compression は、Meiryo フォントを組み込まない場合にも注意が必要なケースのあることが分かりました。それは、IE 7 を組み込む場合です。

Monotype Imaging AC3 Font Compression を組み込んだ OS イメージで IE 7 を動かすと、日本語ページを表示する際に、IE 7 内部で例外送出が繰り返し発生し、表示までに長い時間を要したり、あるいは、日本語テキストが表示されない場合もある、という問題があるのです。この問題は、少なくとも ARMv5 と ARMv7 のプロセッサでは発生することを確認しています。僕自身は確認していませんが、x86 では発生しないようです。

IE 7 で日本語ページを表示する際、Monotype Imaging AC3 Font Compression を組み込んだ OS イメージでは、次のようなログが繰り返し出力されます:


  48706 PID:5d80026 TID:5de002e Exception 'Raised Exception' (0xe06d7363): Thread-Id=05de002e(pth=c04a20c0), Proc-Id=05d80026(pprc=c049c8b8) 'iesample.exe', VM-active=05d80026(pprc=c049c8b8) 'iesample.exe'
  48706 PID:5d80026 TID:5de002e PC=400512c8(coredll.dll+0x000412c8) RA=800956f0(kernel.dll+0x0000e6f0) SP=00148ad0, BVA=00148b0c

IE 7 で日本語ページを表示する際に、繰り返し例外送出が起きて表示に時間を要する、という問題は、以前にも経験していたのですが、その時は、原因が分からず、対策をとっていませんでした。先日、この問題に取り組む必要が生じ、あらためて追ってみたところ、ようやく、AC3 フォント圧縮(Monotype Imaging AC3 Font Compression)との因果関係が分かったのです。この問題に以前遭遇した際に、カーネルデバッガで追ってみた時の記憶では、上記の例外送出は、IE 7 内部で、フォントの表示幅を算出する処理で起きているようです。

以前に調べた時の記憶が正しければ、フォントの表示幅を算出する処理で例外送出が起きていますので、フォントデータへのアクセスで例外送出が起きている可能性があります。このことと、ARM プロセッサでは症状が発生し、x86 プロセッサでは症状が発生しないようだ、ということを考え合わせると、フォントデータへのアクセスで word alignment のとれていないデータ読み出しを行っている箇所があり、そこで例外送出が起きる、ということなのかも知れません。全くの憶測にすぎませんが、AC3 フォント圧縮されたフォントファイル(非圧縮の .ttc ではなく、圧縮版の .ac3)内のデータをアクセスする際に、圧縮によって word alignment のとれていないフィールドがあり、そこに対して整数値の読み出しを行おうとして例外送出が起きている、ということなのかも知れません。

なお、このような症状は、WinCE 6.0 では発生していませんでした。従って、WinCE 6.0 から WEC 7 への変更点の中に、要因があるのではないかと思われます。WinCE 6.0 から WEC 7 への変更点の中に、ARM コンパイラの強化があります。WinCE 6.0 までは、ARMv4 アーキテクチャの命令しか出力できなかったのが、WEC 7 では、ARMv5, ARMv6, ARMv7 をサポートしています(※最新メジャー版の WEC 2013 では、ARM コンパイラが再び変更され、ARMv7 のみのサポートとなったのは、皆さんご存知の通りです)。WEC 7 での ARM コンパイラの強化には、この他に、memcpy() や memset() が Intrinsic Function となった、というものがあります。これは、ARM コンパイラだけではなく、x86, ARM, MIPS 共通です。

memcpy() や memset() が Intrisic Function となった結果、これらの関数に渡す実引数の型次第では、byte 単位ではなく、word 単位でのアクセスが行われるようです。これは、一般的には高速化に役立ちますが、[unsigned ]short や [unsigned ]long、[unsigned ]int のポインタが実引数として渡され、かつ、実際には、そのポインタ値が、word alignment がとれていない場合(つまり、short のポインタ値が 2Byte 単位のアドレス境界に揃っていなかったり、long のポインタ値が 4Byte 単位のアドレス境界に揃っていない場合)、ARM プロセッサでは、例外送出が起きてしまいます。x86 では、例外送出は起きません。このような状況は、たとえば、可変長のヘッダを持つ通信パケットがあり、そのペイロードを構造体にキャストしてアクセスする場合に起こり得ます。もしかすると、IE 7 が AC3 フォント圧縮されたフォントファイル内のデータをアクセスする際に、そのようなことが起きているの *かも* 知れません。

WinCE 6.0 から WEC 7 への変更点には、コンパイラの強化の他に、IE の変更もあり、レンダリングエンジンの強化も行われていますから、コンパイラの違いが要因ではなく、レンダリングエンジンの変更が要因である可能性もあります。IE 7 のレンダリングエンジンのソースコードは開示されていませんので、真相は、Microsoft で調べてもらわなければ分かりません。

ともあれ、少なくとも現時点では、Meiryo フォントに加え、IE 7 とも、Monotype Imaging AC3 Font Compression を共存させてはいけない、ということが言えます。IE 7 と日本語フォントを OS イメージに組み込まれる方は、ご留意下さい。

■おまけ
WEC 7 のコンパイラの Intrinsic Function のうち、CPU アーキテクチャ共通のものについては、リファレンスの次のセクションに記載されています:

 Compiler Intrinsic Functions (Compact 7)
 http://msdn.microsoft.com/en-us/library/ee480143(v=winembedded.70).aspx

memcpy() や memset() は、次のページに載っています:

 Intrinsic Forms of CRT Functions (Compact 7)
 http://msdn.microsoft.com/en-us/library/ee479418(v=winembedded.70).aspx

Add comment 2014/08/25 koga

OS Design から生成した SDK インストーラの問題(日本語ロケール)

■はじめに
Windows Embedded Compact (WEC) では、OS のコンフィグレーション、つまり OS Design に対応した SDK を作成できることは、皆さんご存知の通りです。もし、OS Design に対応した SDK を作成する方法をご存知ないのであれば、開発者ガイドの次のページが参考になります:

 Create an SDK (Compact 7)
 http://msdn.microsoft.com/en-us/library/jj200346(v=winembedded.70).aspx

 Build an SDK (Compact 2013)
 http://msdn.microsoft.com/en-us/library/dn197930.aspx

OS Design に対応した SDK を使ってアプリケーションを作成することにより、アプリケーションと OS のコンフィグレーションの食い違いが起きるのを防ぐことができる、というわけです。たとえば、DirectShow を組み込んでいない OS で動かすアプリケーションを作成する際に、誤って DirectShow API を呼び出すコードを書いてしまうという手違いが、なくなります。そのようなコードを書いても、OS に組み込まれていない API を宣言したヘッダファイルやライブラリは、SDK には組み込まれていないので、アプリケーションをビルドする際にビルドエラーとなるからです。

ところで、WEC 7 の開発環境では、この SDK に少しだけ問題があります。作成した SDK のインストーラで、画面表示が不適切になってしまう箇所があるのです。下の図を見て下さい。

  OEM EULA Dialog

この図は、SDK のインストーラの、使用許諾契約書 (EULA; End-User License Agreement) の表示・同意確認画面ですが、「同意しない」 (Decline) のラジオボタンが途中で切れてしまい、見えていません(※「同意」までしか見えていません)。これは、ユーザに不親切で、適切ではありません。SDK をビルドする際、日本語ロケールに設定せず、英語ロケールにした場合には、”Accept” と “Decline” という二つのラジオボタンが正しく表示されるのですが、日本語ロケールだと、上の図のように、表示が途中で切れてしまうのです。

これは、どちらかといえば、WEC 7 の開発ツールの不具合ですが、少なくとも2014年8月までにリリースされたアップデータでは、修正されていません。しかし、ご安心下さい。この不具合は、自分で修正することができます。

■SDK インストーラのテンプレート
上で紹介した開発者ガイドのページで説明されているように、SDK を作成してビルドすると、SDK のインストーラ(.msi ファイル)が生成されるのですが、この .msi ファイルは、ビルドのたびに一から生成されるのではなく、テンプレートを元にして生成されます。このテンプレートは、WEC 7/Visual Studio 2008 の場合ですと、次のディレクトリに配置されています:

 C:/Program Files (x86)/Microsoft Platform Builder/7.00/cepb/ideVS/SdkTools/RollerFiles

注:上のディレクトリパスは、64bit OS の場合です。32bit OS では、’Program Files’ の後の ‘(x86)’ は、ありません。

上のディレクトリには、.msi ファイルのテンプレートの他、使用許諾契約書のテンプレートや、SDK のビルド時に実行される VB スクリプト(.vbs ファイル)も収録されています。興味のある方は、それらの内容をご覧になってみて下さい。

さて、今回重要なのは、.msi ファイルのテンプレートです。日本語ロケール用のテンプレートファイルは、拡張子の前に ‘_1041′ が付いており、
 template_1041.msi
というのが、日本語ロケール用のものです。このテンプレートには、SDK のダイアログのリソースも入っており、それを修正することで、上述した画面表示の問題を解消できます。では、.msi ファイルの内容を、どうやって修正すればよいのでしょうか?

ご安心下さい。Visual Studio には、.msi ファイルの内容を編集できる orca というツールが付属しています。このツールを使えば、.msi ファイルを開いて、インストーラのダイアログのリソースを編集できるのです。

■インストーラのテンプレートを修正する
orca を使うには、まずインストールしなければいけません。Visual Studio や WEC の開発環境(Platform Builder)をインストールしただけでは、orca はインストールされないのです。orca のインストーラが付属しているので、それを使ってインストールして下さい。orca のインストーラは、C:/Program Files (x86)/ ディレクトリ(※32bit OS の場合は、C:/Program Files/ ディレクトリ)で、’orca’ を含む名前のファイルを検索すれば見つけることができます。VS 2008 + WEC 7 をインストールしていれば、
 C:/Program Files (x86)/Windows Kits/8.0/bin/x86/
というディレクトリの中に、Orca-x86_en-us.msi というファイルが見つかるでしょう。もし、この他に、
 C:/Program Files (x86)/Orca/
というディレクトリと、その中に入っている Orca.exe というファイルが見つかる場合には、既に orca がインストールされています。orca がインストールされていない場合は、見つかったインストーラを実行して、インストールして下さい。

orca をインストールして使えるようになったら、いよいよ、日本語ロケールの SDK のインストーラのテンプレート(.msi ファイル)を開いて修正です。おっと、その前に、修正する .msi ファイルのバックアップをとっておいて下さい。万が一、修正作業で手違いが起きても、元の状態に戻せるようにするためです。

orca の使い方については、次のページが参考になるでしょう:

 Orca データベース エディタを使用して Windows インストーラ ファイルを編集する方法
 http://support.microsoft.com/kb/255905/ja

問題の、使用許諾契約書の表示・同意確認画面のラジオボタンの表示設定は、’Control’ テーブルに入っています。下に、’Control’ テーブルの該当行を編集しようとしている様子を写した画面キャプチャを示します。

 orca edit window

上の図の、選択されて青くハイライトされているのが、Control テーブルの該当行です。この行は、’OEM_EULA_Dlg’ というダイアログの、Control カラムの値が ‘Buttons’、Type カラムの値が ‘RadioButtonGroup’ という行です。この行の ‘Width’ というカラムに、二つのラジオボタンで構成されたラジオボタングループの表示幅が格納されています。日本語ロケールの SDK インストーラで表示が切れてしまうのは、この幅が、英語表示だと足りるものの、日本語表示だと足りない値だからなのです。従って、この幅の値を増やしてやれば、表示が切れなくなります。上の画面キャプチャで、黄色くハイライトされているカラムは、値が 135 ですが、これを 170 に変更すると、「同意しない」というキャプションが、正しく表示されるようになります。

.msi ファイルの編集中に、編集結果を確認したい場合には、orca の ‘Tools’ メニューから ‘Dialog Preview…’ を選択すれば、ダイアログ表示をプレビューできます。試してみて下さい。

Add comment 2014/08/22 koga

WEC 7 の WMP サンプルアプリケーション

■Microsoft Silverlight for Windows Embedded の修正にまつわる問題と対処
WEC 7 で、付属のメディアプレイヤーアプリケーション(Music Player, Photo Viewer, Video Player)を組み込んだ OS イメージをビルドしていらっしゃる方は、月例アップデータを適用する場合、注意する必要があります。今年リリースされた月例アップデータの中に、適用すると、これらメディアプレイヤーアプリケーションが動作しなくなる症状を引き起こすものがあるからです。

具体的に言うと、Microsoft Silverlight for Windows Embedded に対する修正が多く投入された Monthly Update May 2014 において、その問題を確認しています。これは、不具合というよりは、月例アップデータのパッケージング上の問題だと思います。Monthly Update May 2014 には、以下の8件の修正が投入されているのですが、この中に、問題の要因があるようです:

 Windows Embedded Compact 7 Monthly Update May 2014
 http://www.microsoft.com/en-us/download/details.aspx?id=43368

  FIX: Cannot display all UI elements correctly in a SWE application for Windows Embedded Compact 7
  
FIX: Storyboard doesn’t run if it references a custom DependencyProperty in Windows Embedded Compact 7
  FIX: Poor performance when you use XAMLRuntime OpenGL in Windows Embedded Compact 7
  FIX: Cannot receive the GID_END gesture message in an SWE application on a Windows Embedded Compact 7-based device
  FIX: An update is available about adding the GetTemplateChild method to the SWE IXRControl class in Windows Embedded Compact 7
  FIX: Setting DataContext to null resets two-way binding in the SWE application in Windows Embedded Compact 7
  FIX: ListBox selection issue when you use touch gestures in an SWE application on a Windows Embedded Compact 7-based device
  FIX: A RadioButton in a ListBox control stays in checked state when removed and re-added in Windows Embedded Compact 7

このアップデータを適用する前に、メディアプレイヤーアプリケーションを組み込んだ OS イメージをビルドしたことがある場合、このアップデータを適用してから OS イメージをビルドすると、その OS 上でメディアプレイヤーアプリケーションを起動しようとすると、起動時にアプリケーション内部で例外送出が起き、起動しないのです。この症状は、OS イメージをビルドする際、Platform Builder のソリューションエクスプローラで以下のフォルダ配下をリビルドすることで解決できます:

 C:/WINCE700/
  public/
   mediaapps/
    oak/
     samples/
★     cemp

‘cemp’ ディレクトリ配下に、メディアプレイヤーアプリケーションのソースコードがあります。アップデータを適用後、このディレクトリ配下をリビルドしてから OS イメージをビルドする(※OSDesign をリビルドする)ことにより、メディアアプリケーションが起動しないという問題が解消します。

この問題の原因は不明ですが、アップデータに含まれる Silverlight for Windows Embedded (SWE) の修正において、バイナリ互換性を壊してしまったものがあり、そのため、アプリケーションをリビルドしなければならない、ということのようです。

■問題の発生要因
さて、このような問題は、通常起きませんし、起きてはならないことだと思います。WEC 7 に付属しているコンポーネントが、アップデータの適用によって動作しなくなるということは、通常は考えられない事態です。実際、僕は初めて体験しました。特殊な状況だと思います。

この問題ですが、冒頭の方で、「不具合というよりは、月例アップデータのパッケージング上の問題だと思います。」と述べました。どういうことかといえば、ビルド済みの中間バイナリファイルが要因なのです。メディアプレイヤーアプリケーションと同様、WEC 7 に付属するアプリケーションである標準シェル(explorer.exe)や IE 7 は、メディアプレイヤーと同様、アプリケーション部分はソースファイルが付属するのですが、ビルド済みの中間バイナリファイル(.lib)も付属しており、手作業で明示的にソースファイルをビルドしない限りは、ビルド済みの中間バイナリファイルをリンクしてアプリケーションの .exe が生成されます。仮に、それらのアプリケーションとのバイナリ互換性を壊す修正が OS 本体に加えられた場合は、アプリケーションのビルド済み中間バイナリファイルも、修正と一緒に配布されるでしょう。従って、メディアプレイヤーアプリケーションで起きたような問題は、発生しないのです。

一方、メディアプレイヤーアプリケーションは、ソースファイルしか付属していないため、バイナリ互換性を壊す修正が OS 本体に加えられても、それに対応したビルド済み中間バイナリファイルはアップデータに含まれません。OS イメージをビルドする際、メディアプレイヤーアプリケーションのソースが未だビルドされていない場合は、ビルドされて、ビルド済み中間バイナリファイルが、ソースファイルのディレクトリ内に生成されます。OS イメージをビルドする際に、メディアプレイヤーアプリケーションのソースファイルから生成されたビルド済み中間バイナリファイルは、ソースファイルが更新されるか、または、手作業で明示的にソースファイルをリビルドしなければ更新されません。これが、上述した問題の原因、というわけなのです。

今回紹介した、Silverlight for Windows Embedded アプリケーションが月例アップデータ適用後に動作しなくなる、という問題は、僕は初めて体験しました。特殊なケースだとは思いますが、月例アップデータ、および、メディアプレイヤーアプリケーション(WMP サンプルアプリケーション)の WEC 7 における提供のされ方に要因があり、今後も起こる可能性がある問題だと思います。月例アップデータを適用する際には、適用前に、どのような修正が加えられたのかを確認し、Silverlight for Windows Embedded に関する修正の場合は、注意される方が良いと思います。

Add comment 2014/08/05 koga

ネイティブコードから C# へのコールバックについて補足

今回は、2012/05/14 のエントリ(「ネイティブコードから C# のメソッドをコールバック」)についての補足を書きます。補足するのは、次の二点です:

・コールバック関数のコーリングコンベンション
・ネイティブコードへのデリゲートオブジェクト渡し

それぞれについて、以下に記します。

■コールバック関数のコーリングコンベンション
2012/05/14 のエントリでは、マネージドコードからアンマネージコードに渡すコールバック関数の関数ポインタを得る API として、GetFunctionPointerForDelegate() を紹介しました。この API で取り出す関数ポインタに対しては、アンマネージコード側からみた場合、留意すべき点があります。リファレンスページの “Remarks” の項にある注意書きです。

 Marshal.GetFunctionPointerForDelegate Method
 http://msdn.microsoft.com/en-us/library/at4fb09f(v=vs.90).aspx

上のページの “Remarks” の項には、次の注意書きがあります:

The delegate d is converted to a function pointer that can be passed to unmanaged code using the __stdcall calling convention.

つまり、アンマネージコード(ネイティブコード)から見た場合、マネージコードから渡される関数ポインタのコーリングコンベンション(呼出規約)は __stdcall になるので、アンマネージコード側では、受け取る関数ポインタの型を、__stdcall 付きで宣言しておく必要がある、というわけです。__stdcall をはじめ、Visual C++ の C/C++ コンパイラにおけるコーリングコンベンションの説明は、リファレンスの次のページをご覧ください。

 Argument Passing and Naming Conventions
 http://msdn.microsoft.com/ja-jp/library/984×0h58(v=vs.90).aspx

デフォルトのコーリングコンベンションは、スタックに積まれた手続きの引数を、手続きを呼び出した側がスタックから取り除く __cdecl であり、呼び出された手続きが取り除く __stdcall とは異なりますので、その点に注意して下さい。

さて、アンマネージコードの既存のライブラリがあり、そのライブラリに対してコールバック関数を設定するマネージドコードを実装しようとした際、既存のライブラリが受け取るコールバック関数が __cdecl であった場合は、どうすればよいのでしょうか?そのままでは、コーリングコンベンションが合っていないため、実行時エラーを引き起こす可能性があります。

ご安心下さい。その問題は、マネージドコードからの呼び出し方を変えることで解決できます。これは、冒頭に示した二点目の補足に関係します。

注意:実は、WinCE/WEC では、後述するようにコーリングコンベンションの問題はありません。この問題を考慮する必要があるのは、フル版の .NET Framework の場合だけなのです。

■ネイティブコードへのデリゲートオブジェクト渡し
2012/05/15 のエントリでは、マネージドコードからアンマネージコードへ渡す関数ポインタを得るために、delegate 実体に GetFunctionPointerForDelegate() を適用する必要があると述べました。しかし、この呼び出しは必須ではなく、delegate 実体をそのまま渡すことができます。MSDN にある .NET Framework プログラミングガイドにある、「アンマネージコードとの相互運用」にあるサンプルコードでも、delegate 実体をそのまま渡しています。2012/05/15 のエントリを書いた時点では、そのことを理解していませんでした。ごめんなさい。

MSDN にあるサンプルコードは、次のページをご覧ください:

 Callback のサンプル(マネージドコード)
 http://msdn.microsoft.com/ja-jp/library/5zwkzwf4(v=vs.90)

 PinvokeLib.dll(アンマネージコード)
 http://msdn.microsoft.com/ja-jp/library/as6wyhwt(v=vs.90).aspx

さて、コーリングコンベンションに話を戻します。.NET Framework には、マネージドコードとアンマネージコードの間での、(アンマネージコードにおける)コーリングコンベンションを表現する UnmanagedFunctionPointerAttribute というクラスがあります。このクラスを使って、delegate のコーリングコンベンションを変更できます。具体的には、delegate を宣言する際、次のように属性記述すれば、delegate のコーリングコンベンションを __cdecl に設定できるようです:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool FPtr( int value );


しかし、この UnmanagedFunctionPointerAttribute は、WinCE/WEC 環境、つまり .NET Compact Framework ではサポートされていません。さらに、UnmanagedFunctionPointerAttribute クラスのコンストラクタ引数である、enumeration 型の CallingConvention は、.NET Compact Framework の場合には、メンバとして Winapi しか持たず、これは __cdecl に対応するとリファレンスに書かれているのです。

 CallingConvention Enumeration
 http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.90).aspx

このリファレンスには、デフォルトのコーリングコンベンションを示す Winapi は、「Windows 環境」、つまりフル版の .NET Framework では __stdcall に対応し、WinCE/WEC 環境(.NET Compact Framework)では __cdecl に該当するという旨の説明があります。これは一体、どいうことなのでしょう?Win32 API は、WINAPI 付きで宣言されており、__stdcall のコーリングコンベンションを設定されています。一方、UnmanagedFunctionPointerAttribute が __cdecl にしか対応しないのでは、両者が整合しないということです。

実は、WinCE/WEC のコーリングコンベンションは __cdecl だけなのです。このことは、次のヘッダファイルを見れば分かります:
 %_WINCEROOT%/public/common/sdk/inc/windef.h

windef.h には、次の行があり、’__stdcall’ が __cdecl の別名として定義されているのです。

#ifdef UNDER_CE
#define __stdcall __cdecl // Note this doesn't match the desktop definition
#define _stdcall __cdecl // Note this doesn't match the desktop definition
#endif


従って、.NET Compact Framework の場合には、コーリングコンベンションのことを意識する必要は、ありません。とはいえ、.NET Compact Framework 版と .NET Framework 版とで、アンマネージコードを含めてソースコードを共有できるように、可搬なコードを書く場合には、.NET Framework 版でも問題なく動作するよう、コーリングコンベンションを意識する必要があります。

■まとめ
今回は、2012/05/14 のエントリに対して二つの補足を書きました。二つのうち、コーリングコンベンションについては、WinCE/WEC では考慮せずともよいのですが、Win7 や Win8 でも動かせる可搬なコードを書く場合には、考慮する必要があります。

もう一つの、デリゲート渡しについては、わざわざ GetFunctionPointerForDelegate() を呼ぶ必要はない、というのが結論です。これは、2012/05/14 のエントリで、どちらかといえば、不適切な内容だったと思います。同様の見解が、.NET Framework に関する次の記事にも書かれています:

 C# からコールバック関数を使う C の関数を呼ぶ
 http://sgry.jp/pgarticles/cs_pinvoke_callback.html

今回の補足ですが、2012/05/15 のエントリに頂いていたコメントを拝見して書きました。コメントを頂いたのは、2013/05/14 ですから、半年以上の時間が経ってしまったのですが、参考になりました。有難うございます。

Add comment 2014/02/16 koga

WinCE/WEC の Secure Loader

前回(2013/02/10)から、一年ほど間が空いてしまいました。その間に、Windows Embedded Comact の最新版である Windows Embedded Comapct 2013 (WEC 2013) の一般提供も始まりました。この Blog でも、今後 WEC 2013 のことも交えて書く予定です。

ですが、今回は、前回の続編となることを書きます。組込み機器用の OS として WinCE/WEC が備えている、セキュリティを確保するための機能です。

■拡張性と脆弱性
前回は、ActivateDevice[Ex]() を用いてデバイスドライバを動的ロードすることができると説明しました。これは、便利な機能である反面、脆弱性の要因でもあります。

デバイスドライバを動的ロードできるということは、OS イメージに含めていないデバイスドライバを、OS の動作中に追加して、あらかじめ組み込まれてはいない機能を利用できるということです。たとえば、Flash メモリなどに配置した OS イメージを書き換えてアップデートする場合に、Flash メモリ用のデバイスドライバを OS イメージに組み込んでいなくとも、アップデート処理を実行するアプリケーションの .exe と、Flash メモリ用のデバイスドライバの .dll、および新しい OS イメージを USB メモリに入れて WEC デバイスにマウントし、アプリケーションを実行してアップデート処理を行うことが可能です。アップデート処理を実行するアプリケーションは、最初に ActivateDevice[Ex]() を使って、Flash メモリ用のデバイスドライバをカーネルにロードさせ、その後、DeviceIoControl() を使ってデバイスドライバを呼び出すことにより、OS イメージを Flash メモリに書き込む、というわけです。

デバイスドライバ、つまりカーネルモジュールを動的ロードできれば、このように拡張性を得られる一方で、悪意を持ったソフトウェアによる攻撃を容易にするという側面もあります。(悪意を持った)アプリケーションが可能な攻撃は、仮想メモリと実行モードによって制限されていますから、カーネルに対して致命的な障害を与えることは困難です。しかし、(悪意を持った)カーネルモジュールをアプリケーションがロードできてしまえば、カーネルのメモリ空間をアクセスできますし、プロセッサの特権モードで動きますので、カーネルに致命的な障害を引き起こすことは難しくありません。
(WinCE 5.0 までの純粋マイクロカーネルの場合には、デバイスドライバは、ユーザプロセスであるデバイスマネージャにロードされて動きますので、カーネルの保護は、より堅牢だったと言えます。)

■WinCE/WEC の Secure Loader
WinCE/WEC には、悪意を持ったソフトウェアによる攻撃を防ぐ機能として、Secure Loader (Loader Verifier Module) が用意されています。これは、あらかじめ OS イメージに組み込まれているアプリケーションや、認証されたアプリケーション以外は実行できないようにする仕組みです。WEC 7 のリファレンスでは、次のページで説明されています:

 Security Loader (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg155695(v=winembedded.70).aspx

Secure Loader を有効にした OS イメージを作るには、OS Design のカタログ項目において、Loader Verifier Module を選択してビルドします。Loader Verifier Module は、Platform Builder のカタログ項目ビューにおいて、次の場所にあります:

 
  Core OS
   Windows Embedded Comapct
    Security
★    Loader Verifier Module

Secure Loader を有効にすると、OS イメージに組み込まれていないアプリケーションは、実行できなくなります。つまり、上で述べたデバイスドライバの動的ロードの例のように、USB メモリに入れたアプリケーションを実行しようとしても、実行できません。

OS イメージに組み込まれていないアプリケーションの実行を許さない処理は、WinCE/WEC のローダーによって行われます。ローダーが実行ファイル(executable)をロードする際に、ロードして構わないファイルかどうかをチェックして、条件に合わないものが指定された場合はエラーとするのです。

ローダーのソースコードは、
 C:/WINCE700/private/winceos/COREOS/nk/kernel/loader.c
にあります。loader.c で実装されている OpenExecutable() が、実行ファイルをロードする関数ですが、OpenExecutable() は、OS イメージに収録されたファイルに対しては OpenFileFromROM() を、ファイルシステム上のファイルに対しては OpenFileFromFilesys() を呼び出します。OpenFileFromROM() と OpenFileFromFilesys() は、FSOpenModule() という関数を呼び出して実行ファイルの内容をロードしますが、この FSOpenModule() において、ロードして構わないファイルかどうかのチェックが行われるようです。FSOpenModule() のソースは
 C:/WINCE700/private/winceos/COREOS/nk/kernel/fscall.c
にありますが、この関数は、filesys.dll を呼び出します。filesys.dll のソースは開示されていないため、詳細は分かりませんが、OpenFileFromROM() の場合、FSOpenModule() に渡す第二引数のフラグビット列で OPENMODULE_OPEN_ROMMODULE ビットが必ず 1 になるため、このビットを使って、OS イメージに収録されたファイルと、それ以外のファイルを区別して扱っているのだと思われます。

なお、Secure Loader 機能自体は、filesys.dll ではなく、lvmod.dll という DLL で実装されており、filesys.dll が lvmod.dll を呼び出すようです。カタログ項目の Loader Verifier Module を選択すると lvmod.dll が OS イメージに組み込まれることは、
 C:/WINCE700/public/common/oak/files/common.bib
を見ると分かります。Loader Verifier Module に関連付られた SysGen 変数は CE_MODULES_LVMOD ですので、これをキーにして common.bib を検索してみて下さい。

■署名による認証
Secure Loader を有効にすることにより、OS イメージに組み込まれていないアプリケーションを実行できないようになります。これでセキュリティは非常に高まりますが、拡張性は損なわれます。あらかじめ固定された機能しか動作しない、昔ながらの組込み機器であれば、ROM に配置した OS イメージに組み込まれたアプリケーションしか動かせなくても、問題ありません。しかし、サードパーティ製のアプリケーションを動かせるようにしたり、冒頭で述べたアップデート機能など、特定の場合にだけしか使わない機能を OS イメージには組み込まず、必要な時にだけ使いたい、という場合には、 OS イメージに組み込まれたアプリケーションしか実行できないのでは、対応できません。

ご安心下さい。WinCE/WEC の Secure Loader は、そのような場合に対応するための機能も備えています。実行ファイルに対する署名と、署名に対する認証機構により、OS イメージに組み込まれていないアプリケーションでも、認証できるものは実行を許すようになっています。上述した lvmod.dll が、この認証処理を行います。lmvod.dll が行う認証処理は、上述したローダーから呼び出される他、API としても提供されています。Secure Loader の API については、リファレンスの次のページをご覧ください:

 Security Loader Reference (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg155306(v=winembedded.70).aspx

このリファレンスのページを見ると、ファイルを認証する API に加え、ブロックリスト、つまり、署名に対して認証エラーとするファイルの一覧を扱うものがあります。署名されており、署名に対する認証処理が成功するアプリケーション(実行ファイル)であっても、不正な動作をすることが分かった場合に、それをブロック対象として登録し、実行できないようにすることが可能なのです。

このように、不正なソフトウェアが実行されるのを防ぎ、セキュリティを確保するための仕組みを、拡張性と共に提供しているのが WinCE/WEC の Secure Loader というわけです。Secure Loader 用に、実行ファイルに署名を付ける手順は、リファレンスの次のページで説明されています:

 Signing Binaries (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg156011(v=winembedded.70).aspx

 Deploy an Application on an OS with Security Loader (Compact 7)
 http://msdn.microsoft.com/en-us/library/jj200466(v=winembedded.70).aspx

WEC の最新版である WEC 2013 のリファレンスにも、同じ内容の説明があります:

 Signing Binaries (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg156011.aspx

 Deploy an Application on an OS with Security Loader (Compact 2013)
 http://msdn.microsoft.com/en-us/library/jj200466.aspx

■WinCE/WEC のセキュリティ
組込み機器のセキュリティは、一つの方策だけで確保できるものでは、ありません。機器の特性に応じて、複数の方策を組み合わせる必要があります。WinCE/WEC でのセキュリティの考え方や実現方策については、以下のページが参考になるでしょう。興味のある方は、ご覧になってみて下さい。

 Security for Windows Embedded Compact (Compact 7)
 http://msdn.microsoft.com/en-us/library/ee498894(v=WinEmbedded.70).aspx

 Trusted Environment Creation (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee498576(v=winembedded.60).aspx

 Windows Embedded CE 6.0 Security Model
 http://msdn.microsoft.com/en-us/library/ee497961(v=winembedded.60).aspx

 Security for Windows Embedded Compact Code Samples (Compact 7)
 http://msdn.microsoft.com/en-us/library/hh802407(v=winembedded.70).aspx

Add comment 2014/02/02 koga

insmod と ActivateDevice()

今回は、WEC/WinCE の、カーネルモジュールを動的にロード/アンロードする方法について述べます。Linux を御存知の方であれば、insmod や modprobe、rmmod に相当するものだといえば分かるでしょう。

■デバイスドライバの動的ロードとアンロード
Linux の insmod コマンドに相当するコマンドプログラムは、WEC/WinCE には存在しませんが、同様のことを実現可能な API が提供されています。それが、ActivateDevice[Ex]() です。

 ActivateDevice (Windows Embedded Compact 7)
 http://msdn.microsoft.com/en-us/library/ee484864.aspx

 ActivateDeviceEx (Windows Embedded Compact 7)
 http://msdn.microsoft.com/en-us/library/ee484469.aspx

これらの API を使ってデバイスドライバをロードする手順は、次の通りです:

1.) ロードするデバイスドライバを DeviceManager に登録するための、レジストリ項目を設定する。

2.) ActivateDevice[Ex]() を呼び出す。

デバイスドライバを使い終え、必要なくなったら、DeactivateDevice() を呼び出します。これで、カーネル内にロードされていたデバイスドライバの DLL がアンロードされます。

上の手順を insmod コマンドの場合と比べると、レジストリ設定が必要なぶん手順が一つ多いですが、十分シンプルだと言えるでしょう。レジストリに設定する項目は、そのドライバ用のサブキー配下の ‘Dll’ と ‘Prefix’ だけです。以下は、’MyDriver’ という名前で、デバイスファイル名に使われる3文字プレフィクスが ‘MYD’ というドライバの例です:


[HKEY_LOCAL_MACHINE\Drivers\AddOn\MyDriver]
   "Dll"="MyDriver.dll"
   "Prefix"="MYD"

上の例では、レジストリキー HKEY_LOCAL_MACHINE\Drivers の下に、動的ロード対象の意味で ‘AddOn’ というサブキーを割り当て、その下に、ドライバ用のサブキー ‘MyDriver’ を設定しています。

■LoadKernelLibrary()
ところで、Linux の insmod コマンドは、カーネルのローダブルモジュールをロードすることができ、対象はデバイスドライバに限定されていません。一方、WEC/WinCE の ActivateDevice[Ex]() は、DeviceManager が管理するデバイスドライバに対象が限定されています。

実は、WEC/WinCE にも、ローダブルモジュール(DLL)をカーネルにロードさせる API があります。LoadKernelLibrary というのが、その関数です。ただし、以下のリファレンスページで説明されているように、この API は、カーネルのログ機能を司る CeLog.dll と、カーネルデバッガの DLL をロードする用途に限定されています。実際には、それ以外の DLL もロードは可能だと思いますが、推奨はされないと考えて下さい。

 LoadKernelLibrary (Windows Embedded Compact 7)
 http://msdn.microsoft.com/en-us/library/ee478202.aspx

WEC/WinCE カーネルのログ機能と、ログデータの解析ツール(Kernel Tracker)については、2012/08/16 に書いたエントリ(「CeLogFlush.exe と Kernel Tracker」)で紹介しました。このエントリで、ログ機能を有効にしていないデバイスに対しても、一時的にログ機能を有効にできると書きました。CeLogFlush.exe は、LoadKernelLibrary() を呼び出すことにより、カーネルに CeLog.dll をロードさせるのです。

■動的ロードについて、もう少し
DLL をプロセスにロードする API として、LoadLibrary() がありますが、カーネル内部でも、デバイスドライバをロードする際に LoadLibrary() を呼び出す場合があります。これについては、ActivateDeviceEx() のリファレンスで説明されています。

通常は、ドライバのロードには LoadDriver() という関数が使われるのですが、ロードするドライバに対して ‘Flags’ というレジストリ項目が設定されていて、その値が 0×2 ビット(DEVFLAGS_LOADLIBRARY)を含んでいる場合は、LoadLibrary() が使われます。LoadLibrary() と LoadDriver() の違いは、ロード対象の DLL に対してデマンドページングを行うかどうかです。LoadDriver() は、ロードする DLL をデマンドページングの対象外とします。

WEC/WinCE カーネルのデマンドページングについては、リファレンスの次のページで説明されています。

 Demand Paging Considerations (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee482784(v=winembedded.60).aspx

時間制約の厳しいデバイスドライバの場合は、LoadDriver() によりデマンドページング無しでロードします。これが通常動作です。一方、サイズが大きく、かつ、時間制約も厳しくないドライバの場合には、LoadLibrary() を使うことにより、デマンドページング有りでロードすれば、メモリ使用量を抑えることが可能というわけです。

デバイスドライバにおいて、通常動作ではデマンドページング無しでロードするのは、リアルタイム性を確保するための仕組みです。WinCE 6.0 のリファレンスには、上のページを含む、“Real-Time Performance” という節があります。興味のある方は、ご覧になってみて下さい。

WEC 7 のリファレンスには、デマンドページングについて述べたページは見当たりませんが、割り込み応答動作のタイミングを計測するツール(ILTiming.exe)の説明などが載っています。こちらも、参考になるでしょう:

 ILTiming.exe Real-Time Measurement Tool (Windows Embedded Compact 7)
 http://msdn.microsoft.com/en-us/library/ee483144.aspx

Add comment 2013/02/10 koga

Previous Posts


Categories

Links

Posts by Authors

Recent Posts

Calendar

2017年4月
« 9月    
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Posts by Month

Posts by Category

Meta