Posts filed under '付属機能の使い方'
前回のエントリで、.NET CF アプリケーションから WNetAddConnection3() を呼び出す例を述べました。その例の説明で書いたように、WNetAddConnection3() は、Windows フィルサーバとの接続などを行う API です。
■Windows ファイルサーバに対するクライアント機能
Windows ファイルサーバ(CIFS サーバ)をアクセスする機能、つまり、Windows ファイルサーバに対するクライアント機能は、”Windows Networking API/Redirector” というコンポーネントになっており、SYSGEN 変数の SYSGEN_REDIR を設定することにより、OS イメージに組み込まれます。詳細については、リファレンスの次のページをご覧ください。
Windows Networking API/Redirector Reference (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee495264.aspx
Windows Networking API/Redirector (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-US/library/ee494246(v=winembedded.60).aspx
Platform Builder のカタログ項目ビュー(Catalog Items View)の階層で言うと、次の項目を選択すると、SMB/CIFS クライアント機能が OS Design に組み込まれます(※WEC 7 の場合):
<OS Design 名>
Core OS
Windows Embedded Compact
Communication Services and Networking
Networking – General
★ Windows Networking API/Redirector (SMB/CIFS)
ここで、”Redirector” という Windows の用語に馴染みのない方は、たとえば、次の解説記事が参考になるのではないかと思います。
「基礎から学ぶWindowsネットワーク 第20回」
http://www.atmarkit.co.jp/fwin2k/network/baswinlan020/baswinlan020_03.html
「基礎から学ぶWindowsネットワーク」
http://www.atmarkit.co.jp/fwin2k/network/baswinlan002/baswinlan002_03.html
なお、CIFS クライアントだけでなく、CIFS サーバ機能も WEC/WinCE に付属しています。こちらは、カタログ項目ビューの階層の、次の場所にあります:
<OS Design 名>
Core OS
Windows Embedded Compact
Communication Services and Networking
Servers
File Server (SMB/CIFS)
CIFS サーバに関する記述は、なぜか WEC 7 のリファレンスに見当たりません。興味のある方は、WinCE 6.0 のリファレンスをご覧ください。
File Server (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-us/library/ee500573(v=winembedded.60).aspx
■SMB/CIFS クライアント機能を組み込む場合の注意点
さて、SMB/CIFS クライアント機能(Windows Networking API/Redirector)を組み込む場合、一つ注意しなければいけません。WEC/WinCE のリファレンスには、このコンポーネントが依存するのは、TCP/IP (SYSGEN_TCPIP) と Winsock (SYSGEN_WINSOCK)、および NDIS (SYSGEN_NDIS) とだけ書かれているのですが、それらのコンポーネントだけでは、SMB/CIFS サーバにアクセスすることは、できません。
CIFS サーバに接続する場合は、認証処理が必要ですが、認証処理に必要なコンポーネントは、SMB/CIFS クライアント機能を組み込んでも自動的に組み込まれないため、明示的に組み込む必要があるのです。認証処理に必要なコンポーネントは、WEC 7 ですと、カタログ項目ビューの階層の、次の場所にあります:
<OS Design 名>
Core OS
Windows Embedded Compact
Security
Authentication Services (SSPI)
★ Kerberos
★ NTLM
通常は、NTLM だけを選択しても CIFS サーバに接続できるでしょう。お手元の環境で、試してみて下さい。これらのコンポーネントについては、リファレンスの次のページで説明されています(※WEC 7 の場合)。
NTLM Security Support Provider (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498104.aspx
Kerberos Security Support Provider (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498711.aspx
Authentication Services (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee498877.aspx
2012/05/07
koga
Windows Embedded CE/Compact には、PC 用の Windows と同様のコマンドプロセッサ(cmd.exe)が付属しているのは、皆さんご存じの通りです。今回は、このコマンドプロセッサをシリアルケーブル経由で PC から操作する手順について述べます。
さて、Platform Builder のカタログ項目ビューで依存項目を見ると、コマンドプロセッサには、コンソールウィンドウと Telnet サーバーが依存していることが分かります。コンソールウィンドウは、「コマンドプロンプト」のウィンドウですが、コンソールアプリケーションから printf() などで標準出力に出力した場合も、コンソールウィンドウが開いて出力内容が表示されます。PC 用の Windows でも、同様にコンソールウィンドウが開きますよね。つまり、コンソールウィンドウは、標準出力と結びつく(同様に、標準入力とも結びつく)コンソールデバイスとして機能するというわけです。Telnet サーバについては、後で述べます。
■コンソールの出力先に対するレジストリ設定
コマンドプロセッサを、シリアルケーブル経由で PC から操作するには、コンソールの出力先の設定を変更します。この設定は、リファレンスの次のページで説明されています:
Command Processor Registry Settings (Windows Embedded CE 6.0)
http://msdn.microsoft.com/en-US/library/ee506287(v=winembedded.60).aspx
Command Processor Registry Settings (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee506287.aspx
上のページで説明されているように、[HKEY_LOCAL_MACHINE\Drivers\Console] キー直下の OutputTo の値により、コンソールの出力先を、コンソールウィンドウ以外に設定できるのです。たとえば、2番のシリアルポート(COM2)であれば、次のような設定になります。
[HKEY_LOCAL_MACHINE\Drivers\Console]
"OutputTo"=dword:2
"COMSpeed"=dword:1C200
上の例は、COM2 をコンソールの出力先とし、COM2 のボーレートを 115200 としています。
上記のように設定することで、シリアルケーブルで接続した PC から、ターミナルソフトを使ってコマンドプロセッサを操作できます。このように設定しておくと、コマンドプロセッサ(cmd.exe)を起動すると、コンソールウィンドウは開かず、代わりに、シリアルケーブルで接続した PC のターミナルソフトのウィンドウに、コマンドプロセッサのプロンプトが出力されます。そして、コマンドプロセッサのプロンプトに対して、コマンド名やプログラム名をタイプ入力すれば、Windows Embedded CE/Compact 上のコンソールウィンドウの場合と同様、コマンドやプログラムを実行できます。
なお、コンソール出力をデフォルト(0)以外に設定すると、「コマンドプロンプト」を起動しても、ウィンドウが開かず、コンソール出力に設定したデバイスにプロンプトが出力されます。この点は、注意して下さい。
■シリアルコンソールに関する注意点
シリアルケーブル経由でコマンドプロセッサを使う設定については、たとえば、次の Blog エントリでも紹介されています:
Command line (Console) over serial
http://ce4all.blogspot.com/2007/06/command-line-console-over-serial.html
このエントリには、いくつか読者コメントが付いており、最後のコメントにある「入力がエコーバックされない」という質問には、回答が付いていないままです。実は、エコーバックされないのが通常動作です。コマンドプロセッサ(cmd.exe)のソースを見ると分かりますが、cmd.exe 自身は、コンソールに対して入力をエコーバックしません。バッチファイル(.bat, .cmd)の中で、echo コマンドによるエコーバックの ON/OFF 切り替えは出来ますが、コンソールに対しては、echo コマンドの実行有無とは関係無く、エコーバック動作は行いません。
cmd.exe のソースコードは、WinCE/WEC のソースツリーの、以下の場所にあります:
%_WINCEROOT%/private/winceos/UTILS/cmd2/
コンソールに対する入力処理は、cmd.cxx にある cmd_GetInput() で実装されていますが、バッチファイル(.bat, .cmd)の実行ではなく、コンソールからの入力、つまり標準入力に対する入力処理では、エコーバックは行いません。そもそも、コマンド入力を _fgetts() で取得していますので、タイプ入力の1文字ごとにエコーバックする仕組み自体がないのです。従って、シリアルコンソールで入力のエコーバックを実現するには、自前でエコーバック処理を実装する必要があります。
■タイプ入力に対するエコーバック動作
コマンドプロセッサのプロンプトに対してエコーバックする処理は、コマンドプロセッサが標準入出力を行う先の、「コンソールデバイス」が担当する役割です。たとえば、コンソールウィンドウは、コマンドプロセッサがデフォルトで使用するコンソールデバイスであり、コンソールウィンドウにおけるタイプ入力のエコーバック処理は、コンソールウィンドウが行います。
もう一つの例は、Telnet サーバです。以下のページでも言及されていますが、Telnet サーバは、自身をコンソールデバイスとして登録したうえで、コマンドプロセッサ(cmd.exe)を起動します。
http://msdn.microsoft.com/en-us/library/aa446909.aspx (Implementing a Network Service on Windows CE)
そして、Telnet クライアントでのタイプ入力を受け取り、それを、コンソールデバイスのインタフェースを介してコマンドプロセッサへ渡し、コマンドの実行を依頼します。コマンドプロセッサがコマンドを実行し、標準出力へ出力を行うと、それが(入力とは逆の向きに)コンソールインタフェースを介して Telnet サーバに渡され、Telnet クライアントに送信される、というわけです。なお、Telnet の場合には、Telnet クライアントでのタイプ入力のエコーバック処理は、Telnet クライアント自身が行うことになるはずです。
コンソールウィンドウのソースコードは、開示されていないため、コンソールデバイスとしての振る舞いの詳細を見ることができません。一方、Telnet サーバの方は、
%_WINCEROOT%/public/servers/sdk/samples/telnetd/
にソースコードが収録されていますので、興味のある方は、ご覧になってみて下さい。telndev.cpp において、Stream Interface Driver のインタフェース(TEL_Init(), TEL_Deinit(), TEL_Open(), TEL_Close(), TEL_Read(), TEL_Write(), TEL_Seek(), TEL_IOControl(), TEL_PowerUp(), TEL_PowerDown() 関数)を実装しており、TEL_Read() と TEL_Write() で、コンソール入出力の処理を行います。
Telnet サーバが cmd.exe を起動する処理は、telnetd.cpp にある TelnetLaunchCmd() ですが、ここでは、cmd.exe を起動する前に、SetStdioPathW() を呼び出して標準入出力を変更します。この時、コンソールデバイスとして、自分自身を実体に割り当てたデバイス(”TEL<インデックス>:” というデバイス名)を設定することにより、cmd.exe の標準入出力先として自身を設定します。Telnet サーバは、Windows の流儀でいう「サービス」、つまり、ユーザモードのデバイスドライバとして動作しますから、このような動作が可能なのです。
■エコーバック動作の実現方策
では、シリアルコンソールの場合に、タイプ入力のエコーバック動作を実現するには、どうすればよいのでしょうか?
方策は、二つ考えられます。一つは、Telnet サーバのように、自前でコンソールデバイス機能を実装し、コマンドプロセッサとシリアルポートの間に介在することにより、シリアルポートからの入力を、一文字ごとにエコーバックする、という方策です。
もう一つは、シリアルポートに対して直接入出力を行い、シリアルポートから文字列が入力されたら、入力された文字列をコマンド名として、都度コマンドプロセッサ(cmd.exe)を起動する、という方策です。
二つの方策のうち、後者の方が、より簡単に実装できます。cmd.exe にコマンドを実行させるには、/Q と /C オプション付きで cmd.exe を実行すればよいのです。つまり、
”/Q /C <コマンド名>[ コマンド引数]”
という文字列を、CreateProcess() の第二引数として渡し、第一引数に “cmd.exe” を渡して呼び出せば、cmd.exe がコマンドを実行し、その結果が、レジストリで設定したコマンドプロセッサのコンソール、つまりシリアルポートへ出力されます。その際、CreateProcess() が返したプロセスハンドルに対して WaitForSingleObject() を呼び出せば、コマンド実行の完了を待つことが出来ます。
ただし、二番目の方策については、次の点を注意して下さい:
・コマンド実行のたびに、都度 cmd.exe を起動するので、シリアルコンソールから cd コマンドが実行されても、その結果が保持されない。
(cd コマンドに対応するとしたら、移動先に指定されたディレクトリを、cmd.exe を起動するプログラム自身が記録することによって、履歴動作を実現する必要がある。)
・cmd.exe を実行している間は、シリアルポートを閉じて、cmd.exe がシリアルコンソールを使用できるようにしなければならない。
二番目の注意点ですが、これは、シリアルポートのデバイスドライバの制約によるものです。シリアルポートは、一つの実体に対し、デバイスを一つしかオープンできないのです。たとえば、COM1 であれば、”COM1:” を指定して CreateFile() を呼び出すことにより、シリアルポートのデバイスをオープンします。この時、”COM1:” のデバイスをオープンしたままの状態で、同じシリアルポート(”COM1:”)に対して CreateFile() を呼び出すと、エラーとなり、オープンできません。この制約は、シリアルポートのデバイスドライバの、MDD レイヤの実装によるものです。
シリアルポートのデバイスドライバの MDD レイヤのソースコードは、
%_WINCEROOT%/public/COMMON/oak/drivers/serial/com_mdd2/
に収録されています。このディレクトリにある mdd.c で実装されている COM_Open() を見ると、既にオープン済みのシリアルポートに対して呼び出された場合は、ERROR_INVALID_ACCESS をエラーコードとしてセットして、NULL を返すことが分かります。
ところで、シリアルコンソールにバックスペース(’\b’)が入力された場合、エコーバック処理では、入力を一文字消さなければいけません。単純に ‘\b’ をエコーバックしただけでは、PC のターミナルソフトでは、カーソルが一文字戻るだけで、文字が消えないのではないかと思います。カーソルを戻すだけでなく、直前にあった文字を消したい場合には、’\b’ を単純にエコーバックする代わりに、”\b \b” という文字列を出力すればよいでしょう。つまり、カーソルを一文字分戻した後、空白文字を出力することにより、直前にあった文字を消し、再度 ‘\b’ を出力してカーソルを戻す、というわけです。
■シリアルコンソールを利用した管理機能の実現
上で述べた、シリアルコンソールのタイプ入力に対するエコーバック動作の二つの実現方策のうち、後者の方は、シリアルコンソールを使った管理機能を実装する際には、却って向いているかも知れません。前者の方策の場合、OS (WinCE/WEC) の起動完了直後にコマンドプロセッサを起動するように設定すれば、シリアルコンソールにコマンドプロセッサのプロンプトが出力されて、Linux などと同じ感覚で使うことが可能です。しかし、そのようにしてしまうと、全てのプログラムをコマンドプロセッサから実行できてしまいます。
シェルをカスタマイズして、特定の操作しかできないようにしたデバイスや、あるいは、ヘッドレスのデバイスの場合には、限られたコマンドだけをシリアルコンソールから実行できるように制限したり、また、ログイン動作を実装してセキュリティを確保する必要があるでしょう。そのような場合は、単純にコマンドプロセッサをシリアルコンソールへ割り当てるのではなく、コマンドプロセッサを呼び出す「ラッパー」/「ドライバ」プログラムを割り当てて、そのプログラムが、ログイン動作や、シリアルコンソールから実行可能なプログラムの名前だけを受け付けて実行する、という仕組みにするのが良いと思います。
2012/03/15
koga
Visual Studio 2005 以降の Visual C++ は、OpenMP 2.0 をサポートしています。OpenMP は、共有メモリ型の並列処理を記述するための仕組みで、C/C++ と Fortran に対する仕様が、OpenMP Architecture Review Board (OpenMP ARB) によって策定・公開されています。OpenMP の詳細は、OpenMP ARB の Web サイト、および、Visual Studio のリファレンスをご覧ください:
Open MP
http://openmp.org/
OpenMP C and C++ Application Program Interface
http://msdn.microsoft.com/en-us/library/8y6825×5(v=vs.90).aspx
なお、OpenMP 仕様の最新版は、2011年の 3.1 ですが、Visual C++ がサポートしているのは 2.0 です。
さて、この OpenMP 2.0 は、Windows Embedded Compact 7 (WEC 7) で対応が追加されました。OpenMP のカタログ項目は、Platform Builder の Catalog Items View に表示されるツリーの、以下の位置にあります:
Core OS
Windows Embedded Compact
Application and Services Development
C Libraries and Runtimes
★ OpenMP Runtime
対応する Sysgen 変数は、SYSGEN_OPENMP です。これについては、リファレンスの次のページに書かれています:
C Libraries and Runtimes Catalog Items and Sysgen Variables (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/gg155191.aspx
ただし、デスクトップ版の Windows(Windows7 など)とは異なり、WEC 7 の OpenMP 対応では、C/C++ コンパイラの pragma に制限があり、以下のものは使えません。
omp dynamic
omp threadprivate
これについては、リファレンスの次のページをご覧ください:
C/C++ Libraries for Windows Embedded Compact (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/ee479345.aspx
さらに、WEC 7 の場合は、SMP 対応のボード、つまり、カーネルがマルチコアに対応していなければ、OpenMP を使用できないということが、先ほど述べたカタログ項目のリファレンスのページに書かれています。
マルチコアのプロセッサを搭載した WEC 7 対応のボードをお持ちの方は、試してみると面白いかも知れませんね。Visula C++ で OpenMP を使う場合は、以下のリファレンスページが参考になるでしょう:
OpenMP in Visual C++
http://msdn.microsoft.com/en-us/library/tt15eb9t(v=vs.90).aspx
2012/02/29
koga
Windows Embedded Compact 7 (WEC 7) で追加になった機能の中に、USB Audio のクラスドライバがあります。このドライバを組み込めば、オーディオ出力のハードウェアがないデバイスでも、USB Audio アダプタを使って音声出力できます。今回は、実際に動作確認してみた結果を紹介します。
■USB Audio アダプタ
今回動作確認に使ったのは、エアリア社製の「響音4」という製品です:
http://www.area-powers.jp/product/usb_product/product/kyo-on/u1sounds4.html
家電量販店の店頭で購入しましたが、¥2,000未満の価格で、お手頃です。購入して、Windows 7 の PC に接続してみたところ、パッケージの箱の説明どおり、付属ドライバのインストール無しで、動作しました。つまり、USB Audio の規格に合致しているので、Windows 7 の標準ドライバで動作します。Windpows 7 のデバイスマネージャでデバイス情報を見たところ、USB の Vendor ID が 0D8C ですから、台湾の C-Media Electronics 社のチップセットを使っているようです:
http://www.cmedia.com.tw/ProductsIndex.aspx
■USB Audio クラスドライバ
WEC 7 の USB Audio クラスドライバは、リファレンスに簡単な説明が載っています。
http://msdn.microsoft.com/en-us/library/gg158818.aspx (USB Host Driver Catalog Items and Sysgen Variables)
上のページを見ると、SYSGEN_USB_AUDIO という Sysgen 変数が定義されると組み込まれ、出力と入力をサポートしているようです。このドライバのソースコードは、WEC 7 のソースツリーで、以下の場所にあります。
%_WINCEROOT%/public/COMMON/oak/drivers/wavedev/wavedev2/usb/
このソースを見ると、wavemain.cpp にある Waveform Audio Driver の関数実装のうち、WAV_IOControl() において、入力デバイスに対するメッセージ(WIDM_*)の応答動作も実装されており、出力と入力をどちらもサポートしていることが分かります。Waveform Audio Driver については、リファレンスをご覧ください:
http://msdn.microsoft.com/en-us/library/ee485907.aspx (Waveform Audio Driver Reference)
■USB Audio 機能の組み込み
USB Audio クラスドライバを OS イメージに組み込むには、Platform Builder のカタログ項目ビュー(Catalog Items View)で、次のカタログ項目を選択して下さい:
<OS Design 名>
Core OS
Device Drivers
USB
USB Host
USB Class Drivers
★ USB Audio Class Driver
USB ポート(USB ホストのポート)を搭載した WEC 7 デバイスであれば、これにより、音声出力できるようになります。USB Audio クラスドライバのレジストリ設定ファイル(usbaudio.reg)を見ると分かりますが、Audio クラスを宣言する全ての USB デバイスに対して、このドライバを USB Host Client Driver として登録する設定になっており、USB Audio デバイスの Vendor ID や Product ID を意識する必要は、ありません。
弊社が BSP を提供している CPU ボードである、アットマークテクノ社の Armadillo-440 で試してみたところ、冒頭で紹介した USB Audio アダプタ(エアリア社の響音4)を使って、ヘッドフォンに音声出力できました。エクスプローラのディレクトリアイコンをダブルクリック(ダブルタップ)した際などに、効果音が出力されます。Armadillo-400 シリーズ用 BSP の、公開している評価版では、Armadillo-440 液晶モデルの音声出力インタフェースをサポートしていませんが、USB Audio アダプタと組み合わせれば、WEC 7 の USB Audio クラスドライバを使って音声出力できるというわけです。
さらに、DirectShow と Windows Music Player、および、MP3 と WMA のコーデックを OS イメージへ組み込むことにより、MP3 や WMA の音楽ファイルを再生できることも確認できました。
以上、WEC 7 で追加された USB Audio クラスドライバの紹介です。興味のある方は、ぜひ試してみて下さい。
2012/02/12
koga
前回のエントリで、WinCE/WEC の「リモートディスプレイアプリケーション」のソースディレクトリについて述べました。
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/
の配下には、前回のエントリで紹介した CERDisp と CERHost の他に、CERDRV というソースディレクトリがあるのです。今回は、前回触れなかった CERDRV について紹介します。
■CERDRV の組み込み
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/ の下にある CERDRV ディレクトリには、ddi_cer という、仮想フレームバッファ機能を提供するディスプレイドライバのソースファイルが収録されています。より正確には、CERDRV ディレクトリは、ddi_cer_lib.lib という static library のソースディレクトリであって、
%_WINCEROOT%/PUBLIC/COMMON/CESYSGEN/makefile
に記述された設定によって、ddi_cer.dll が生成されます。%_WINCEROOT%/PUBLIC/COMMON/ 配下のソースのビルドについては、2011/08/14 のエントリも、ご覧になってみて下さい。
ddi_cer を OS イメージに組み込むには、BSP_DISPLAY_CER という環境変数を設定して下さい。このことは、
%_WINCEROOT%/PUBLIC/COMMON/OAK/FILES/common.bib
に書かれています。common.bib の、CE_MODULES_DISPLAY に対する条件記述は、以下のようになっています。
; @CESYSGEN IF CE_MODULES_DISPLAY
; @CESYSGEN IF CE_MODULES_MULTIMON
multimon.dll $(_FLATRELEASEDIR)\multimon.dll NK SHK
; @CESYSGEN ENDIF CE_MODULES_MULTIMON
IF BSP_NODISPLAY !
;
; MGDI Display drivers
;
; To use driver set this env var
;
; ddi_flat.dll BSP_DISPLAY_FLAT
; ddi_3dr.dll BSP_DISPLAY_SMI3DR
; ddi_ragexl.dll BSP_DISPLAY_RAGEXL
; ddi_cer.dll BSP_DISPLAY_CER
; ddi_nop.dll BSP_DISPLAY_NOP
IF BSP_DISPLAY_NOP
ddi_nop.dll $(_FLATRELEASEDIR)\ddi_nop.dll NK SHK
ENDIF BSP_DISPLAY_NOP
IF BSP_DISPLAY_CER
ddi_cer.dll $(_FLATRELEASEDIR)\ddi_cer.dll NK SHK
ENDIF BSP_DISPLAY_CER
…(途中略)
ENDIF BSP_NODISPLAY !
; @CESYSGEN ENDIF CE_MODULES_DISPLAY
最後の方にある、IF BSP_DISPLAY_CER と ENDIF BSP_DISPLAY_CER で囲われた行を見て下さい。
また、
%_WINCEROOT%/PUBLIC/COMMON/OAK/FILES/common.reg
には、以下の行があります。
IF BSP_DISPLAY_CER
[HKEY_LOCAL_MACHINE\System\GDI\Drivers]
"Display"="ddi_cer.dll"
ENDIF
Platform Builder で環境変数 BSP_DISPLAY_CER を設定することにより、common.bib と common.reg の上記個所が有効になり、ddi_cer.dll が OS イメージに組み込まれる、というわけです。
なお、お使いの BSP にディスプレイドライバが付属している場合には、BSP 付属のディスプレイドライバを無効にして下さい。BSP 付属のディスプレイドライバを無効にする方法ですが、platform.reg の中に、レジストリキー [HKEY_LOCAL_MACHINE\System\GDI\Drivers] に対する設定行があれば、ddi_cer.dll を組み込む時は、その行をコメントアウトしたうえで、環境変数 BSP_DISPLAY_CER を設定して OS イメージをビルドして下さい。
ここで、platform.reg は、BSP 固有のレジストリ設定を記述するファイルであり、
%_WINCEROOT%/PLATFORM/<BSP 名>/FILES/
に配置されています。
■WinCE/WEC のディスプレイドライバ
ddi_cer について、「仮想フレームバッファ機能を提供するディスプレイドライバ」と書きましたが、これについて、もう少しだけ詳しく書きます。
まず、WinCE/WEC のディスプレイドライバの考え方については、WinCE 6.0 のリファレンスの、以下のページが参考になります:
Display Driver Development Concepts
http://msdn.microsoft.com/en-US/library/ee485877(v=WinEmbedded.60).aspx
WEC 7 のリファレンスのディスプレイドライバの章には、このページに相当する、考え方(concept)を説明したページは無いようです:
Display Drivers (Windows Embedded Compact 7)
http://msdn.microsoft.com/en-us/library/gg159569.aspx
WinCE 6.0 と WEC 7 では、ディスプレイドライバのアーキテクチャは変わっていません。ディスプレイドライバの役割は、おおざっぱにいえば、GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積し、ディスプレイへ出力することです。そして、仮想フレームバッファ機能というのは、RAM 上にフレームバッファ領域を割り当て、「GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積」するだけの機能のことです。
Linux の場合、同様の仮想フレームバッファ機能として、X Window System の Xvfb (X virtual framebuffer) や、Linux framebuffer (fbdev) があるようです:
Xvfb
http://en.wikipedia.org/wiki/Xvfb
Linux framebuffer
http://en.wikipedia.org/wiki/Linux_framebuffer
さて、ddi_cer による仮想フレームバッファ機能と、前回紹介した「リモートディスプレイアプリケーション」(CERDisp と CERHost)を組み合わせれば、ディスプレイを持たない「ヘッドレス」のデバイスで、GUI 画面によるリモート操作を行うことが可能です。ディスプレイ付きのデバイスの場合は、CERDisp と CERHost により、デバイスに接続されたディスプレイと PC のディスプレイの両方に、WinCE の画面が表示されます。これに対し、ヘッドレスのデバイスの場合(または、ディスプレイ付きのデバイスのディスプレイドライバを無効にして、ddi_cer を組み込んだ場合)は、PC のディスプレイにだけ WinCE の画面が表示される、というわけです。
x86 ベースのボード/デバイスを除く、組み込み機器用プロセッサを用いたデバイスの場合、フレームバッファ領域は、専用の VRAM ではなく、RAM 上に確保するのが一般的です。従って、それらのデバイスについて考えると、実は、ddi_cer と通常のディスプレイドライバの違いは、フレームバッファの内容をディスプレイへ出力するかどうかだけなのです。
ただし、ddi_cer には、CERDisp と同じ機能も実装されていますので、フレームバッファ内容をリモート表示するディスプレイドライバ、という見方もできます。WinCE 6.0 のリファレンスでは、ddi_cer は “Windows Embedded CE remote graphics adapter.” と説明されています:
http://msdn.microsoft.com/en-us/library/ee482180(v=winembedded.60).aspx (Common Windows Embedded CE Modules)
ddi_cer に実装されている CERDisp 相当の機能は、レジストリ設定でオフにすることができます。ddi_cer のレジストリ項目については、
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/CERDRV/
に入っている cerdrv.reg を見て下さい。cerdrv.reg には、二つのレジストリキーが記述されています。[HKEY_LOCAL_MACHINE\System\GDI\Drivers] と [HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] です。このうち、[HKEY_LOCAL_MACHINE\System\GDI\Drivers] の方は、上述した common.reg に記述されているものと同じです。cerdrv.reg の内容は、ビルド時に OS イメージに反映されませんので、[HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] キーの内容については、platform.reg か project.reg にコピーするのがよいでしょう。
ddi_cer の、CERDisp 相当の機能をオフにするには、[HKEY_LOCAL_MACHINE\Drivers\Display\DDI_CER] キーの NoServer サブキーを 1 に設定して下さい。ddi_cer は、NoServer の値が 1 に設定されていると、CERDisp と同様の動作を行うサーバースレッドを始動せず、仮想フレームバッファ動作のみを行います。デフォルトでは、ddi_cer の CERDisp 相当の機能が有効なので、CERDisp を実行せず、PC 上で CERHost を実行するだけで接続することができます。NoServer を 1 に設定した場合は、CERDisp も実行する必要があります。
ddi_cer の CERDsip 相当の機能と CERDisp との主な違いは、次の通りです:
・ddi_cer は、ディスプレイドライバとしてフレームバッファを自分が持っているため、画面内容をクライアントへ転送する際は、フレームバッファを直接アクセスする。
CERDisp は、BitBlt() を使って画面内容をキャプチャしたのち、キャプチャした内容をクライアントへ転送する。
・ddi_cer は、カーネルモードドライバなので、TCP/IP 通信処理は、CERDisp の場合とは異なり、カーネルモジュール用の WinSock DLL がロードされる。
ここで、ddi_cer と CERDisp は、WinSock ライブラリとして、どちらも winsock.lib をリンクしています。従って、CERDisp に対しては、winsock.dll がロードされ、ddi_cer に対しては、k.winsock.dll がロードされます。カーネルモジュール用の DLL については、2008/07/22 に書いたエントリ(「カーネルからWinSock」)も、ご覧になってみて下さい。
なお、2007/07/22 のエントリでは、WinSock の DLL は、ws2.dll と k.ws2.dll だと書いており、ddi_cer と CERDisp がロードする DLL とは違っています。実は、winsock.dll(および k.winsock.dll)の方は、古い版の WinSock で、ws2.dll は、WinCE .NET 4.2 から導入された新しい版です。真相は分かりませんが、ddi_cer が k.winsock.dll を使うようになっているのは、2007/07/22 のエントリに書いた、k.ws2.dll に関する制限を回避するためなのかも知れません。
(あるいは、単に、ddi_cer も CERDsip も、WinCE .NET 4.2 より前から付属していて、[k.]ws2.dll を使うように改訂されていない、ということなのかも知れませんが。)
■通常のディスプレイドライバとの比較
興味のある人は、ddi_cer と、WinCE 6.0 のデバイスエミュレータのディスプレイドライバのソースを読み比べてみると、面白いんじゃないかと思います。WinCE 6.0 のデバイスエミュレータは、Samsung の S3C2410 という、ARM9 コアのプロセッサのリファレンスボードをエミュレーションしています。S3C2410 は、LCD コントローラを内蔵していますので、デバイスエミュレータのディスプレイドライバは、S3C2410 の LCD コントローラを制御する処理が実装されています。このドライバのソースコードを収録したディレクトリは、
%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/SRC/DRIVERS/DISPLAY/LCD
です。また、S3C2410 のデータシートは、
http://www.alldatasheet.com/view.jsp?Searchword=s3c2410
などからダウンロードできます。
ddi_cer と、WinCE 6.0 のデバイスエミュレータのディスプレイドライバの大きな違いは、次の通りです:
・ddi_cer は、フレームバッファ領域を確保する際、C++ の new 演算子を使って確保する。従って、連続した物理メモリ領域は、確保しない。
(※より正確には、GPE ライブラリの GPESurf クラスのインスタンスを生成し、GPESurf のコンストラクタによってバッファを確保します。)
デバイスエミュレータのディスプレイドライバは、config.bib の設定により、フレームバッファとして予約した領域を使用する。
・ddi_cer は、ディスプレイデバイスを制御しない。GWES の描画エンジン機能が生成したレンダリング結果を、フレームバッファ上で合成・蓄積するのみ。ただし、レジストリ設定で NoServer が 0 に設定されているか、または何も設定されていない場合は、クライアントに対してフレームバッファ内容を定期的に転送し、「リモートディスプレイ」動作を行う。
デバイスエミュレータのディスプレイドライバは、プロセッサ(S3C2410)の LCD コントローラのレジスタを、カーネル仮想アドレス空間にマップして、必要な制御動作を行う。LCD コントローラの制御動作の一部として、フレームバッファの物理アドレスを、DMA 転送対象領域として LCD コントローラに設定する。
つまり、異なるのは、ディスプレイ出力用のコントローラ(プロセッサ内蔵の LCD コントローラなど)を制御するかどうか、および、フレームバッファ領域として、連続した物理メモリ領域を確保するかどうかです。ディスプレイ出力用のコントローラを制御する、通常のディスプレイドライバの場合には、フレームバッファ全体を一括して DMA 転送できるように、フレームバッファを、連続した物理メモリ領域に確保する必要があります。
なお、デバイスエミュレータのディスプレイドライバでは、フレームバッファの物理アドレスを LCD コントローラに設定する処理は、行っていません。この処理は、OAL の初期化処理として、以下のソースファイルで実装されています:
%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/src/oal/oallib/init.c
init.c の中にある、OEMInit() から呼び出される InitDisplay() で、LCD コントローラの初期化を行い、その際に、フレームバッファの物理アドレスを設定しています。
LCD コントローラの初期化動作を OAL で行う実装になっているのは、カーネル起動時にスタートアップスクリーンを表示するためのようです。ここで、LCD コントローラに設定するフレームバッファのアドレスは、物理アドレスですが、ディスプレイドライバがフレームバッファ領域としてアクセスするアドレスは、カーネル仮想アドレスであることに注意して下さい。RAM の同じ領域であっても、ハードウェア(LCD コントローラ)は、物理アドレスでしかアクセスできず、逆に、カーネルモジュールは、物理アドレスをカーネル仮想アドレス空間にマップしたカーネル仮想アドレスでしかアクセスできません。
余談になりますが、連続した物理メモリ領域をフレームバッファに割り当てる際に、必ずしも config.bib で予約する必要は、ありません。AllocPhysMem() を使って動的に割り当てて使用することも可能です。ただし、AllocPhysMem() では、割り当て先の物理アドレスを指定できないので、ブートローダや OAL がスタートアップスクリーンを表示する際に使う領域と同じ領域を割り当てることが、できません。従って、フレームバッファとして使用する領域のサイズを、仕様として固定してしまえるのであれば、config.bib の設定で割り当てる方が、どちらかといえば簡単になるでしょう。
2012/01/04
koga
■「リモートディスプレイアプリケーション」
どちらかといえば、デバッグ用の機能になりますが、WinCE/WEC には、ネットワーク経由で画面をリモート表示し、操作する機能が標準で付属しています。CERDisp というユーティリティが、そうです。
CERDisp を OS イメージへ追加するには、次のカタログ項目を選択して下さい:
コア OS
CEBASE
コア OS サービス
デバッグツール
★ リモートディスプレイアプリケーション
Platform Builder のカタログ項目ビューには、「リモートディスプレイアプリケーション」の下に、次の二つの項目があります:
CE リモートディスプレイアプリケーション
CE リモートディスプレイユーティリティ
「CE リモートディスプレイアプリケーション」が、CERDisp であり、リモート操作する対象の WinCE/WEC 上で動作します。「CE リモートディスプレイユーティリティ」は、リモート操作を行うホスト(開発用の PC など)で動かすユーティリティで、CERHost という名前です。
CERHost の方は、PC 用にあらかじめビルドされたものが、
%_WINCEROOT%/PUBLIC/COMMON/OAK/BIN/I386/
に入っています。このフォルダに入っている cerhost.exe を PC で起動しておき、その後、リモート操作したい WinCE/WEC 上で、cerdisp.exe を起動して、両者を接続すると、WinCE/WEC の画面を PC に表示できます。
CERDisp の使い方については、Nicolas BESSON という人の Blog の 2007/12/28 のエントリでも説明されています:
Enable Remote Display Application – CERDisp
http://nicolasbesson.blogspot.com/2007/12/enable-remote-display-application.html
また、Mike Hall(Windows Embedded プロダクトグループのテクニカルプロダクトマネージャ)の Blog の 2007/01/04 のエントリでは、”AutoLaunch” というツールと一緒に、CERDisp が紹介されています:
CE 6.0: Booting processes with Command Line Options.
http://blogs.msdn.com/b/mikehall/archive/2007/01/04/ce-6-0-booting-processes-with-command-line-options.aspx
このページで紹介されている AutoLaunch は、レジストリの [HKEY_LOCAL_MACHINE\init] キー下に登録して WinCE/WEC の起動直後に起動されるようになっています(そのための .reg ファイルが、ソースファイルに同梱されています)。AutoLaunch は、起動すると、レジストリの [HKEY_LOCAL_MACHINE\Startup] キー下に登録された内容を見て、登録されたアプリケーションを起動します。
AutoLaunch の便利な点は、[HKEY_LOCAL_MACHINE\Startup] キー下に登録されたアプリケーションを起動する前に、WinCE/WEC に IP アドレスが設定されるまで待つ点です。つまり、WinCE/WEC デバイスのネットワークインタフェース(Ethernet コントローラなど)と TCP/IP スタックの初期化動作が完了し、さらに、DHCP が有効な場合は DHCP サーバから IP アドレスの割り当てを受けて IP アドレスが設定されるまでの間、AutoLaunch は、登録されたアプリケーションを起動しません。従って、WinCE/WEC デバイスの IP アドレスが確定する前にネットワークアプリケーションが自動起動されてしまい、そのアプリケーションがエラーを起こしてしまう、という心配がないのです。
また、[HKEY_LOCAL_MACHINE\init] キーに登録する場合とは違い、登録するアプリケーションのコマンド引数を指定できるのも、AutoLaunch の便利な点です。[HKEY_LOCAL_MACHINE\init] キーの場合、起動されたアプリケーションが SignalStarted() を呼び出す際の引数をコマンドライン引数として渡す仕組みになっているため、コマンド引数を指定することが出来ません。詳細については、上記の Mike Hall の Blog を読んでみて下さい。
■CERDisp と CERHost の動作の仕組み
さて、以下では、CERDisp と CEHost の動作の仕組みについて、簡単に説明します。これら二つのアプリケーションのソースコードは、
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/
配下にありますので、興味のある人は、ソースコードも読んでみて下さい。
上で紹介した Nicolas BESSON の Blog では、CERHost がサーバ、CERDisp がクライアントという風に説明されていますが、実際は逆で、CERDisp の方がサーバとして動作します。CERDisp と CERHost は、それぞれ、次のように動作します。
・CERDisp の動作
- 起動すると、スクリーンサイズと OS バージョンなどのデバイス情報を取得し、クライアント(CERHost)との接続用スレッドを始動する。
- クライアントとの接続用スレッドは、TCP ソケットを生成して、ポート987番に対し bind() & listen() を実行した後、ブロードキャスト用のスレッドを始動し、その後、accept() を呼び出して TCP 接続されるのを待つ。
- ブロードキャスト用のスレッドは、UDP ソケットを生成して、同じくポート987に対し、デバイス情報と自身の IP アドレスの対を5秒間隔で繰り返しブロードキャストする。
ブロードキャスト用のスレッドは、クライアントと接続したことを検出したら、自発終了する。
- クライアントとの接続用スレッドは、accept() の呼び出しが完了してクライアントと接続したら、ブロードキャスト用のスレッドの終了を待つ。ブロードキャスト用のスレッドが終了したら、クライアントからのイベント通知を受信するスレッドを始動し、その後、画面内容をクライアントへ転送するループ動作を実行する。
画面内容をクライアントへ転送するループ動作では、レジストリで設定された間隔(デフォルトは、100ミリ秒)で画面内容をキャプチャして、キャプチャした画像データをクライアントに送信する。ここで、送信する画像データは、BitBlt() でキャプチャした画面内容を、行単位でランレングス圧縮したもの。
- クライアントからのイベント通知受信スレッドは、画面内容を送信するのと同じ TCP ソケットに対して、クライアントから送られたイベントを受信して処理するループ動作を実行する。クライアントから送られたイベントは、マウスイベントであれば、mouse_event() を呼び出し、キーボードイベントであれば keybd_event() を呼び出すことにより、システムのイベントキューへ投入する。
・CERHost の動作
- 起動すると、UDP ソケットを生成して、ポート987番に対するブロードキャストメッセージを監視し、検出したサーバ(CERDisp)のリストを構築・管理する。
- 検出した CERDisp のうち、[File] → [Connect...] メニューのダイアログで指定された IP アドレスのものに TCP 接続し、通知されたスクリーンサイズと同じサイズになるよう、自身のウィンドウの(クライアント矩形の)サイズを調節する。
- 以降は、TCP ソケットから画面内容を受信して、自身のウィンドウに表示する動作と、マウスイベントおよびキーボードイベントを TCP ソケットで送信する動作を繰り返す。
このようにして、CERDisp と CERHost が連携し、WinCE/WEC のリモート操作を実現します。WinCE/WEC には、RDP (Remote Desktop Protocol) のサーバ機能は付属していませんが、CERDisp と CERHost を使えば、似たことができるというわけです。
Mike Hall が彼の Blog で書いているように、この機能は、WinCE/WEC のリファレンスボードや開発ボードを使う際、ボードにモニタや液晶パネルを繋がなくても、開発用の PC だけで、WinCE/WEC と開発環境を同時に操作する際に便利でしょう。最近のボードは、液晶パネルが付属しているものが珍しくありませんが、それでも、開発用の PC だけで操作できるというのは、便利な場合が少なくないと思います。
ところで、注意深い人は、
%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CERDISP/
の下に、CERDisp と CERHost の他にもソースディレクトリがあることに気づいたのではないかと思います。次回は、それについて書きます。
2011/12/29
koga
前回 は、OS をカスタマイズせずに、レジストリ設定や API 呼び出しだけでタスクバーの非表示対応(アプリケーションのフルスクリーン表示対応)を行う方法について、説明しました。今回は、タスクバーを表示しないシェルを組み込む方法について説明します。
■Taskman Shell
WinCE 6.0(および WEC 7)には、タスクバーを表示しないカスタムシェルのソースコードが付属しており、WinCE 6.0 のリファレンスでも説明されています:
http://msdn.microsoft.com/en-US/library/ee502244(v=WinEmbedded.60).aspx (Including the Taskman Shell)
標準シェルを使用する必要がなく、専用のフルスクリーン表示アプリケーションを搭載するデバイスを開発する場合には、標準シェルの代わりに、この Taskman Shell を OS イメージに組み込むのが、簡単で良いのではないかと思います。
ただし、上のページに書かれている、Taskman Shell を組み込む方法は、実際には使えません。説明が間違っているように思われます。そこで、Taskman Shell を組み込むやり方について、以下に一例を述べます。
■Taskman Shell の組み込み
上記の、WinCE 6.0 のリファレンスページでは、Taskman Shell を OS イメージに組み込む場合、’__SYSGEN_TASKMAN’ という環境変数の値を 1 に設定すればよいと書かれています。しかし、その設定を行っても、Taskman Shell は OS イメージに組み込まれず、それどころか、Tasklman Shell のビルドすら行われません。WinCE のソースツリー配下のファイルを ‘__SYSGEN_TASKMAN’ で検索しても、この環境変数を参照している個所は見当たりませんし、さらに、Taskman Shell のソースコードの親ディレクトリ(%_WINCEROOT%/Public/Wceshellfe/Oak/)にある dir ファイルを見ると、Taskman Shell のディレクトリ(taskman)が記載されておらず、ビルド対象から外れていることが分かります。
では、どうすれば、OS Design をビルドした時に、Taskman Shell が OS イメージへ組み込まれるようになるのでしょうか?以下に、Taskman Shell を OS イメージへ組み込む手順の一例を述べます。
1.) taskman/ ディレクトリを、ビルド対象に含める。
VS 2005/Platform Builder の「ソリューションエクスプローラー」で、TASKMAN のアイコンを右クリックしてポップアップメニューを開き、[ビルドに含める] を選択します。TASKMAN は、ソリューションエクスプローラーで、次の階層にあります:
C:/WINCE600
PUBLIC
wceshellfe
OAK
TASKMAN ★
注:確認していませんが、この(1)(「ビルドに含める」)は、必要ないかも知れません。
2.) %_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/ にある wceshellfe.bat を編集して、次の行を追加する:
REM for Taskamn shell
if "%__NO_SYSGEN_TASKMAN%"=="1" goto noTaskmanShell
rem Turn off StandardShell
set __SYSGEN_EXPLORER=
rem add TaskmanShell to WCESHELL module
set WCESHELLFE_MODULES=%WCESHELLFE_MODULES% taskman
:noTaskmanShell
上の行を追加する位置は、wceshellfe.bat の440行付近にある、以下の行の直前にして下さい。
goto :EOF
:Not_Pass1
3.) OS Design をリビルドする。
上記の手順により、OS イメージ(nk.bin)に taskman.exe が追加され、さらに、レジストリの [HKEY_LOCAL_MACHINE\init] キーの Launch50 の値が "taskman.exe" になります。標準シェル(explorer.exe)は OS イメージには収録されず、標準シェルを Taskman Shell が置き換えられます。ここで、標準シェルが OS イメージに収録されないのは、上の (2) で wceshellfe.bat に追加した行の中の
set __SYSGEN_EXPLORER=
という行の働きによるものです。一方、Taskman Shell が OS イメージに収録されるのは、
set WCESHELLFE_MODULES=%WCESHELLFE_MODULES% taskman
という行の働きによるものです。この行の意味については、2011/08/14 に書いた「CESYSGEN 条件文での ‘Component’ 名」を併せ読んでみて下さい。この行の働きにより、taskman がビルドターゲットの依存ファイル/ターゲットの一部となり、ビルドされるようになるというわけです。さらに、 %_WINCEROOT%/Public/Wceshellfe/Oak/FILES/ にある wceshellfe.bib と wceshellfe.reg の中の taskman.exe に関する記述が、上の行の設定により有効になる、というわけです。以下に、wceshellfe.bib と wceshellfe.reg の当該個所を引用します:
wceshellfe.bib
; @CESYSGEN IF WCESHELLFE_MODULES_TASKMAN
taskman.exe $(_FLATRELEASEDIR)\taskman.exe NK SH
; @CESYSGEN ENDIF
wceshellfe.reg
; Choose only *one* of the taskman or explorer components in your cesysgen.bat
; @CESYSGEN IF WCESHELLFE_MODULES_TASKMAN
[HKEY_LOCAL_MACHINE\init]
"Launch50"="taskman.exe"
"Depend50"=hex:14,00, 1e,00
; @CESYSGEN ENDIF
Taskman Shell を OS イメージに収録せず、標準シェルに戻す場合は、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/wceshellfe.bat に追加した行を削って元に戻すか、または、'__NO_SYSGEN_TASKMAN' という環境変数の値を 1 に設定して OS Design をビルドして下さい(*)。
* 上述した例では、'__NO_SYSGEN_TASKMAN' という環境変数を設定しない場合、Taskman Shell が組み込まれます。つまり、デフォルトで Taskman Shell が組み込まれる設定になってしまいます。どちらかといえば、デフォルトでは標準シェルが組み込まれるように設定できるのが良いと思います。つまり、冒頭で述べた WinCE 6.0 のリファレンスページに書かれているように、'__SYSGEN_TASKMAN' という環境変数を 1 に設定した時にだけ、Taskman Shell が組み込まれるようにできる方が良いでしょう。しかし、試してみたところ、wceshellfe.bat に追加する行の中で '__SYSGEN_TASKMAN' という環境変数を参照して条件分岐するようにしても、期待通りに動作しませんでした。簡単に動きを追ってみたところ、Platform Builder の環境変数設定で __SYSGEN_TASKMAN の値を設定しても、wceshellfe.bat の当該個所が実行される時点で、__SYSGEN_TASKMAN の値が空になってしまっていることが原因でした。詳細は不明ですが、WinCE のビルドシステムにおいて、'__SYSGEN' という接頭辞の環境変数に対しては、ビルド処理を開始する前に、その値を空にする内部処理を実行しているのかも知れません。
おそらく、今回述べた手順例よりも、もっと良いやり方があるのではないかと思います。WinCE のビルドシステムに対し、ビルド内容をカスタマイズする「フック」の仕組みが提供されていますから、その仕組みを使うことで、もっとエレガントな対応ができるのかも知れません。興味のある方は、ご自分で調べて、追ってみて下さい。
2011/08/18
koga
WinCE 6.0 用の UVC (USB Video Class) ドライバを、Microsoft Download Center から入手できます。次のページです:
Windows Embedded CE 6.0 USB Camera Driver
http://www.microsoft.com/download/en/details.aspx?id=19512
このドライバは、WinCE 6.0 の Camera Driver Interface に対応した UVC ドライバであり、このドライバを OS イメージへ組み込むことにより、DirectShow の Video Capture Filter を使って UVC カメラから映像や静止画をキャプチャできるようになります。Camera Driver Interface と Video Capture Filter については、リファレンスの次のページをご覧ください。
http://msdn.microsoft.com/en-US/library/ee486313(v=WinEmbedded.60).aspx (Camera Driver Interface)
http://msdn.microsoft.com/en-US/library/ee494400(v=WinEmbedded.60).aspx (Video Capture Filter)
さて、上記の UVC ドライバをインストールしてビルドした際、もしかすると、次のようなコンパイルエラーが起きて、ビルドに失敗するかも知れません。
error C3892: ‘g_wszPinDeviceNames’ : you cannot assign to a variable that is const
もし、このようなコンパイルエラーが起きた場合には、WinCE 6.0 のアップデータを適用して下さい。WinCE 6.0 R3 までをインストールしていらっしゃる場合、2010/12/31 までのアップデータを集積した Cumulative Product Update Rollup Package を適用して下さい:
Windows Embedded CE 6.0 Cumulative Product Update Rollup Package (through 12/31/2010)
http://www.microsoft.com/download/en/details.aspx?id=1127
この集積アップデートのうち、上に示したコンパイルエラーに関係するのは、090930_KBSOURCE のソースコード修正に含まれる
public\common\oak\drivers\capture\camera\layered\inc\camerapddprops.h
です。修正前の camerapddprops.h では、変数 g_wszPinDeviceNames の宣言が const 修飾されており、そのために、コンパイルエラーが発生します。
WinCE 6.0 R3 のインストールが未だであれば、このアップデータを適用する前に、R3 までをインストールして下さい。このページの “System requirements” に記載されていますが、WinCE 6.0 R3 までのインストール順序は、次の通りです:
1. Visual Studio 2005
2. Visual Studio 2005 Service Pack 1
3. Visual Studio 2005 Service Pack 1 Update for Windows Vista (※Windows Vista および、それ以降の OS をお使いの場合)
4. Windows Embedded CE 6.0 Platform Builder
5. Windows Embedded CE 6.0 SP1
6. Windows Embedded CE 6.0 R2
7. Windows Embedded CE 6.0 R3
なお、僕は2009年2月に、この UVC ドライバを一度評価したことがあるのですが、その時の環境では(WinCE 6.0 R2 だったはずです)、上記のコンパイルエラーは、起きていませんでした。なぜ、WinCE 6.0 R3 でコンパイルエラーが起きるようになったのか、原因は不明です。ここでは、その解決策(アップデータの適用)だけを記しておきます。
ところで、Video Capture Filter を使ったキャプチャ処理のやり方については、
%_WINCEROOT%/PRIVATE/TEST/MULTIMEDIA/DIRECTX/DSHOW/CAMERA/
配下のソースコードが参考になるでしょう。ただし、このソースコードは、PRIVATE/ ディレクトリ配下の Shared Source code です。使用例として読み、参考にするにとどまらず、もしソースコードの再利用をお考えの場合には、ライセンス条件をご確認下さい。
2011/08/03
koga
2011/02/21 に書いたエントリ(「レジストリ変更内容の永続化(2/2)」では、Hive-Based と RAM-Based の二種類があるレジストリのうち、Hive-Based のレジストリを永続化する仕組みについて説明しました。今回は、RAM-Based のレジストリの永続化について、Hive-Based のレジストリとの比較を交えて書きます。
まず、RAM-Based のレジストリを永続化する方法については、リファレンスの次のページに記載されています:
http://msdn.microsoft.com/en-us/library/ee489958(v=WinEmbedded.60).aspx (Persisting Data with the RAM-Based Registry)
http://msdn.microsoft.com/en-US/library/ee490769(v=WinEmbedded.60).aspx (Data Persistence with the RAM-based Registry Using the OAL)
実は、RAM-Based のレジストリを永続化する方法は二つあり、上の二つのページで、その各々について説明されています。それぞれの概要は、次の通りです。
(1) Oemregistry.dll という DLL を組み込む。この DLL で、ReadRegData(), WriteRegData(), RegistryOperation() を実装する。
(2) OAL (OEM Adaptation Layer) に、WriteRegistryToOEM() と ReadRegistryFromOEM() の実装を追加する。
これら二つの方法のうち、(1) は、永続記憶域としてファイルシステムを使う場合に適しています。一方、(2) は、NOR Flash の特定領域を割り当てるなどして、ファイルシステムを介さず、生の(raw)ストレージメディアにレジストリ内容を保存する場合に適しています。
どちらの場合も、レジストリの変更内容を永続記憶域に保存するためには、RegFlushKey() の呼び出しが必要です。Hive-Based のレジストリとは異なり、RAM-Based のレジストリでは、RegFlushKey() を呼び出さない限り、レジストリの変更内容は保存されません。
さて、上記 (1) と (2) のどちらも、WinCE 6.0 ではサンプル実装が提供されています。(1) は、リファレンスのページでも紹介されているように、%_WINCEROOT%/Public/Common/Oak/Drivers/FSD/Oemfs/ がサンプル実装です。このサンプル(Oemfs)では、永続記憶域として、リリースディレクトリファイルシステムを使用します。つまり、開発ホスト機の Flat Release Directory に、RAM-Based レジストリの内容がファイルとして保存されます。当然ですが、使用する場合は、OS Design のカタログ項目で、「リリースディレクトリファイルシステム」を選択して OS イメージをビルドしなければいけません。また、サンプルのままでは、ビルドの target name が Oemfs になっており、’Oemregistry’ とは違います。さらに、他の Public/ ディレクトリ配下のコンポーネントと同様、target type が LIBRARY になっていて、一旦 static library (.lib) としてビルドされた後、sysgen 変数でビルド対象に設定された場合にのみ、DLL (.dll) が生成されるように sources ファイルが記述されています。従って、sources ファイルを作り直し、target name を Oemregistry、target type を DYNLINK にする必要があります。
(2) のサンプル実装は、%_WINCEROOT%/Platform/Common/Src/Common/PerReg/ です。このソースから、oal_perreg.lib という static lilbrary がビルドされます。NOR Flash を搭載したボードの BSP で、NOR Flash に対する OALFlashErase() と OALFlashWrite() が実装されている場合には、oal_perreg.lib を oal.exe のリンクライブラリに追加して、PerReg の初期化関数を OAL の初期化時に呼び出せば(※OEMInit() の末尾がよいでしょう)、そのまま使用することができます。そのようにして oal_perreg.lib を組み込むだけで、NOR Flash の特定領域を使った永続化が、RAM-Based レジストリに対して可能になります。PerReg の初期化関数は、%_WINCEROOT%/Platform/Common/Src/INC/oal_perreg.h で宣言されている OALPerRegInit() です。
ところで、ここまで見てきて、レジストリを永続化する方法には、次の三つがあることが分かりました:
(a) Hive-Based のレジストリを組込み、永続化の設定を行う。
(b) RAM-Based のレジストリと Oemregistry.dll を組み込む(※配置先ファイルシステムは、適切に変更する必要あり)。
(c) RAM-Based のレジストリを組込み、OAL に WriteRegistryToOEM() と ReadRegistryFromOEM() の実装を追加する。
どれが最も良いのでしょうか?結論としては、WinCE 6.0 を動かすハードウェア次第であり、ケースバイケースだと言えます。ただし、NOR Flash を搭載したハードウェアであり、かつ、レジストリの書き換えが頻繁には起こらず、あらかじめ意図した変更内容だけを永続記憶域に保存したい場合には、(c) の方法が最も良いのではないかと思います。
なお、WinCE 6.0 のリファレンスを見ると、次のように書かれています:
”The preferred method for persisting registry data is to use the hive-based registry.”
(Persisting Data with the RAM-Based Registry)
”The preferred method for persisting data with the RAM-based registry is to include the OEMRegistry.dll sample file in your OS image.”
(Data Persistence with the RAM-based Registry Using the OAL)
これを見ると、最も preferred なのが (a)、その次が (b) で、最後が (c) という順番です。しかし、ここで言う “preferred” の順番は、堅牢性の観点から推奨されるものではなく、より一般的に使える方策の順、および、OS のカスタマイズが必要な度合いが小さい順であると思います。以下に、(a)~(b) の各々について、長所・短所を述べます。
(a) Hive-Based のレジストリ
長所:OS のカスタマイズは最小限。「レジストリ変更内容の永続化(2/2)」 で説明したように、OS イメージに組み込むレジストリの設定だけで対応できる。
短所:レジストリの Hive を配置するファイルシステムを Bootable に設定しなければならないため、そのファイルシステムが破損するなどしてマウントできなくなった場合、ブートの第2フェーズへ遷移できず、第1フェーズの完了待ちで WinCE カーネルの起動が停止してしまう。
その他:レジストリの Hive ファイルが、WinCE によってオープンされたままとなる。このため、WinCE の起動後にバックアップファイルで上書きするといったことができない。また、RegFlushKey() を呼び出さなくても、暗黙裡に Hive ファイルが更新されるため(※試してみたところでは、起動のたびに、ファイルが更新されます)、レジストリ内容の永続記憶域への書き出しを、意図したタイミングでしか行いたくない場合には、不向き。
(b) RAM-Based のレジストリ + Oemregistry.dll
長所:Oemregistry.dll を組み込むだけで対応でき、OAL の改変が不要。レジストリ内容を、ファイルシステム上にファイルとして保存するが、そのファイルシステムは Bootable に設定する必要がない。そのため、保存先のファイルシステムが破損しても、WinCE は起動する。
短所:レジストリ内容をファイルとして保存するため、レジストリ内容を隠したい場合には不向き。
(c) RAM-Based のレジストリ + OAL に WriteRegistryToOEM() と ReadRegistryFromOEM() を追加
長所:レジストリ内容は、ファイルシステムを介さずに保存できるため、ユーザから隠して保護するのが容易。また、NOR Flash を搭載したハードウェアであれば、NOR Flash 用のサンプル実装(PerReg)をそのまま使える。
短所:OAL の改変が必要。NOR Flash を搭載していないハードウェアの場合には、WriteRegistryToOEM() と ReadRegistryFromOEM() の実装は、それなりに手間がかかる。
どうでしょうか?それぞれの長所と短所は、相対的なものですから、どれかが絶対的に優れているとはいえず、ケースバイケースだと思います。
たとえば、ハードディスク(や SSD)を搭載し、そこに OS イメージも配置する設計のデバイス(ハードウェア)であれば、(a) が最も適しているでしょう。あるいは、上述したように、NOR Flash を搭載していて、レジストリの更新内容を特定の場合にしか保存しなくてよい仕様のデバイスであれば、最も適しているのは (c) だと思います。また、CF カードなどのストレージメディアに Bootable なファイルシステムを置くデバイスで、Bootable なファイルシステムへの書き込みを極力抑えたい、という場合には、(b) が適しているのではないかと思います。
※2011/08/13 追記
RAM-Based のレジストリを永続化した場合、OS イメージを書き換える際に問題となることがあります。新しい OS イメージで、レジストリ項目の追加や削除など、レジストリ内容の変更を行っていても、OS イメージを書き換えた際に、その変更が反映されない、という問題です。これについて、「RAM-Based レジストリの永続化に関する補足」に書きました。
2011/08/02
koga
「なんだ、それ?dir ファイルにサブディレクトリを書くだけでしょ。」というのが、たぶん普通の反応なんだと思います。たしかに、Windows Embedded CE の流儀では、それが正しいやり方です。
以下で説明するのは、WinCE の流儀には忠実でないやり方で、PlatformBuilder の OSDesign サブプロジェクトを作る方法です。この方法は、クロスプラットフォームのオープンソースのライブラリを WinCE で利用したり、あるいは、将来的なクロスプラットフォームでの利用も想定して、可搬性の高いコードやプロジェクトを作る場合に、役立つのではないかと思います。
さて、「方法」と書きましたが、実際は、そんなに大袈裟なものでは、ありません。単に、sources ファイルにターゲット(オブジェクトファイル)の生成規則を追加するだけです。生成規則は、
<WINCE600_Root>/PUBLIC/COMMON/OAK/MISC/
にある makefile.def ファイルに記述されています。たとえば、makefile を配置したディレクトリの、一つ上のディレクトリに存在する .cpp ファイルに対するオブジェクトファイルの生成規則は、次の通りです:
{..\}.cpp{$(_OBJDIR)\}.obj:
@echo BUILD_MARKER:CPP_COMPILE_START Compiling $<
@type <<
$(ECHO_CXX_MSG)
<<NOKEEP
@$(CXXCOMPILER) @<< $(CONLY_FLAGS) -Fo$@
$(C_COMMAND_LINE_OPTIONS: =
) $(MAKEDIR)\$<
<<NOKEEP
@echo BUILD_MARKER:CPP_COMPILE_END
この生成規則があるため、次のようなディレクトリ構成で PlatformBuilder サブプロジェクトを作成しても、正しくビルドされるのです。
MySubProj/
myprojsrc.cpp
myprojsrc2.cpp
WCEPB/
makefile
MySubProj.pbpxml
postlink.bat
prelink.bat
ProjSysgen.bat
sources
上の例では、MySubProj/ というディレクトリが、PlatformBuilder サブプロジェクトのソースディレクトリです。この中の、WCEPB/ というサブディレクトリに PlatformBuilder のプロジェクトファイルを配置しています。この例では、sources ファイルでは、SOURCES マクロが次のように定義されているはずです:
SOURCES= \
..\myprojsrc.cpp \
..\myprojsrc2.cpp \
ここで、上の例とは異なり、ソースファイル用のサブディレクトリがある場合には、makefile.def ファイルに記述された生成規則だけではビルドできません。次のようなディレクトリ構成の場合です。
MySubProj2/
myprojsrc.cpp
myprojsrc2.cpp
SubDir/
subdirsrc.cpp
WCEPB/
…(※上の例と同じ)
この場合、sources ファイルで SOURCES マクロを次のように書いても、ビルドエラーとなります。
SOURCES= \
..\myprojsrc.cpp \
..\myprojsrc2.cpp \
..\SubDir\subdirsrc.cpp \
ビルドエラーのメッセージは、次のようになる筈です。
NMAKE : U1073: don’t know how to make ‘obj\ARMV4I\debug\subdirsrc.obj’
これは、subdirsrc.cpp に対応するオブジェクトファイルの生成規則が見つからないからです。従って、sources ファイルに次の生成規則を追加することにより、このエラーを解消できます:
# for source files in ‘SubDir/’ dir
{..\SubDir\}.cpp{$(_OBJDIR)\}.obj:
@echo BUILD_MARKER:CPP_COMPILE_START Compiling $<
@type <<
$(ECHO_CXX_MSG)
<<NOKEEP
@$(CXXCOMPILER) @<< $(CONLY_FLAGS) -Fo$@
$(C_COMMAND_LINE_OPTIONS: =
) $(MAKEDIR)\$<
<<NOKEEP
@echo BUILD_MARKER:CPP_COMPILE_END
サブディレクトリに配置するソースファイルが、C++ のソース(.cpp)ではなく C のソース(.c)の場合に、どのような生成規則を追加すればよいのかは、makefile.def を御覧になってみて下さい。’BUILD_MARKER:C_COMPILE_START’ という文字列で検索すれば、すぐに見つかるでしょう
2008/12/14
koga
Previous Posts