Archive for 2008/07

ソースファイルのエンコーディング変換

WinCE 6.0 のソースファイルの中には、日本語環境でビルドするとエラーになってしまうものがあります。たとえば、
 <WINCE600>/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/MSFLASH/
にある FMDWRAPPERPDD というサブプロジェクト(※PlatofmBuilder/VS .NET 2005 の「ソリューションエクスプローラー」では、”fmdwrapper” という小文字表記になります)をビルドすると、次のエラーが出ます:

BUILD: [01:0000000035:PROGC ] Compiling .\fmdwrapperpdd.cpp
BUILD: [01:0000000038:ERRORE] d:\apps\wince600\public\common\oak\drivers\block\msflash\fmdwrapperpdd\FmdWrapperPdd.h : error C2220: warning treated as error – no ‘object’ file generated
BUILD: [01:0000000039:WARNN ] d:\apps\wince600\public\common\oak\drivers\block\msflash\fmdwrapperpdd\FmdWrapperPdd.h : warning C4819: The file contains a character that cannot be represented in the current code page (932). Save the file in Unicode format to prevent data loss
BUILD: [01:0000000040:PROGC ] Compiling .\fmdwrappermain.cpp
BUILD: [01:0000000043:ERRORE] d:\apps\wince600\public\common\oak\drivers\block\msflash\fmdwrapperpdd\FmdWrapperPdd.h : error C2220: warning treated as error – no ‘object’ file generated
BUILD: [01:0000000044:WARNN ] d:\apps\wince600\public\common\oak\drivers\block\msflash\fmdwrapperpdd\FmdWrapperPdd.h : warning C4819: The file contains a character that cannot be represented in the current code page (932). Save the file in Unicode format to prevent data loss

ここで、エラー行のメッセージは “warning treated as error – no ‘object’ file generated” なのですが、その原因は、警告行のメッセージで報告されています:

 The file contains a character that cannot be represented in the current code page (932). Save the file in Unicode format to prevent data loss

メッセージの内容は、コンパイラがテキストファイルを読む際に、現在設定されているコードページでは表現不可能な文字が含まれている、というものです。そのため、Unicode で保存し直せと言っていますので、このファイル(FmdWrapperPdd.h)のエンコーディングを変換しなければいけません。

実は、FmdWrapperPdd.h の内容をよく見ると、FmdWrapperPdd クラスの定義で、LockBlocks() メソッドの宣言に書かれているコメントに、余計な文字が付いているのが原因だと分かります。80行目にある、以下の箇所です:

    //Hardware lock a range of physical blocks. Must be implemented if block locking is to be 
    // supported.  BlockRun specifies the run of blocks to lock.  All other blocks are assumed
    // to be unlocked.
    virtual LRESULT LockBlocks (
        IN DWORD BlockRunCount,
        IN BLOCK_RUN BlockRunList[]);

ここで、コメント先頭の “Hardware” の前に、余計な文字が入っているのです。おそらく、意図的なものではなく、このヘッダファイルが書かれた時に、不注意なタイプ入力ミスがあったなどしたのでしょう。この余計な文字を削ってやれば、コンパイルエラーが解消されます。

とはいえ、このようなコンパイルエラーが起きた際に、原因となっている文字を探す時間がない、という場合もあるでしょう。そのような場合は、コンパイラが出したメッセージの通り、ファイルのキャラクタエンコーディングを変換してやる、というのが安直です。さいわい、VS .NET 2005 の IDE には、テキストファイルのキャラクタエンコーディングを変換する機能があります。

この手の機能は、最近の IDE(統合開発環境)では珍しくありませんが、あると便利なことが多いものです。僕は、VS .NET 2005 よりも動きが軽快という理由で、もっぱら VC++ 6 の IDE をコーディングの際のエディタに使っていますが、VC++ 6 の IDE には、このエンコーディング変換機能がないため、若干不便に思います(余談になりますが、WinCE 用のプロジェクトでも、WinCE に依存しない部分は、VC++ 6 の IDE でコーディングして、ビルドと単体テストを済ませてから WinCE のプロジェクトへ取り込む、という手順で作業を進めることもあります)。

さて、ご存知ない方のために、VS .2005 の IDE でテキストファイルのエンコーディング変換を行う手順を説明します。キャラクタエンコーディングに加え、改行文字も変換できますから、わりに重宝しています。手順は、次の通りです:

 1.) 変換したいファイルを IDE で開きます。 

 2.) Alt-F キーと A キーを入力するか、または、[ファイル] -> [名前を付けて xxx を保存...] メニューを選択して下さい(※”xxx” は、実際のファイル名)。「名前を付けてファイルを保存」ダイアログが開きます。

 3.) 開いたダイアログの「保存」ボタンの右に付いているプルダウンメニューをクリックして開き、「エンコード付きで保存…」を選択して下さい。

 4.) 既存のファイルを置き換えてよいかというアラートが出ますので、「OK」をクリックして閉じて下さい。「保存オプションの詳細設定」というダイアログが開きます。

 5.) 「保存オプションの詳細設定」ダイアログには、「エンコード」と「行の終わり」という二つのドロップダウンリストがあります。このドロップダウンリストで、それぞれ、キャラクタエンコーディングと改行コードを指定できます。

 6.) 設定したいキャラクタエンコーディングと改行コードを指定したら、「OK」ボタンをクリックして保存して下さい。

上記の手順で、キャラクタエンコーディングに”Unicode(UTF-8 シグネチャ(BOM)付き)- コードページ 65001”を指定して上書き保存すると、上述したコンパイルエラーを解消できます。試してみて下さい。

なお、PlatformBuilder のコンパイラは、ファイル先頭の BOM がないと正しく認識できないようで、”Unicode(UTF-8 シグネチャ(BOM)なし)- コードページ 65001”で保存した場合は、上述のコンパイルエラーは解消できません。この点は、ご注意下さい。
 
ところで、上述のビルドエラーには、OSDesign を単純にビルドする場合には遭遇しません。プレビルドしたバイナリが使われるので、上記のソースのコンパイルが起きないからです。しかし、SYSGEN 変数の設定を変えるなどして、「システム生成」を行う必要が生じて
 [ビルド] -> [詳細なビルドコマンド] -> [ビルドおよびシステム生成]
メニューを実行した場合には、上述のビルドエラーに遭遇することでしょう。もちろん、日本語環境を使っている場合には、ですけれど。

Add comment 2008/07/24 koga

カーネルからWinSock

カーネルモードで動作するデバイスドライバでは、アプリケーションから使える API を同じように呼び出すことは、できません。一般に、呼び出せる API には制限が加わります。ここで「API」と書きましたが、デバイスドライバから呼び出す関数やルーチンを “API”(Application Programming Interface) と呼ぶのは、適切ではありませんね。「システム関数」や「システムルーチン」と呼ぶべきなのかも知れません。

さて、デバイスドライバを実装する際に、アプリケーションと同じシステム関数を呼び出したい場合があります。たとえば、デバイスドライバから GUI 表示を行いたい場合や、TCP/IP 通信などのネットワーク入出力を行いたい場合です。WinCE 6.0 では、このような場合、そのデバイスドライバを、カーネル・モード・ドライバではなく、ユーザ・モード・ドライバとして動かすというのが、最も安直でしょう。

デバイスドライバをユーザ・モード・ドライバとして動かす場合、そのドライバに対する、.bib ファイルの MODULES セクションの記述と .reg ファイルのレジストリ項目記述による指定が必要です。.bib ファイルの MODULES セクションでは、Type エントリに記述するフラグから K を外すか、または、K の代わりに Q を指定しなければいけません。MSDN のリファレンスの、
 http://msdn.microsoft.com/en-us/library/aa908680.aspx
 http://msdn.microsoft.com/en-us/library/aa909662.aspx
が参考になるでしょう。.reg ファイルのレジストリ項目では、そのドライバのキーの Flags 値に 0×10(EVFLAGS_LOAD_AS_USERPROC)を設定します。これについては、MSDN のリファレンスの、
 http://msdn.microsoft.com/en-us/library/aa930532.aspx
に説明があります。

しかし、ユーザ・モード・ドライバの場合、アプリケーションからドライバを呼び出す際のオーバーヘッドが大きいという短所があります。カーネル・モード・ドライバの場合とは異なり、ユーザ・モード・ドライバの呼び出しでは、システムコールと Device Manager を介した先に、さらに User Mode Driver Reflector と User Mode Driver Host が介在するからです。この様子は、MSDN のリファレンスの
 http://msdn.microsoft.com/en-us/library/aa932450.aspx
で説明されています。また、今年8月号の『インターフェース』誌に掲載された解説記事にも、説明が載っているようです(第2章「–新しくなったOSアーキテクチャと変更点– Windows CEにおけるデバイス・ドライバ開発の実際」)。
 http://www.cqpub.co.jp/INTERFACE/contents/2008/JA/200808.htm

僕は『インターフェース』誌を購読していないので、記事の詳細は分かりませんが、参考になる記事ではないかと思います。ちなみに、上のページでは、記事の冒頭2ページが載った PDF をダウンロードできます。

ところで、ユーザ・モード・ドライバだと、アプリケーションからの呼び出しのオーバーヘッドが大きいので、カーネル・モード・ドライバとして動かしたい、と考えることも多いでしょう。なおかつ、そのドライバで、どうしても GUI 表示やネットワーク入出力処理を行いたい、という場合には、別の手段があります。カーネル・モード・ドライバから GUI 表示を行う場合には、CeCallUserProc() というカーネル関数を使います。この関数の説明は、MSDN のリファレンスの
 http://msdn.microsoft.com/en-us/library/aa909021.aspx
にあります。また、Windows CE Base Team Blog の
 http://blogs.msdn.com/ce_base/archive/2006/11/09/CE6-Drivers_3A00_-What-you-need-to-know.aspx
も参考になるでしょう。CeCallUserProc() をカーネル側から呼び出した場合、udevice.exe、つまり User Mode Driver Host を介して、指定した DLL のルーチンが呼び出されます。GUI の表示処理は、その DLL で実装しておくというわけです。

次は、GUI 表示ではなく、ネットワーク入出力です。実は、ネットワーク入出力については、WinSock API をカーネル・モード・ドライバから呼び出すことができるのです。つまり、GUI 表示の場合とは異なり、CeCallUserProc() を使わず、直接 WinSock API を呼び出すことが可能です。これについては、
 http://download.microsoft.com/download/a/0/9/a09e587c-4ff9-4a58-a854-56fe50b862b2/release%20notes.htm
に記載があります(”Do not use k.ws2.dll from kernel mode”)。このページでは、”k.ws2.dll を使わないように”という注意書きがあるのですが、これについては、
 http://msdn.microsoft.com/en-us/library/aa915026.aspx
を併せ読むのがよいでしょう。WinCE での WinSock API の DLL は ws2.dll であり、WinSock API を使うアプリケーションは、ws2.lib をリンクライブラリに加えてビルドします。この ws2.dll のカーネルモジュール用のものが k.ws2.dll というわけです。

上の二番目の Web ページの説明(”Loading DLLs in Kernel Mode or User Mode: Windows CE 5.0 vs. Windows Embedded CE 6.0″)で書かれているように、カーネル・モード・ドライバ、つまりカーネルモジュールがリンクした DLL をロードする際、WinCE 6.0 のカーネルは、リンク指定された DLL の名前の前に ‘k.’ を付けた DLL があれば、そちらを自動的にロードするようです。従って、カーネル・モード・ドライバのビルド設定で、リンクライブラリとして ws2.dll が指定されていた場合、k.ws2.dll がロードされます。しかし、上の一番目の Web ページの注意書きでは、k.ws2.dll を使わないようにと言っていますので、WinSock API を使うカーネル・モード・ドライバでは、リンクライブラリとして、ws2.lib(または k.ws2.lib)を指定せず、カーネルモジュール専用の ws2k.lib をリンクするのがよいでしょう。

なお、WinCE ではなく、WinXP までの WinNT 系カーネルでは、カーネルモジュールからネットワーク入出力を行う場合には、このようなことができず、TDI(Transport Driver Interface) を使う必要があったようです。Vista からは、この点が改善され、WinSock と同様のインタフェースを持つ WSK(WinSock Kernel) が提供されているそうです:
 http://www.exconn.net/Blogs/windows/archive/2006/03/04/7383.aspx
 http://www.atmarkit.co.jp/fwin2k/network/baswinlan017/baswinlan017_03.html
 http://www.themssforum.com/Drivers/Driver-serial-104389/

WinCE 6.0 の ws2k.dll は、この WSK と同じ位置づけだと言えるでしょう。ただし、現状では、ws2k.dll の呼び出しはカーネル側で完結せず、CeCallUserProc() を使ってユーザランド側の DLL を呼び出す場合と同様に、ユーザランド側のモジュールを介して、ユーザランド側から WinSock が呼び出される仕組みになっているようです。これは、上に挙げた
 http://download.microsoft.com/download/a/0/9/a09e587c-4ff9-4a58-a854-56fe50b862b2/release%20notes.htm
で説明されています。実際、WinSock API を使って TCP/IP 通信を行うカーネル・モード・ドライバを作り(※もちろん、そのドライバをビルドする際には、ws2k.lib をリンクします)、カーネルデバッガを接続して動かすと、その様子を見ることができます。たとえば、Ethrnet コントローラのドライバのソースのパケット送出関数にブレークポイントをセットして、そのブレークポイントがヒットした時のコールスタックを見ると、ws2serv.exe をロードした udevice.exe のスレッドからの呼び出しになっています。その状態で、当該デバイスドライバからの WinSock 呼び出しを見ると(※カーネルデバッガの「スレッド」メニューを使えば、executable ごとのスレッド一覧と、各スレッドのコールスタックを表示できます)、ws2k.dll から User Mode Driver Reflector を介して ws2serv.exe を呼び出し、その呼び出しの完了待ちになっている様子が分かります。

将来的には、Vista の WSK と同様、WinCE の ws2k.dll が提供するインタフェースの実装が全てカーネル側に置かれ、それによって、カーネル側からユーザランドを呼び出して戻るオーバーヘッド無しで、WinSock によるネットワーク入出力が可能になるのだと思われます。

Add comment 2008/07/22 koga

圧縮フォント

日本語フォントを追加したら、OS イメージのサイズが大きくなり過ぎてしまい、ROM に収まらなくなってしまう、という場合があります。このような場合、圧縮されたフォントファイルを使うように設定することで、OS イメージのサイズを小さくできます。

WinCE では、日本語などのアジア言語圏用のフォントに対しては、非圧縮の True Type フォントファイル(拡張子 .ttc)を使うか、圧縮形式のフォントファイル(拡張子 .ac3)を使うかを、カタログ項目の選択で切り替えできます。日本語の場合、対応するカタログ項目は、次のものです:

 コア OS
  CEBASE
   インターナショナル
    ロケール特定サポート
     日本語
      Monotype Imaging AC3 フォント圧縮

このカタログ項目を選択すると、日本語フォントのフォントファイルは、非圧縮の .ttc ではなく、圧縮形式の .ac3 が OS イメージに含められます。これらのフォントファイルは、<WINCE600>/PUBLIC/COMMON/OAK/FILES/ に配置されています。

たとえば、MS Gothic(「MS ゴシック、MS P ゴシックおよび MS UI Gothic」)の場合、非圧縮のフォントファイルが msgothic.ttc、圧縮形式のフォントファイルが msgothic.ac3 です。両者のファイルサイズを比べると、msgothic.ttc は 7.89[MB]、msgothic.ac3 は 4.63[MB] で、3[MB] 以上もサイズが違います。

上記カタログ項目の「Monotype Imaging AC3 フォント圧縮」は、SYSGEN 変数 ‘SYSGEN_AGFA_FONT’ に関連づけられており、この変数が定義されると、’FONTS_AC3_VERSIONS’ という SYSGEN 変数も有効になるようです。<WINCE600>/PUBLIC/COMMON/OAK/FILES/ にある common.bib と common.reg を見ると、FONTS_AC3_VERSIONS が有効かどうかにより、フォントファイルとして .ttc を使うか .ac3 を使うかが切り替わるのが分かります。

「Monotype Imaging AC3 フォント圧縮」(”Monotype Imaging AC3 Font Compression”)についての説明は、MSDN のリファレンスの
 http://msdn.microsoft.com/en-us/library/aa912914.aspx
に説明があります。このカタログ項目は、WinCE 6.0 から名前が変わっています。上のページにも説明がありますし、
 http://msdn.microsoft.com/en-us/library/aa924053.aspx
にも書かれています(”Agfa AC3 Font Compression”)。

ちなみに、.ac3 形式のフォントファイルを展開する処理は、decomdll.dll というフォントドライバプラグインによって実装されているようです:
 http://msdn.microsoft.com/en-us/library/aa913710.aspx
この decompdll.dll および、decomdll.dll で実装されているルーチンの呼び出しを行う部分(※GDI/GWES 内部から呼び出されるようです)は、ビルド済みのバイナリのみ提供されるコンポーネントであるため、ソースを見て詳細を理解することはできません。

1 comment 2008/07/08 koga

RTCを持たないデバイスで日時合わせ

開発ターゲットのデバイスが RTC を搭載していない場合、日時設定をどうするか、という問題が生じます。もちろん、画面表示を行わないデバイスであったり、あるいは、WinCE の標準シェルを使わず、現在日時の表示や取り扱いをしなくてよいデバイスであれば、日時設定の必要はありません。また、一度電源を入れたら、ずっと電源を切らず、リセットもしない、というデバイスであれば、起動時に日時設定を行えばよいでしょう。

しかし、ネットワークに接続するデバイスの場合には、他の機器と通信する関係上、現在日時の設定が必要なことが多いと思います。そのような場合、たとえばWeb端末であれば、接続した Web サーバが返す HTTP レスポンスメッセージを解析し、HTTP レスポンスの Date ヘッダに記述された日時を設定する、という方策もあるでしょう。もっと簡単に、ネットワークタイムサーバから日時を取得して設定することも可能です。

さいわいにして、WinCE では、SNTP(Simple NTP) クライアント機能が標準で付属しています。SNTP クライアント機能を OS イメージに組み込んで動かせば、デバイスの起動時にネットワークタイムサーバを参照し、自動的に現在日時設定を行わせることができます。もちろん、起動後は、設定した間隔でネットワークタイムサーバを参照して、自動的に時刻合わせを行います。

以下に、WinCE 付属の SNTP クライアント機能を組み込む手順を説明します。なお、WinCE 付属の SNTP 機能については、MSDN のリファレンスにも記載があります:
 http://msdn.microsoft.com/en-us/library/aa919019.aspx

ただし、そこでの説明には、SNTP クライアント機能を有効にするためのレジストリ設定で欠けているものがあります。以下の説明では、その点についても述べます。
 

■SNTP クライアント機能のカタログ項目
SNTP クライアント機能を OS イメージに組み込むためのカタログ項目は、次の二つです:

 コア OS
  CEBASE
   通信サービスおよびネットワーク
    Simple Network Time Protocol (SNTP)
     DST 付き SNTP クライアント
     SNTP 自動更新およびサーバー同期

「DST 付き SNTP クライアント」は、標準シェルが依存しており、標準シェルを OS イメージに含めると、自動的に選択されます。ただし、この項目だけでは SNTP クライアント機能が有効になりません。「SNTP 自動更新およびサーバー同期」も選択して下さい。

■SNTP クライアント機能のレジストリ設定
SNTP クライアント機能を動作させるためのレジストリ項目は、MSDN のリファレンスに記載されています:
 http://msdn.microsoft.com/en-us/library/aa924011.aspx

ただし、このページに記載されている項目は、不足しています。このページに記載されている以外に、AutoUpdate という DWORD 型のキー値を  1 に設定しなければいけません。このキー値が必要なことは、SNTP クライアントのソースを読めば分かります。SNTP クライアントのソースは、<WINCE600>/PRIVATE/SERVERS/TIMESVC2/SNTP/ にあります。このディレクトリにある sntp.cxx がソースファイルで、レジストリ設定の読み出し処理は、TimeState::RefreshConfig() で実装されています。

いきなりソースを読むというのは、少々乱暴かも知れませんね。その場合は、WinCE 付属のレジストリ設定ファイルを見て下さい。SNTP クライアント関連のレジストリ設定記述は、<WINCE600>/PUBLIC/SERVERS/OAK/FILES/ にある servers.reg にあります。このファイルをテキストエディタで開いて、’SERVERS_MODULES_TIMESVC’ で検索してみて下さい。SNTP 機能に関するレジストリ項目の説明を記したコメントと、実際のレジストリ記述があります。AutoUpdate の説明もあります。レジストリ記述を見ると、’TIMESVC_TSVC_AUTO_UPDATE’ という SYSGEN 変数が定義されていれば、AutoUpdate を含め、SNTP クライアント機能を動作させるためのレジストリ設定が有効になるようです。ただし、この SYSGEN 変数は、PlatformBuidler(Visual Studio) の「カタログ項目ビュー」に表示される、上で述べたカタログ項目には関連づけられていません。従って、この SYSGEN 変数を自分で設定するか、または、プロジェクト固有のレジストリ設定ファイル(project.reg)に、必要なレジストリ記述を追加しなければいけません。

以下は、SNTP クライアント用のレジストリ設定記述の例です。これを project.reg に追加して OS イメージをビルドすれば、SNTP クライアント機能が動作するはずです。

[HKEY_LOCAL_MACHINE\Services\Timesvc]
“server”=”time.windows.com”
“AutoUpdate”=dword:1
“refresh”=dword:36EE80
“recoveryrefresh”=dword:493E0
“threshold”=dword:5265C00
“ServerRole”=dword:0
“trustlocalclock”=dword:0

servers.reg ファイルでは、’ServerRole’ を 1 に設定していますが、SNTP クライアント機能だけを使う場合は、0 に設定して構いません。上の例では、’ServerRole’ を 0 に設定しています。

 
■ついでにタイムゾーン設定
SNTP クライアントとは関係ありませんが、タイムゾーン設定のレジストリ項目について、ついでに書きます。OS イメージを小さく抑えたいなどの理由で、日本語フォントを入れない場合、OS イメージのロケールを英語にすることになるでしょう。ロケールを日本語にした場合は、タイムゾーンも日本になるのですが、ロケールとは異なるタイムゾーンを設定したい場合には、[HKEY_LOCAL_MACHINE\Time Zones] というレジストリキーを使って設定します。以下が、タイムゾーンを Tokyo にする場合の記述です。

[HKEY_LOCAL_MACHINE\Time Zones]
“Default”=”Tokyo Standard Time”

この記述を project.reg に追加して OS イメージをビルドすれば、ロケール設定に関係なく、タイムゾーンが日本(Tokyo)になります。

Add comment 2008/07/04 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

2008年7月
    9月 »
 12345
6789101112
13141516171819
20212223242526
2728293031  

Posts by Month

Posts by Category

Meta