<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Windows CE Blog</title>
	<atom:link href="http://www.stprec.co.jp/ceblog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.stprec.co.jp/ceblog</link>
	<description>Windows CE/ Windows Embedded CEに関するブログ　by Something Precious</description>
	<lastBuildDate>Mon, 14 May 2012 09:14:12 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>ネイティブコードから C# のメソッドをコールバック</title>
		<link>http://www.stprec.co.jp/ceblog/2012/05/14/%e3%83%8d%e3%82%a4%e3%83%86%e3%82%a3%e3%83%96%e3%82%b3%e3%83%bc%e3%83%89%e3%81%8b%e3%82%89-c-%e3%81%ae%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%82%92%e3%82%b3%e3%83%bc%e3%83%ab%e3%83%90%e3%83%83/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/05/14/%e3%83%8d%e3%82%a4%e3%83%86%e3%82%a3%e3%83%96%e3%82%b3%e3%83%bc%e3%83%89%e3%81%8b%e3%82%89-c-%e3%81%ae%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%82%92%e3%82%b3%e3%83%bc%e3%83%ab%e3%83%90%e3%83%83/#comments</comments>
		<pubDate>Mon, 14 May 2012 06:21:52 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[.NET Compact Framework]]></category>
		<category><![CDATA[アプリケーション開発]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=375</guid>
		<description><![CDATA[今回は、ネイティブコード（アンマネージコード）から C# のメソッド、つまりマネージドコードをコールバックする方法について紹介します。
■C# のコールバック実装
ネイティブコードから C# のメソッドをコールバックさせる場合の手順を説明する前に、C# でコールバック関数を実装する方法について、簡単に説明します。C#/CLR の delegate についてご存じの方は、以下の説明は飛ばして、次の「ネイティブコードからコールバックさせる場合の手順」をご覧になって下さい。
C# でコールバック関数を実装する場合は、delegate を定義して、コールバック関数として用いるメソッドを、その delegate に割り当てます。delegate については、C# のプログラミングガイドの、次のページで説明されています。
　delegate (C# Reference)
　http://msdn.microsoft.com/en-us/library/900fyy8e%28v=VS.80%29.aspx
　Delegates (C# Programming Guide)
　http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx
上のページで説明されているように、delegate は、メソッドに対する参照型（reference type）です。delegate は、delegate キーワードを使って、通常のメソッドと同じように宣言しますが、delegate と同じシグネチャを持つメソッドを、その実体として割り当てることができます。この時、宣言された delegate に対して、System.Delegate クラスの派生クラスがコンパイラによって生成され、インスタンス化されます。Delegate クラスのリファレンスは、次のページにあります。
　Delegate Class
　http://msdn.microsoft.com/en-us/library/system.delegate(v=vs.90).aspx
なお、delegate に割り当てることのできるメソッドは、static である必要はなく、インスタンスメソッドを割り当てることが可能です。たとえば、System.Threading の Thread クラスは、コンストラクタの引数に ThreadStart という delegate を受け取り、その delegate に割り当てられたメソッド実体を、インスタンス内部で生成するスレッドによって実行します。ここで、インスタンスメソッドを delegate に割り当てることができるので、C++ の場合とは違い、クラスのインスタンスは渡す必要がない、というわけです。
■ネイティブコードからコールバックさせる場合の手順
さて、本題です。ネイティブコードから C# のメソッドをコールバックさせる手順ですが、実は簡単です。必要な手順は、次の三つです：

コールバックさせるメソッドに対応する delegate を定義する。
定義した delegate にメソッドを割り当てたのち、System.Runtime.InteropServices.GCHandle 構造体の Alloc() メソッドを適用して、delegate 実体に対する [...]]]></description>
			<content:encoded><![CDATA[<p>今回は、ネイティブコード（アンマネージコード）から C# のメソッド、つまりマネージドコードをコールバックする方法について紹介します。</p>
<p>■C# のコールバック実装<br />
ネイティブコードから C# のメソッドをコールバックさせる場合の手順を説明する前に、C# でコールバック関数を実装する方法について、簡単に説明します。C#/CLR の delegate についてご存じの方は、以下の説明は飛ばして、次の「ネイティブコードからコールバックさせる場合の手順」をご覧になって下さい。</p>
<p>C# でコールバック関数を実装する場合は、delegate を定義して、コールバック関数として用いるメソッドを、その delegate に割り当てます。delegate については、C# のプログラミングガイドの、次のページで説明されています。</p>
<p>　delegate (C# Reference)<br />
　<a href="http://msdn.microsoft.com/en-us/library/900fyy8e%28v=VS.80%29.aspx">http://msdn.microsoft.com/en-us/library/900fyy8e%28v=VS.80%29.aspx</a></p>
<p>　Delegates (C# Programming Guide)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx">http://msdn.microsoft.com/en-us/library/ms173171(v=vs.80).aspx</a></p>
<p>上のページで説明されているように、delegate は、メソッドに対する参照型（reference type）です。delegate は、delegate キーワードを使って、通常のメソッドと同じように宣言しますが、delegate と同じシグネチャを持つメソッドを、その実体として割り当てることができます。この時、宣言された delegate に対して、System.Delegate クラスの派生クラスがコンパイラによって生成され、インスタンス化されます。Delegate クラスのリファレンスは、次のページにあります。</p>
<p>　Delegate Class<br />
　<a href="http://msdn.microsoft.com/en-us/library/system.delegate(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/system.delegate(v=vs.90).aspx</a></p>
<p>なお、delegate に割り当てることのできるメソッドは、static である必要はなく、インスタンスメソッドを割り当てることが可能です。たとえば、System.Threading の Thread クラスは、コンストラクタの引数に ThreadStart という delegate を受け取り、その delegate に割り当てられたメソッド実体を、インスタンス内部で生成するスレッドによって実行します。ここで、インスタンスメソッドを delegate に割り当てることができるので、C++ の場合とは違い、クラスのインスタンスは渡す必要がない、というわけです。</p>
<p>■ネイティブコードからコールバックさせる場合の手順<br />
さて、本題です。ネイティブコードから C# のメソッドをコールバックさせる手順ですが、実は簡単です。必要な手順は、次の三つです：</p>
<ol>
<li>コールバックさせるメソッドに対応する delegate を定義する。
<li>定義した delegate にメソッドを割り当てたのち、System.Runtime.InteropServices.GCHandle 構造体の Alloc() メソッドを適用して、delegate 実体に対する GCHandle インスタンスを生成する。
<li>delegate 実体に、System.Runtime.InteropServices.Marshal クラスの GetFunctionPointerForDelegate() を適用して、その戻り値を、関数ポインタとしてネイティブコードに渡す。
</ol>
<p>C のネイティブコード API で、コールバック関数の関数ポインタを受け取るものを使ってコールバックする場合ですと、関数ポインタの引数を IntPtr として P/Invoke の宣言を行い、System.Runtime.InteropServices.Marshal クラスの GetFunctionPointerForDelegate() の戻り値（IntPtr 型）を、関数ポインタの引数として渡せばよいのです。ただし、それだけですと、その関数ポインタに関連づけられた delegate の実体、つまり C# のメソッド（インスタンスメソッドの場合のインスタンスおよび、クラス）が、ガベージコレクション（GC）によって解放されてしまう可能性があります。そのため、メソッドを割り当てた delegate 実体に対して GCHandle のインスタンスを生成し、GC の対象外にする、というわけです。</p>
<p>GCHandle 構造体および、System.Runtime.InteropServices.Marshal クラスの GetFunctionPointerForDelegate() については、リファレンスの次のページをご覧ください。</p>
<p>　GCHandle Structure<br />
　<a href="http://msdn.microsoft.com/en-us/library/khk3k17t(v=vs.90)">http://msdn.microsoft.com/en-us/library/khk3k17t(v=vs.90)</a></p>
<p>　Marshal.GetFunctionPointerForDelegate Method<br />
　<a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getfunctionpointerfordelegate(v=vs.90)">http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getfunctionpointerfordelegate(v=vs.90)</a></p>
<p>ここで述べた、ネイティブコード（アンマネージコード）からマネージドコードを呼び出す手順については、Bojan Resnik という人の Blog でも紹介されています：</p>
<p>　Passing C++/CLI delegate to native code<br />
　<a href="http://resnikb.wordpress.com/2009/05/18/passing-ccli-delegate-to-native-code/">http://resnikb.wordpress.com/2009/05/18/passing-ccli-delegate-to-native-code/</a></p>
<p>この Blog エントリでは、C# ではなく、C++/CLI の場合の例を説明していますが、C# の場合も同様です。上の説明と合わせ、参考にしてみて下さい。</p>
<p>■おまけ： .NET CF のガベージコレクション<br />
上の説明で、ガベージコレクション（GC）について述べましたので、ついでに、.NET CF の GC について留意点を書いておきます。</p>
<p>まず、.NET CF の GC は、フル版の .NET Framework の GC とは実装が異なります。フル版の .NET Framework では（より正確には、フル版の .NET Framework の CLR では）、世代別 GC かつ、コンカレントまたはバックグラウンド GC が実装されています。詳しくは、次のページをご覧ください。</p>
<p>　Fundamentals of Garbage Collection<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee787088">http://msdn.microsoft.com/en-us/library/ee787088</a></p>
<p>一方、.NET CF では、コンパクション付きのマーク＆スイープ GC です。これについては、.NET CF チームの  Steven Pratschner という人が MSDN Blog に書いたエントリが参考になります：</p>
<p>　An Overview of the .Net Compact Framework Garbage Collector<br />
　<a href="http://blogs.msdn.com/b/stevenpr/archive/2004/07/26/197254.aspx">http://blogs.msdn.com/b/stevenpr/archive/2004/07/26/197254.aspx</a></p>
<p>　The Design of the .Net Compact Framework CLR, Part III: GC Heap Management<br />
　<a href="http://blogs.msdn.com/b/stevenpr/archive/2005/12/14/503818.aspx">http://blogs.msdn.com/b/stevenpr/archive/2005/12/14/503818.aspx</a></p>
<p>.NET CF の GC は、コンカレント GC ではなく、また、世代別 GC でもないため、GC が起きた際の、GC 実行中におけるマネージドコード全スレッドの停止と、停止期間について留意する必要があります。一般論としては、マネージドコードでは、リアルタイム処理に関わる動作を行わず、リアルタイム処理は全てアンマネージコードに任せるか、または、頻繁に GC が起きないように設計する方が良いと思います。GC が頻繁に起きないようにする方策としては、マネージドコードでも、バッファやオブジェクトのプールを実装し、過度に GC に頼らないようにすることが考えられます。もちろん、GC に頼らないということにこだわり過ぎて、全てのオブジェクトのメモリ管理を自前で行おうとして複雑な設計や実装をするのは、本末転倒です。とはいえ、数百KB以上のサイズのオブジェクトやバッファ領域を頻繁に使用する場合には、それらを都度 new で生成して GC に回収させるのではなく、あらかじめ必要な個数を生成しておき（プーリング）、それらを使いまわすプール方式で実装する方が良い場合は、少なくないと思います。</p>
<p>GC の場合も、チューニングを行う場合には、定性的な評価・予測に頼るのではなく、実測に基づく、定量的な評価・分析が重要です。たとえば、「GC.Collect() をアプリケーションが呼ぶのは間違いか？」という意味のタイトルの次の質問に対する回答でも、「それが有効な場合は、レアケースとして考えられる。ただし、十分に実測して、そのうえで効果を判定すべし。」と書かれています：</p>
<p>　What&#8217;s so wrong about using GC.Collect()?<br />
　<a href="http://stackoverflow.com/questions/118633/whats-so-wrong-about-using-gc-collect">http://stackoverflow.com/questions/118633/whats-so-wrong-about-using-gc-collect</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/05/14/%e3%83%8d%e3%82%a4%e3%83%86%e3%82%a3%e3%83%96%e3%82%b3%e3%83%bc%e3%83%89%e3%81%8b%e3%82%89-c-%e3%81%ae%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%82%92%e3%82%b3%e3%83%bc%e3%83%ab%e3%83%90%e3%83%83/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WEC/WinCE から共有ディレクトリ（ファイルサーバ）をアクセス</title>
		<link>http://www.stprec.co.jp/ceblog/2012/05/07/wecwince-%e3%81%8b%e3%82%89%e5%85%b1%e6%9c%89%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%ef%bc%88%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%82%b5%e3%83%bc%e3%83%90%ef%bc%89%e3%82%92%e3%82%a2/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/05/07/wecwince-%e3%81%8b%e3%82%89%e5%85%b1%e6%9c%89%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%ef%bc%88%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%82%b5%e3%83%bc%e3%83%90%ef%bc%89%e3%82%92%e3%82%a2/#comments</comments>
		<pubDate>Mon, 07 May 2012 08:12:20 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[OS のコンフィグレーション]]></category>
		<category><![CDATA[付属機能の使い方]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=369</guid>
		<description><![CDATA[前回のエントリで、.NET CF アプリケーションから WNetAddConnection3() を呼び出す例を述べました。その例の説明で書いたように、WNetAddConnection3() は、Windows フィルサーバとの接続などを行う API です。
■Windows ファイルサーバに対するクライアント機能
Windows ファイルサーバ（CIFS サーバ）をアクセスする機能、つまり、Windows ファイルサーバに対するクライアント機能は、&#8221;Windows Networking API/Redirector&#8221; というコンポーネントになっており、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 の場合）：
　&#60;OS Design 名&#62;
　Core OS
　　Windows Embedded Compact
　　　Communication Services and Networking
　　　　Networking &#8211; General
★　　　　Windows Networking API/Redirector [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.stprec.co.jp/ceblog/2012/05/02/">前回のエントリ</a>で、.NET CF アプリケーションから <a href="http://msdn.microsoft.com/en-us/library/ee493048.aspx">WNetAddConnection3()</a> を呼び出す例を述べました。その例の説明で書いたように、WNetAddConnection3() は、Windows フィルサーバとの接続などを行う API です。</p>
<p>■Windows ファイルサーバに対するクライアント機能<br />
Windows ファイルサーバ（CIFS サーバ）をアクセスする機能、つまり、Windows ファイルサーバに対するクライアント機能は、&#8221;Windows Networking API/Redirector&#8221; というコンポーネントになっており、<a href="http://msdn.microsoft.com/en-us/library/ee479032(v=winembedded.60).aspx">SYSGEN 変数</a>の SYSGEN_REDIR を設定することにより、OS イメージに組み込まれます。詳細については、リファレンスの次のページをご覧ください。</p>
<p>　Windows Networking API/Redirector Reference (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee495264.aspx">http://msdn.microsoft.com/en-us/library/ee495264.aspx</a></p>
<p>　Windows Networking API/Redirector (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee494246(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee494246(v=winembedded.60).aspx</a></p>
<p>Platform Builder のカタログ項目ビュー（Catalog Items View）の階層で言うと、次の項目を選択すると、SMB/CIFS クライアント機能が OS Design に組み込まれます（※WEC 7 の場合）：</p>
<p>　&lt;OS Design 名&gt;<br />
　Core OS<br />
　　Windows Embedded Compact<br />
　　　Communication Services and Networking<br />
　　　　Networking &#8211; General<br />
★　　　　Windows Networking API/Redirector (SMB/CIFS)</p>
<p>ここで、&#8221;Redirector&#8221; という Windows の用語に馴染みのない方は、たとえば、次の解説記事が参考になるのではないかと思います。</p>
<p>　「基礎から学ぶWindowsネットワーク 第20回」<br />
　<a href="http://www.atmarkit.co.jp/fwin2k/network/baswinlan020/baswinlan020_03.html">http://www.atmarkit.co.jp/fwin2k/network/baswinlan020/baswinlan020_03.html</a></p>
<p>　「基礎から学ぶWindowsネットワーク」<br />
　<a href="http://www.atmarkit.co.jp/fwin2k/network/baswinlan002/baswinlan002_03.html">http://www.atmarkit.co.jp/fwin2k/network/baswinlan002/baswinlan002_03.html</a></p>
<p>なお、CIFS クライアントだけでなく、CIFS サーバ機能も WEC/WinCE に付属しています。こちらは、カタログ項目ビューの階層の、次の場所にあります：</p>
<p>　&lt;OS Design 名&gt;<br />
　Core OS<br />
　　Windows Embedded Compact<br />
　　　Communication Services and Networking<br />
　　　　Servers<br />
　　　　　File Server (SMB/CIFS)</p>
<p>CIFS サーバに関する記述は、なぜか WEC 7 のリファレンスに見当たりません。興味のある方は、WinCE 6.0 のリファレンスをご覧ください。</p>
<p>　File Server (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee500573(v=winembedded.60).aspx">http://msdn.microsoft.com/en-us/library/ee500573(v=winembedded.60).aspx</a></p>
<p>■SMB/CIFS クライアント機能を組み込む場合の注意点<br />
さて、SMB/CIFS クライアント機能（Windows Networking API/Redirector）を組み込む場合、一つ注意しなければいけません。WEC/WinCE のリファレンスには、このコンポーネントが依存するのは、TCP/IP (SYSGEN_TCPIP) と Winsock (SYSGEN_WINSOCK)、および NDIS (SYSGEN_NDIS) とだけ書かれているのですが、それらのコンポーネントだけでは、SMB/CIFS サーバにアクセスすることは、できません。</p>
<p>CIFS サーバに接続する場合は、認証処理が必要ですが、認証処理に必要なコンポーネントは、SMB/CIFS クライアント機能を組み込んでも自動的に組み込まれないため、明示的に組み込む必要があるのです。認証処理に必要なコンポーネントは、WEC 7 ですと、カタログ項目ビューの階層の、次の場所にあります：</p>
<p>　&lt;OS Design 名&gt;<br />
　Core OS<br />
　　Windows Embedded Compact<br />
　　　Security<br />
　　　　Authentication Services (SSPI)<br />
★　　　　Kerberos<br />
★　　　　NTLM</p>
<p>通常は、NTLM だけを選択しても CIFS サーバに接続できるでしょう。お手元の環境で、試してみて下さい。これらのコンポーネントについては、リファレンスの次のページで説明されています（※WEC 7 の場合）。</p>
<p>　NTLM Security Support Provider (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee498104.aspx">http://msdn.microsoft.com/en-us/library/ee498104.aspx</a></p>
<p>　Kerberos Security Support Provider (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee498711.aspx">http://msdn.microsoft.com/en-us/library/ee498711.aspx</a></p>
<p>　Authentication Services (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee498877.aspx">http://msdn.microsoft.com/en-us/library/ee498877.aspx</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/05/07/wecwince-%e3%81%8b%e3%82%89%e5%85%b1%e6%9c%89%e3%83%87%e3%82%a3%e3%83%ac%e3%82%af%e3%83%88%e3%83%aa%ef%bc%88%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e3%82%b5%e3%83%bc%e3%83%90%ef%bc%89%e3%82%92%e3%82%a2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>.NET CF で Win32 API を P/Invoke する時の注意点</title>
		<link>http://www.stprec.co.jp/ceblog/2012/05/02/net-cf-%e3%81%a7-win32-api-%e3%82%92-pinvoke-%e3%81%99%e3%82%8b%e6%99%82%e3%81%ae%e6%b3%a8%e6%84%8f%e7%82%b9/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/05/02/net-cf-%e3%81%a7-win32-api-%e3%82%92-pinvoke-%e3%81%99%e3%82%8b%e6%99%82%e3%81%ae%e6%b3%a8%e6%84%8f%e7%82%b9/#comments</comments>
		<pubDate>Wed, 02 May 2012 09:27:50 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[.NET Compact Framework]]></category>
		<category><![CDATA[アプリケーション開発]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=352</guid>
		<description><![CDATA[WEC/WinCE で、.NET CF (.NET Compact Framework) のクラスライブラリでは提供されていない API を使う場合は、CLR (Common Language Run-time) の P/Invoke を利用する必要があります。P/Invoke の仕組み自体は、.NET Framework と同じですが、.NET CF における制約や、WEC/WinCE での Win32 API の細かな違いがあり、.NET Framework と全く同じにはできない場合があります。
以下では、それらの注意点のうち、二点紹介します。
■Win32 API の「実際の」名前が違う場合
P/Invoke を使って Win32 API を呼び出す場合に、.NET Framework では動作したコードが、.NET CF では動作しない、という場合があります。つまり、Windows 7 などでは動作した C# のコードを、WEC/WinCE 用にビルドして動かそうとすると、例外送出が起きて動作しない、という場合があるのです。これは、その API の実際の名前が、WEC/WinCE では違っていることが原因です。
例１）InternetConnect()
wininet.dll で提供されている InternetConnect() は、マクロであり、ネイティブコード（アンマネージコード）の ANSI ビルドでは InternetConnectA(), Unicode ビルドでは InternetConnectW() の呼び出しに置換されます。ここで、.NET Framework [...]]]></description>
			<content:encoded><![CDATA[<p>WEC/WinCE で、.NET CF (.NET Compact Framework) のクラスライブラリでは提供されていない API を使う場合は、CLR (Common Language Run-time) の P/Invoke を利用する必要があります。P/Invoke の仕組み自体は、.NET Framework と同じですが、.NET CF における制約や、WEC/WinCE での Win32 API の細かな違いがあり、.NET Framework と全く同じにはできない場合があります。</p>
<p>以下では、それらの注意点のうち、二点紹介します。</p>
<p>■Win32 API の「実際の」名前が違う場合<br />
P/Invoke を使って Win32 API を呼び出す場合に、.NET Framework では動作したコードが、.NET CF では動作しない、という場合があります。つまり、Windows 7 などでは動作した C# のコードを、WEC/WinCE 用にビルドして動かそうとすると、例外送出が起きて動作しない、という場合があるのです。これは、その API の実際の名前が、WEC/WinCE では違っていることが原因です。</p>
<p>例１）InternetConnect()<br />
wininet.dll で提供されている <a href="http://msdn.microsoft.com/en-US/library/ee492231(v=winembedded.60).aspx">InternetConnect()</a> は、マクロであり、ネイティブコード（アンマネージコード）の ANSI ビルドでは InternetConnectA(), Unicode ビルドでは InternetConnectW() の呼び出しに置換されます。ここで、.NET Framework の場合には、ANSI ビルド用の <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa384363(v=vs.85).aspx">InternetConnectA()</a> も使うことができますが、WEC/WinCE は Unicode のみであるため、InternetConnectA() は提供されておらず、使うことが出来ません。従って、InternetConnectW という名前を指定しなければいけません。</p>
<p>例２）SetEvent()<br />
Windows 7 などでは、SetEvent() は kernel32.dll で提供されている一方、WEC/WinCE では、SetEvent() は coredll.dll で提供されています。DLL の名前が違うことに加え、.NET CF で SetEvent に対して P/Invoke 呼び出しを行うと、そのような関数は実装されていないというエラーになります（例外が起きます）。実は、WEC/WinCE では、SetEvent() という名前の API 関数の実体は、存在しないのです。何が違うのかは、両者のリファレンスから辿って、関数宣言のヘッダファイルを調べてみると分ります：</p>
<p>　・デスクトップ版の SetEvent()<br />
　<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms686211(v=vs.85).aspx">http://msdn.microsoft.com/en-us/library/windows/desktop/ms686211(v=vs.85).aspx</a>　</p>
<p>　・WEC/WinCE 版の SetEvent()<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee488785.aspx">http://msdn.microsoft.com/en-us/library/ee488785.aspx</a></p>
<p>上の二つのページで、&#8221;Requirements&#8221; の項にある表の、&#8221;Header&#8221; を見て下さい。デスクトップ版では、WinBase.h ですが、WEC/WinCE 版では kfuncs.h となっています。WEC/WinCE のソースツリーにある winbase.h を見てみると（%_WINCEROOT%/public/COMMON/sdk/inc/ にあります）、SetEvent() の宣言は、#ifndef UNDER_CE &#8230; #endif で囲われており、WEC/WinCE では無効になっています。そして、それらの関数は、kfuncs.h で宣言／定義されているというコメントがあるのです。そこで、kfuncs.h を見てみると、SetEvent() は、_inline 修飾子を使ってインライン関数として定義され、EventModify() という関数を呼び出していることが分かります。EventModify() は、二つの引数を取り、SetEvent() の場合は EVENT_SET という定数（値は3）を第二引数に渡します。つまり、WEC/WinCE では、SetEvent() という名前の関数の実体は、存在しないのです。SetEvent() の呼び出しは、EventModify() の呼び出しに展開されますので、P/Invoke する場合は、EventModify という名前を指定しなければいけません。</p>
<p>このように、同じ名前の Win32 API であっても、WEC/WinCE では実体が異なる場合があるため、.NET Framework では動いたコードが、.NET CF では動かない、という場合があるのです。Win32 API を P/Invoke する場合には、リファレンスを見て、WEC/WinCE では実体が異なっていないかどうか確認してみて下さい。リファレンスだけでは分かりづらいですから、見落とす場合もあると思います。従って、Win32 API を P/Invoke する場合は、必ず単体テストして、期待通りに動作するかどうかを確認し、エラーが起きる場合には、ヘッダファイルをチェックしてみるのが良いでしょう。</p>
<p>■P/Invoke のマーシャリングに関する制約<br />
P/Invoke における、関数引数のマーシャリングは、.NET Framework と .NET CF で一部異なっています。次のページに、その違いがまとめられています：<br />
　An Introduction to P/Invoke and Marshaling on the Microsoft .NET Compact Framework<br />
　<a href="http://msdn.microsoft.com/en-us/library/aa446536.aspx">http://msdn.microsoft.com/en-us/library/aa446536.aspx</a></p>
<p>ここで述べられている違いの中で、Win32 API を P/Invoke する際に注意が必要なのは、.NET CF の場合、構造体メンバ中に参照型（reference type）があっても、マーシャリングされない、という点です。上のページの、&#8221;Passing Structures&#8221; という項に書かれています。これは、文字列型のメンバを持つ構造体を引数に受け取る API 関数を P/Invoke する際に、問題となります。</p>
<p>たとえば、Windows ファイルサーバ（CIFS サーバ）との接続などを行う、<a href="http://msdn.microsoft.com/en-us/library/ee493048.aspx">WNetAddConnection3()</a> は、<a href="http://msdn.microsoft.com/en-us/library/ee493893.aspx">NETRESOURCE 構造体</a>を引数に受け取りますが、この NETRESOURCE 構造体は、文字列型のメンバを持ちます。なお、WNetAddConnection3() の実体は、WNetAddConnection3W() です。</p>
<p>ここで、関数の引数が文字列を受け取る場合は、.NET CF の場合、string オブジェクトをそのまま渡せます。前述した InternetConnectW() の引数や、WNetAddConnection3W() の引数 lpPassword と lpUserName が、その例です。しかし、構造体のメンバが文字列の場合には、マーシャリングが行われないので、特別な対応が必要です。この詳細については、次のページで説明されています。</p>
<p>　Advanced P/Invoke on the Microsoft .NET Compact Framework<br />
　<a href="http://msdn.microsoft.com/en-us/library/aa446529.aspx">http://msdn.microsoft.com/en-us/library/aa446529.aspx</a></p>
<p>上のページでは、いくつかの方策が示されていますが、真ん中ほどにある &#8220;Using a Managed String Pointer&#8221; という項で説明されている方策が、お手頃ではないかと思います。この方策は、IntPtr 型のメンバを持つ CLR の struct（構造体）を定義して、マネージドコードの文字列（string）とアンマネージコードの文字列の間の変換（マーシャリング）を自前で行う、というものです。上のページの例では、文字列マーシャリング用の StringPtr という CLR の構造体と、StringPtr が使う Memory というクラスを VB .NET で定義しています。同様のことを、C# でもう少し単純に定義すると、次のようにも書けます：</p>
<p><code>
<pre>
using System;
using System.Runtime.InteropServices;

public struct NativeString {
    private IntPtr szString;

    // constructor
    public NativeString(string strObj) {
        int len = (strObj.Length + 1) * 2;

        szString = Marshal.AllocHGlobal(len);
        Marshal.Copy(strObj.ToCharArray(), 0, szString, strObj.Length);
        Marshal.WriteByte(szString, len - 2, 0);
        Marshal.WriteByte(szString, len - 1, 0);
    }

    // release the resource
    public void    Dispose() {
        if (IntPtr.Zero != szString)
        {
            Marshal.FreeHGlobal(szString);
            szString = IntPtr.Zero;
        }
    }
}
</pre>
<p></code><br />
この NativeString を使って、NETRESOURCE を次のように定義できます。</p>
<p><code>
<pre>
public struct NETRESOURCE {
    public int  dwScope;
    public int  dwType;
    public int  dwDisplayType;
    public int  dwUsage;
    public NativeString lpLocalName;
    public NativeString lpRemoteName;
    public IntPtr lpComment;
    public IntPtr lpProvider;

    // constructor
    public NETRESOURCE(string localName, string remoteName) {
        dwScope       = 0;
        dwType        = 1;  // RESOURCETYPE_DISK
        dwDisplayType = 0;
        dwUsage       = 0;
        lpLocalName   = new NativeString(localName);
        lpRemoteName  = new NativeString(remoteName);
        lpComment     = IntPtr.Zero;
        lpProvider    = IntPtr.Zero;
    }

    // release the resource
    public void Cleanup() {
        lpLocalName.Dispose();
        lpRemoteName.Dispose();
    }
}
</pre>
<p></code>なお、NativeString は、クラスではなく、構造体（struct）でなければならないことに注意して下さい。NativeString は、Win32 API、つまりアンマネージコードと同じメモリレイアウトを持った NETRESOURCE 構造体のメンバとして使いますので、参照型であるクラスではなく、値型（value type）である構造体として定義する必要がある、というのがその理由です。</p>
<p>以上、.NET CF から Win32 API を P/Invoke する場合に注意すべき点を二つ紹介しました。.NET CF からのアンマネージコードの呼び出しについては、以下の Technical Articles セクションに、他にも記事がありますので、興味のある方は、ご覧になってみて下さい。</p>
<p>　Native Interoperability<br />
　<a href="http://msdn.microsoft.com/en-us/library/aa145821.aspx">http://msdn.microsoft.com/en-us/library/aa145821.aspx</a></p>
<p>上の記事の中には、.NET CF 2.0 を扱ったものもあり（※現時点での .NET CF の最新版は、3.5 です）、少し古く感じられるものもありますが、今でも通用する内容がほとんどだと思います。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/05/02/net-cf-%e3%81%a7-win32-api-%e3%82%92-pinvoke-%e3%81%99%e3%82%8b%e6%99%82%e3%81%ae%e6%b3%a8%e6%84%8f%e7%82%b9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows Developer Days (2012/04/24-25)</title>
		<link>http://www.stprec.co.jp/ceblog/2012/04/14/windows-developer-days-20120424-25/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/04/14/windows-developer-days-20120424-25/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 23:21:59 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[イベント]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=341</guid>
		<description><![CDATA[再来週の火曜日と水曜日（4/24, 25）に、芝公園のザ・プリンスパークタワー東京で「Windows Developer Days」が開催されます。Windows 8 を中心に、Metro スタイルアプリケーションの開発に関する技術セッションが開催されるようです。興味のある方は、是非どうぞ。
]]></description>
			<content:encoded><![CDATA[<p>再来週の火曜日と水曜日（4/24, 25）に、芝公園のザ・プリンスパークタワー東京で「<a href="http://www.microsoft.com/ja-jp/events/wdd/default.aspx">Windows Developer Days</a>」が開催されます。Windows 8 を中心に、Metro スタイルアプリケーションの開発に関する技術セッションが開催されるようです。興味のある方は、是非どうぞ。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/04/14/windows-developer-days-20120424-25/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>.NET CF アプリケーションとカーネルランドを一緒にデバッグ（2/2）</title>
		<link>http://www.stprec.co.jp/ceblog/2012/04/13/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87-2/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/04/13/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87-2/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 08:51:34 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[.NET Compact Framework]]></category>
		<category><![CDATA[OS の内部動作]]></category>
		<category><![CDATA[アプリケーション開発]]></category>
		<category><![CDATA[開発環境]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=333</guid>
		<description><![CDATA[前回の続きです。アプリケーションとカーネルランドを一緒にデバッグするやり方に関連して、いくつか補足します。
■カーネルデバッガと一緒に動かす場合の設定（※ネットワークインタフェースが一つだけの場合）
さて、カーネルデバッガとアプリケーションデバッガを一緒に動かす場合、一つ注意しなければいけません。それは、WinCE/WEC に固定 IP アドレスを設定する場合のレジストリキーです。前回の「ターゲットボードとホスト PC を直結する場合」の説明で、IP アドレスを設定する場合のレジストリキーが
　[HKEY_LOCAL_MACHINE\Comm\\Parms\Tcpip]
だと書きました。カーネルデバッガを動かす場合、この「アダプタ名」が、動かさない時と違う場合があるのです。それは、ターゲットボードにネットワークインタフェースが一つしかなく、アプリケーションデバッガの Ethernet 接続と、カーネルデバッガの Ethernet 接続が、一つのネットワークインタフェースを共有する（つまり、Ethernet ケーブルを一本しか使わない）場合です。
その場合、カーネルデバッガを動かす時は、アダプタ名として &#8220;VMINI1&#8243; を指定しなければいけません。VMINI1 というのは、前回の例で示した &#8220;FEC1&#8243; のような、Ethernet コントローラを直接制御するデバイスドライバではなく、仮想のネットワークデバイス（論理デバイス）のアダプタ名です。この仮想ネットワークデバイス（VMini）は、リファレンスの次のページで説明されています：
　Ethernet Debugging Services (Windows Embedded CE 6.0)
　http://msdn.microsoft.com/en-us/library/ee478406(v=winembedded.60).aspx
WEC 7 のリファレンスには、VMini について説明した個所が見当たりません。もしかすると、WEC 7 からは、VMini の使用を推奨しておらず、そのため、説明を省略しているのかも知れません。なお、KITL についてご存じの方は、ここと、次の説明（「KITL について、もう少しだけ」）は飛ばして下さい。
以下は、KITL について詳しくない方のための説明です。
上のページの図を見ると、&#8221;VBridge&#8221; というモジュールが、&#8221;Vmini.dll&#8221; と &#8220;EDBG&#8221; の二つのモジュールに繋がっており、&#8221;Vmini.dll&#8221; は、NDIS インタフェースを介して TCP/IP スタックと繋がっています。この &#8220;VBridge&#8221; が、カーネルデバッガとアプリケーションデバッガが一つのネットワークインタフェースを共有するためのモジュールで、&#8221;Vmini.dll&#8221; が VMini のことです。VMini は、ネットワークインタフェース、つまり Ethernet コントローラを直接制御せず、VBridge に委譲して間接的に制御します。VBridge は、VMini に加えて、&#8221;EDBG&#8221;、つまり、カーネルデバッガが使用する Ethrnet 通信機能のモジュールに対してもネットワークインタフェース機能を提供します。
この VBridge [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.stprec.co.jp/ceblog/2012/04/03/">前回</a>の続きです。アプリケーションとカーネルランドを一緒にデバッグするやり方に関連して、いくつか補足します。</p>
<p>■カーネルデバッガと一緒に動かす場合の設定（※ネットワークインタフェースが一つだけの場合）<br />
さて、カーネルデバッガとアプリケーションデバッガを一緒に動かす場合、一つ注意しなければいけません。それは、WinCE/WEC に固定 IP アドレスを設定する場合のレジストリキーです。<a href="http://www.stprec.co.jp/ceblog/2012/04/03/">前回</a>の「ターゲットボードとホスト PC を直結する場合」の説明で、IP アドレスを設定する場合のレジストリキーが<br />
　[HKEY_LOCAL_MACHINE\Comm\<アダプタ名>\Parms\Tcpip]<br />
だと書きました。カーネルデバッガを動かす場合、この「アダプタ名」が、動かさない時と違う場合があるのです。それは、ターゲットボードにネットワークインタフェースが一つしかなく、アプリケーションデバッガの Ethernet 接続と、カーネルデバッガの Ethernet 接続が、一つのネットワークインタフェースを共有する（つまり、Ethernet ケーブルを一本しか使わない）場合です。</p>
<p>その場合、カーネルデバッガを動かす時は、アダプタ名として &#8220;VMINI1&#8243; を指定しなければいけません。VMINI1 というのは、<a href="http://www.stprec.co.jp/ceblog/2012/04/03/">前回</a>の例で示した &#8220;FEC1&#8243; のような、Ethernet コントローラを直接制御するデバイスドライバではなく、仮想のネットワークデバイス（論理デバイス）のアダプタ名です。この仮想ネットワークデバイス（VMini）は、リファレンスの次のページで説明されています：</p>
<p>　Ethernet Debugging Services (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee478406(v=winembedded.60).aspx">http://msdn.microsoft.com/en-us/library/ee478406(v=winembedded.60).aspx</a></p>
<p>WEC 7 のリファレンスには、VMini について説明した個所が見当たりません。もしかすると、WEC 7 からは、VMini の使用を推奨しておらず、そのため、説明を省略しているのかも知れません。なお、KITL についてご存じの方は、ここと、次の説明（「KITL について、もう少しだけ」）は飛ばして下さい。</p>
<p>以下は、KITL について詳しくない方のための説明です。</p>
<p>上のページの図を見ると、&#8221;VBridge&#8221; というモジュールが、&#8221;Vmini.dll&#8221; と &#8220;EDBG&#8221; の二つのモジュールに繋がっており、&#8221;Vmini.dll&#8221; は、NDIS インタフェースを介して TCP/IP スタックと繋がっています。この &#8220;VBridge&#8221; が、カーネルデバッガとアプリケーションデバッガが一つのネットワークインタフェースを共有するためのモジュールで、&#8221;Vmini.dll&#8221; が VMini のことです。VMini は、ネットワークインタフェース、つまり Ethernet コントローラを直接制御せず、VBridge に委譲して間接的に制御します。VBridge は、VMini に加えて、&#8221;EDBG&#8221;、つまり、カーネルデバッガが使用する Ethrnet 通信機能のモジュールに対してもネットワークインタフェース機能を提供します。</p>
<p>この VBridge の仕組みから分かるように、カーネルデバッガは、Ethernet 経由でホスト PC と通信しますが、TCP/IP スタックは使用せず、独自のプロトコルで通信します。TCP/IP スタックは、カーネルの上位にありますから、カーネルデバッガが TCP/IP スタックを利用することは、できないのです。カーネルデバッガがホスト PC と通信するプロトコルは、KITL (Kernel Independent Transport Layer) と名付けられており、上のページの一つ上のドキュメント階層で説明されています。</p>
<p>　Kernel Independent Transport Layer (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee479323(v=winembedded.60).aspx">http://msdn.microsoft.com/en-us/library/ee479323(v=winembedded.60).aspx</a></p>
<p>　Kernel Independent Transport Layer (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee479323.aspx">http://msdn.microsoft.com/en-us/library/ee479323.aspx</a></p>
<p>KITL の説明は、WEC 7 のリファレンスにもあり、どちらも同じ図が載っています。この図の &#8220;KITL&#8221; の、&#8221;Device hardware&#8221; に近い側の部分（下側の部分）に先ほどの図の &#8220;VBridge&#8221; があり、そして、&#8221;Kernel debugger&#8221; に近い側の部分（上側の部分）に &#8220;EDBG&#8221; があると考えて下さい。</p>
<p>本題に戻ります。ネットワークインタフェースが一つしかなく、それを KITL と TCP/IP スタックで共有する場合、通常の Ethernet コントローラのドライバでは対応できません。そのため、通常の Ethernet コントローラのドライバ（先の例の &#8220;FEC1&#8243;）は使われず、VMini が代わりに OS イメージに組み込まれ、VBridge の働きにより、TCP/IP スタックと KITL がネットワークインタフェースを共有する、というわけです。そのため、固定 IP アドレスを設定する場合、レジストリキーで指定する TCP/IP スタックのアダプタ名を、VMini のアダプタ名である &#8220;VMINI1&#8243; にしなければいけないのです。</p>
<p>■KITL について、もう少しだけ<br />
ところで、KITL は Ethernet 接続でしか使えないかといえば、そうではありません。Kernel *Independent* Transport Layer という名前の通り、Ethernet 以外の様々な伝送路に対応できます。そして、Ethernet の KITL を使用して、TCP/IP スタックと Ethernet コントローラを共有する場合は、TCP/IP スタックが Ethernet コントローラを占有する場合に比べて、Ethernet による通信のパフォーマンスが低下します。これは、同時に二種類の通信が同じ伝送路を使うせいでもでありますし、また、VMini と VBrige、および、VBridge が呼び出す Ethernet コントローラの制御ルーチン（※この制御ルーチンは、OAL の一部として実装されます）の組み合わせによる動作は、通常の Ethernet コントローラのドライバの単体動作に比べてオーバーヘッドが大きいため、パフォーマンスを出しづらい、という事情もあると思われます。</p>
<p>そのため、WinCE/WEC のネットワークのパフォーマンス評価を行う場合は、VMini を無効にした状態で行うようにという注意書きがリファレンスに書かれています。上の KITL の説明ページにも、KITL と OS（TCP/IP スタック）がハードウェアを共有するとパフォーマンスに悪影響がある、という但し書きがあります。</p>
<p>年々、組み込み機器用のプロセッサは高機能化が進み、多くの周辺機器のコントローラを内蔵するようになってきています。そのため、カーネルデバッガ／KITL と TCP/IP スタックが一つのネットワークインタフェースを共有しなくても、たとえば KITL にはシリアルや USB での接続を割り当て、Ethernet コントローラは TCP/IP スタックに占有させる、といった構成は可能です。逆に、プロセッサに内蔵された Ethernet コントローラはデバッグ専用にして KITL に割り当て、TCP/IP スタック用には、USB や SD の WiFi モジュールを使う、というような構成や、あるいは、安価な Ethernet コントローラを外付けして、そちらをデバッグ専用に使う、という方策も考えられます。</p>
<p>いずれにせよ、VMini による、TCP/IP スタックと KITL の共存は、デバッグ専用のものであり、通常構成で使うものではありません。もちろん、Ethernet コントローラのドライバをデバッグする場合には、VMini は利用できません（※Ethernet コントローラのドライバをデバッグしようとすると、同じコントローラを使う KITL の動作に影響を与えるから・・・ではなく、そもそも、デバッグしたいドライバの代わりに VMini が動作するので、デバッグする方法がありません）。</p>
<p>■ソースコードも、見てみましょう<br />
上で述べた VBridge は、KITL (kitl.dll) に組み込まれるモジュールとなっており、そのソースコードは、WEC 7 と WinCE 6.0 のソースツリーの、それぞれ次の場所にあります：</p>
<p>　・WEC 7<br />
　　%_WINCEROOT%/platform/common/src/common/kitl/</p>
<p>　・WinCE 6.0<br />
　　%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/ETHDBG/VBRIDGE/</p>
<p>WinCE 6.0 では、VBridge は vbridge.lib という static library でしたが（※ソースファイルが vbridge.c 一つだけのライブラリ）、WEC 7 では、oal_kitl.lib という static library にまとめられています。oal_kitl.lib は、WinCE 6.0 にもありますが、VBridge は別のライブラリになっていました。WEC 7 になって、VBridge（vbridge.c） が oal_kitl.lib にまとめられた、というわけです。</p>
<p>ちなみに、カーネルデバッガの OS 側モジュール（kd.dll）のソースコードは、WEC 7 も WinCE 6.0 も、次の場所にあります：</p>
<p>　%_WINCEROOT%/private/winceos/COREOS/nk/kdstub/</p>
<p>興味のある方は、どのような実装になっているのか、ソースコードをご覧になってみると面白いんじゃないかと思います。</p>
<p>■おまけ：ネイティブコードのアプリケーションの場合<br />
さて、.NET CF アプリケーションではなく、ネイティブコード（アンマネージドコード）のアプリケーションをデバッグする場合は、どうなのでしょうか？</p>
<p>Visual Studio の「スマートデバイス」プロジェクトで作成したネイティブコードのアプリケーションをデバッグする手順は、.NET CF アプリケーションの場合と同様です。前回と今回で説明した手順を使えば、ネイティブコードのアプリケーションも、カーネルランドと一緒にデバッグできます。試してみて下さい。</p>
<p>ここで、注意深い方であれば、「あれ？」と思われたかも知れませんね。<a href="http://www.stprec.co.jp/ceblog/2012/04/03/">前回</a>の説明では、冒頭で次のように書いたからです：</p>
<p>　皆さんご存じの通り、ネイティブコードのアプリケーションの場合には、アプリケーションもカーネルランドも、どちらもカーネルデバッガでデバッグできます。</p>
<p>これは、何だったのでしょうか？前回の冒頭で書いたのは、OS Design のサブプロジェクトとしてアプリケーションを作成した場合のことを念頭に置いていました。つまり、OS イメージをビルドする際に、OS と一緒にビルドして、デフォルト設定では OS イメージに組み込まれるようにした場合のことを指していたのです。その場合は、カーネルデバッガを使って、カーネルランドと同時にアプリケーションをデバッグできます。</p>
<p>では、OS Design のサブプロジェクトで作成したアプリケーションではなく、Visual Studio の「スマートデバイス」プロジェクトで作成したネイティブコードのアプリケーションは、カーネルデバッガを使ってデバッグできないのでしょうか？実は、できます。</p>
<p>Visual Studio の「スマートデバイス」プロジェクトで作成したネイティブコードのアプリケーションを、カーネルデバッガでデバッグするには、アプリケーションの実行ファイル（.exe）とプログラムデータベースファイル（.pdb）を、カーネルデバッガが認識できる場所に置けばよいのです。OS Design のカタログ項目で、&#8221;Target Control Support(Shell.exe)” か “Release Directory File System&#8221; を選択して OS イメージをビルドしておき、Flat Release Directory に、デバッグしたいアプリケーションの .exe と .pdb をコピーする、というのが一番お手軽だと思います。こうしておけば、わざわざアプリケーションデバッガを別に動かさなくとも、カーネルデバッガだけで、アプリケーションとカーネルランドを一緒にデバッグできます。</p>
<p>たとえば、OS を開発するチームとアプリケーションを開発するチームが分かれていて、アプリケーションで不具合が起きた場合に、不具合の要因がアプリケーション側にあるのか OS 側にあるのか切り分け調査を行うなどの場合、この方法は便利かも知れません。アプリケーション開発チームは、OS Design のサブプロジェクトではなく、Visual Studio の「スマートデバイス」プロジェクトでアプリケーションを作成するのが一般的だと思います。そのような場合に、カーネルランドとアプリケーションを同時または一緒にデバッグする方策は、次のものが考えられます：</p>
<ol>
<li>カーネルデバッガとアプリケーションデバッガを各々動かして、カーネルランドとアプリケーションを一緒にデバッグする。
<li>「スマートデバイス」プロジェクトを OS Design のサブプロジェクトに作り直し、アプリケーションを OS Design に組み込んだうえで、カーネルデバッガを使ってカーネルランドとアプリケーションを一緒にデバッグする。
<li>「スマートデバイス」プロジェクトは、そのままにして、アプリケーションの .exe と .pdb を Flat Release Directory にコピーしてからカーネルデバッガを動かす（上述した方法）。
</ol>
<p>上の1は、前回と今回の主題である、.NET CF アプリケーションをカーネルランドと一緒にデバッグするのと同じ方策です。2と比べると、3の方が手間が少ないのは明らかです。1と3を比べても、カーネルデバッガだけで作業できるという点で、3の方が、より手間が少ない方法です。</p>
<p>■おまけ２：その他の参考資料<br />
前回と今回では、カーネルランドと一緒（または同時に）アプリケーションをデバッグするにはどうすればよいか、ということを説明しました。「スマートデバイス」プロジェクトで作ったアプリケーションだけをデバッグする場合については、Visual Studio のリファレンスに説明があります。いくつかのトピックが取り上げられていますので、ご覧になってみて下さい：</p>
<p>　Debugging Device Projects<br />
　<a href="http://msdn.microsoft.com/en-us/library/ms180772(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/ms180772(v=vs.90).aspx</a></p>
<p>ネイティブコードのアプリケーションに関しては、MFC や ATL を使ったアプリケーションをビルド・デバッグする場合の注意点や手順についても、解説されています。</p>
<p>　Building and Debugging Visual C++ Device Projects<br />
　<a href="http://msdn.microsoft.com/en-us/library/c5fc53wa(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/c5fc53wa(v=vs.90).aspx</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/04/13/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>.NET CF アプリケーションとカーネルランドを一緒にデバッグ（1/2）</title>
		<link>http://www.stprec.co.jp/ceblog/2012/04/03/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/04/03/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 11:36:34 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[.NET Compact Framework]]></category>
		<category><![CDATA[OS のコンフィグレーション]]></category>
		<category><![CDATA[OS の内部動作]]></category>
		<category><![CDATA[アプリケーション開発]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=323</guid>
		<description><![CDATA[今回は、マネージドコードのアプリケーション、つまり、.NET CF のアプリケーションとカーネルランドを、Ethernet 経由で一緒にデバッグする方法を説明します。「一緒にデバッグする」というのは、”「同時にデバッグする」ことはできないが、アプリケーションデバッガとカーネルデバッガを各々動かすことにより、アプリケーションとカーネルランドを、それぞれのデバッガでデバッグする”という意味です。
皆さんご存じの通り、ネイティブコードのアプリケーションの場合には、アプリケーションもカーネルランドも、どちらもカーネルデバッガでデバッグできます。たとえば、アプリケーションからデバイスドライバを呼び出した場合、デバイスドライバのソースコードにブレークポイントを設定してブレークした時、アプリケーションからデバイスドライバまでのコールスタック（「呼び出し履歴」）を見ることができます。アプリケーションがデバイスドライバを呼び出す場合、システムコールを経由しますが、カーネルデバッガは、その経路を把握して、あたかも通常の関数呼び出しのように、コールスタックを表示してくれるのです。僕は、このように、一つのデバッガだけでアプリケーションとカーネルランドをデバッグすることを指して「同時にデバッグする」と表現しています。
一方、マネージドコード（.NET CF）のアプリケーションの場合には、一つのデバッガだけでアプリケーションとカーネルの両方をデバッグすることは、できません。アプリケーションのデバッガとカーネルデバッガを各々動かす必要があるのです。このことを指して、ここでは「一緒にデバッグする」と表現します。
■Ethernet 経由でのアプリケーションデバッガ接続
Ethernet 経由で WinCE/WEC のアプリケーションデバッガを接続する場合は、ActiveSync は使用しません。ActiveSync は、2005年にリリースされた 4.0 から TCP/IP 接続をサポートしていませんから、使えないのです。ActiveSync を使わずに、TCP/IP で WinCE/WEC に接続する手順は、Visual Studio のリファレンスのページで説明されています：
　How to: Connect to Windows CE Device Without ActiveSync
　http://msdn.microsoft.com/en-US/library/ms228708(v=vs.90).aspx
上のページは、Visual Studio 2008 のリファレンスです。日本語の説明を読みたい場合は、Visual Studio 2005 ですが、ユニダックス社の「CE6.0 技術情報」に日本語の説明があります：
　11. アプリケーションのリモートデバッグ
　http://www.unidux.co.jp/embedded/techinfo/ce6/000437.php
上のページで説明されているように、Visual Studio に付属している次の5つのファイルを、WinCE/WEC の /Windows ディレクトリへコピーして、Visual Studio のデバイス設定を行うことにより、TCP/IP でアプリケーションデバッガを接続できます。
　Clientshutdown.exe
　ConmanClient2.exe
　CMaccept.exe
　eDbgTL.dll
　TcpConnectionA.dll
WEC 7 で、AMR9 コアの CPU のボードに接続する場合ですと、上記のファイルは、次の場所にあります。
　C:\Program Files\Common Files\microsoft shared\CoreCon\1.0\Target\wce400\armv5\
これらのファイルを、WinCE/WEC が起動した後に、/Windows ディレクトリに [...]]]></description>
			<content:encoded><![CDATA[<p>今回は、マネージドコードのアプリケーション、つまり、.NET CF のアプリケーションとカーネルランドを、Ethernet 経由で一緒にデバッグする方法を説明します。「一緒にデバッグする」というのは、”「同時にデバッグする」ことはできないが、アプリケーションデバッガとカーネルデバッガを各々動かすことにより、アプリケーションとカーネルランドを、それぞれのデバッガでデバッグする”という意味です。</p>
<p>皆さんご存じの通り、ネイティブコードのアプリケーションの場合には、アプリケーションもカーネルランドも、どちらもカーネルデバッガでデバッグできます。たとえば、アプリケーションからデバイスドライバを呼び出した場合、デバイスドライバのソースコードにブレークポイントを設定してブレークした時、アプリケーションからデバイスドライバまでのコールスタック（「呼び出し履歴」）を見ることができます。アプリケーションがデバイスドライバを呼び出す場合、システムコールを経由しますが、カーネルデバッガは、その経路を把握して、あたかも通常の関数呼び出しのように、コールスタックを表示してくれるのです。僕は、このように、一つのデバッガだけでアプリケーションとカーネルランドをデバッグすることを指して「同時にデバッグする」と表現しています。</p>
<p>一方、マネージドコード（.NET CF）のアプリケーションの場合には、一つのデバッガだけでアプリケーションとカーネルの両方をデバッグすることは、できません。アプリケーションのデバッガとカーネルデバッガを各々動かす必要があるのです。このことを指して、ここでは「一緒にデバッグする」と表現します。</p>
<p>■Ethernet 経由でのアプリケーションデバッガ接続<br />
Ethernet 経由で WinCE/WEC のアプリケーションデバッガを接続する場合は、ActiveSync は使用しません。ActiveSync は、<a href="http://www.pocketpcfaq.com/faqs/activesync/activesync4.0.htm">2005年にリリースされた 4.0 から TCP/IP 接続をサポートしていません</a>から、使えないのです。ActiveSync を使わずに、TCP/IP で WinCE/WEC に接続する手順は、Visual Studio のリファレンスのページで説明されています：</p>
<p>　How to: Connect to Windows CE Device Without ActiveSync<br />
　<a href="http://msdn.microsoft.com/en-US/library/ms228708(v=vs.90).aspx">http://msdn.microsoft.com/en-US/library/ms228708(v=vs.90).aspx</a></p>
<p>上のページは、Visual Studio 2008 のリファレンスです。日本語の説明を読みたい場合は、Visual Studio 2005 ですが、ユニダックス社の「CE6.0 技術情報」に日本語の説明があります：</p>
<p>　11. アプリケーションのリモートデバッグ<br />
　<a href="http://www.unidux.co.jp/embedded/techinfo/ce6/000437.php">http://www.unidux.co.jp/embedded/techinfo/ce6/000437.php</a></p>
<p>上のページで説明されているように、Visual Studio に付属している次の5つのファイルを、WinCE/WEC の /Windows ディレクトリへコピーして、Visual Studio のデバイス設定を行うことにより、TCP/IP でアプリケーションデバッガを接続できます。</p>
<p>　Clientshutdown.exe<br />
　ConmanClient2.exe<br />
　CMaccept.exe<br />
　eDbgTL.dll<br />
　TcpConnectionA.dll</p>
<p>WEC 7 で、AMR9 コアの CPU のボードに接続する場合ですと、上記のファイルは、次の場所にあります。</p>
<p>　C:\Program Files\Common Files\microsoft shared\CoreCon\1.0\Target\wce400\armv5\</p>
<p>これらのファイルを、WinCE/WEC が起動した後に、/Windows ディレクトリに USB メモリなどを使ってコピーするか、または、OS Design の project.bib ファイルを編集して OS イメージに組み込めば、使えます。</p>
<p>または、OS Design のカタログ項目で、&#8221;Target Control Support(Shell.exe)&#8221; か &#8220;Release Directory File System&#8221; を選択している場合は、Flat Release Directory（<a href="http://msdn.microsoft.com/en-us/library/ee479008.aspx">環境変数 _FLATRELEASEDIR</a> が指すディレクトリ）に上記5つのファイルを置いても、使えます。Target Control Support を有効にしてカーネルデバッガを使っている場合には、これが一番お手軽でしょう。</p>
<p>■OS Design から作った SDK との関連づけ<br />
マネージドコードのアプリケーションも、ネイティブコードのアプリケーションも、WinCE/WEC のアプリケーションは、Visual Studio の「スマートデバイス」プロジェクトで作成します。それらのアプリケーションをデバッグする場合は、上で紹介したページで説明されている手順で、WinCE/WEC と接続する必要があります。</p>
<p>Visual Studio 2008 日本語版と WEC 7 の場合ですと、以下のような手順になります。</p>
<ol>
<li>上記の5つのファイルを、WEC 7 の /Windows ディレクトリへコピーする。
<li>WEC 7 を DHCP 有効で動かしている場合は、コマンドプロンプトで ipconfig を実行して、WEC 7 の IP アドレスを調べる。
<li>Visual Studio 2008 の [ツール] > [オプション...] メニューを選択し、「オプション」ダイアログを開く。
<li>「オプション」ダイアログの左端にあるツリービューで、[デバイスツール」を展開し、その下にある「デバイス」を選択する。
<li>「オプション」ダイアログの右端に表示された「デバイス」画面にある、「デバイス(V):」というリストボックスから、アプリケーションのビルドに使った SDK のデバイスを選択する。
<li>「デバイス(V):」リストボックスの右側にある「プロパティ」ボタンをクリックして、選択したデバイスのプロパティダイアログを開く。
<li>プロパティダイアログの「トランスポート(R):」というドロップダウンリストから、「TCP 接続トランスポート」を選択する。
<li>「トランスポート(R):」ドロップダウンリストの右側にある「構成(C)...」ボタンをクリックして、「TCP/IP トランスポートの構成」ダイアログを開く。
<li>「TCP/IP トランスポートの構成」ダイアログにある二つのラジオボタンのうち、「特定の IP アドレスを使用(S):」を選択する。
<li>「特定の IP アドレスを使用（S):」の下にあるエディットフィールド兼ドロップダウンリストに、WEC 7 の IP アドレスを入力して設定する。
<li>「TCP/IP トランスポートの構成」ダイアログ、プロパティダイアログ、および「オプション」ダイアログの OK ボタンを順にクリックして、設定内容を確定する。
<li>WEC 7 で、ConmanClient2.exe と CMaccept.exe を順に実行する。
<li>Visual Studio 2008 の [ツール] > [デバイスへの接続...] メニューを選択する。
</ol>
<p>■ターゲットボードとホスト PC を直結する場合<br />
さて、上の手順では、WinCE/WEC を DHCP 有効で動かしている場合（デフォルトでは、DHCP 有効です）、WinCE/WEC を起動するたびに IP アドレスを調べて Visual Studio の「TCP/IP トランスポート構成」の設定に反映しなければいけません。DHCP 有効ではなく、固定 IP アドレスで動かせば、WinCE/WEC を起動するたびに設定する必要がなくなり、少しだけ手間を減らせます。</p>
<p>あるいは、WinCE/WEC とホスト PC をハブ経由で接続せず、クロスの Ethernet ケーブル（*）で直接接続する場合は、DHCP サーバにアクセスできませんから、WinCE/WEC とホスト PC 共に、固定 IP アドレスを割り当てる必要があります。</p>
<p>※最近のネットワークインタフェースは、ストレートの Ethernet ケーブルで直接接続しても、自動認識してくれるものが多くなっています。従って、WinCE/WEC とホスト PC を Ethernet ケーブルで直接接続する際、クロスケーブルがなければ、ストレートケーブルで接続しても大丈夫な場合があります。手元にクロスケーブルがない場合は、試してみて下さい。<br />
　なお、Ethernet 経由でリモートデバッグする場合、WinCE/WEC とホスト PC をハブ経由で接続するよりも、直接接続する方が、一般的には通信速度が上がりますので、より快適にデバッグできます。</p>
<p>Visual Studio を動かすホスト PC、つまり、Windows 7 や Vista/WindowsXP に固定 IP アドレスを設定する手順は、皆さんご存じだと思いますので、ここでは、WinCE/WEC に固定 IP アドレスを設定する方法を述べます。</p>
<p>WEC 7 と WinCE 6.0 の IP アドレス設定のレジストリは、リファレンスの以下のページで説明されています：</p>
<p>　TCP/IPv4 Configurable Registry Settings (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee494881.aspx">http://msdn.microsoft.com/en-us/library/ee494881.aspx</a></p>
<p>　TCP/IPv4 Configurable Registry Settings (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee494881(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee494881(v=winembedded.60).aspx</a></p>
<p>上のページの中ほどにある、&#8221;Adapter-specific Values&#8221; という表を見て下さい。ここに、&#8221;EnableDHCP&#8221;, &#8220;DefaultGateway&#8221;, &#8220;IpAddress&#8221;, &#8220;Subnetmask&#8221; が載っています。これらのレジストリ項目を使って、WinCE/WEC に固定 IP アドレスを設定できます。レジストリキーは、これらのページに書かれている通り、<br />
　[HKEY_LOCAL_MACHINE\Comm\<アダプタ名>\Parms\Tcpip]<br />
です。たとえば、弊社が提供している <a href="https://armadillo.atmark-techno.com/wince_download_form">Armadillo-400 シリーズ用の BSP</a> であれば、以下の行を .reg ファイルに書いて OS イメージをビルドすると、クラス C の固定 IP アドレス 192.168.0.50 が設定されます：<br />
<code>
<pre>[HKEY_LOCAL_MACHINE\Comm\FEC1\Parms\Tcpip]
    "EnableDHCP"=dword:0
    "DefaultGateway"="192.168.0.1"
    "IpAddress"="192.168.0.50"
    "Subnetmask"="255.255.255.0"</pre>
<p></code><br />
ここまでの設定は、Visual Studio のスマートデバイスプロジェクトのアプリケーションを Ethernet 経由でデバッグするための手順です。次に、今回の本題である、マネージドコード（.NET CF）のアプリケーションをカーネルランドと一緒にデバッグする方法を説明します。</p>
<p>■カーネルデバッガも一緒に動かす方法<br />
今回の冒頭で、「.NET CF のアプリケーションとカーネルランドを一緒にデバッグするとは、アプリケーションデバッガとカーネルデバッガを各々動かすことにより、それぞれをデバッグすること」だと述べました。アプリケーションデバッガとカーネルデバッガを各々動かすというのは、どういうことかと言えば、Visual Studio を二つ起動して、一方でアプリケーションデバッガ、もう一方でカーネルデバッガを動かす、ということです。</p>
<p>以下に、もう少し具体的な手順を述べます。以下のようにすれば、アプリケーションデバッガとカーネルデバッガを各々動かして、.NET CF のアプリケーションとカーネルランドを一緒にデバッグできます。</p>
<ol>
<li>Visual Studio を起動して、OS Design のプロジェクトを開く。
<li>OS Design のプロジェクトをビルドしてできた OS イメージを、ホスト PC と Ethernet 接続したターゲットボードに転送し、カーネルデバッガを有効にして WinCE/WEC を起動する。
<li>Visual Studio をもう一つ起動して、デバッグしたいアプリケーションのプロジェクトを開く。
<li>上で述べた手順を使って、アプリケーションのプロジェクトを開いた Visual Studio から WinCE/WEC に接続する。
<li>アプリケーションのデバッグを開始する。
</ol>
<p>このようにすれば、二つ起動した Visual Studio のうち、OS Design のプロジェクトを開いた方でカーネルデバッガが動き、アプリケーションのプロジェクトを開いた方でアプリケーションデバッガが動きます。なお、アプリケーションデバッガで設定したブレークポイントでアプリケーションが停止した場合、OS（WinCE/WEC）は動作し続けていますが、カーネルデバッガで設定したブレークポイントで停止した場合は、OS 全体が停止しますので、アプリケーションの動作も停止します。これは、アプリケーションデバッガとカーネルデバッガで違う点です。</p>
<p>以上で、アプリケーションとカーネルランドを一緒にデバッグする方法の説明は、一応終わりです。ただし、いくつか補足が必要なので、次回に続けます。もし、上の手順を試してみたが、アプリケーションデバッガとカーネルデバッガを一緒に動かせない、という方は、次回の説明もあわせて読んでみて下さい。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/04/03/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%a8%e3%82%ab%e3%83%bc%e3%83%8d%e3%83%ab%e3%83%a9%e3%83%b3%e3%83%89%e3%82%92%e4%b8%80%e7%b7%92%e3%81%ab%e3%83%87/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>.NET CF アプリケーションからデバッグメッセージ出力</title>
		<link>http://www.stprec.co.jp/ceblog/2012/03/29/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%8b%e3%82%89%e3%83%87%e3%83%90%e3%83%83%e3%82%b0%e3%83%a1%e3%83%83%e3%82%bb%e3%83%bc%e3%82%b8%e5%87%ba%e5%8a%9b/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/03/29/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%8b%e3%82%89%e3%83%87%e3%83%90%e3%83%83%e3%82%b0%e3%83%a1%e3%83%83%e3%82%bb%e3%83%bc%e3%82%b8%e5%87%ba%e5%8a%9b/#comments</comments>
		<pubDate>Thu, 29 Mar 2012 08:42:20 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[.NET Compact Framework]]></category>
		<category><![CDATA[アプリケーション開発]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=318</guid>
		<description><![CDATA[C# や Visual Basic で WinCE/WEC のアプリケーションを開発する際に、デバッグメッセージを出力する方法は、いくつかあります：

System.Console クラスの Write(), WriteLine() を呼び出す
System.Diagnostics.Debug クラスの Write()/WriteIf(), WriteLine()/WriteLineIf() を呼び出す
P/Invoke を使って NKDbgPrintfW() を呼び出す

上に挙げた三つの方法について、それらがどう違うのか、以下に述べます。なお、ここでは、.NET CF (.NET Compact Framework) の 3.5 について記します。
■System.Console クラスを使う場合
このクラスのメソッド一覧は、次のリファレンスページに載っています：
　Console Methods
　http://msdn.microsoft.com/en-us/library/system.console_methods(v=vs.90).aspx
System.Console クラスの Write() や WriteLine() を呼び出すと、コンソールウィンドウが開いて、引数に渡した文字列が表示されます。これは、ネイティブコードの場合に、printf() などを使って標準出力へ出力した場合と同じ振る舞いです。クラス名の通り、コンソールデバイスに出力する API というわけです。
■System.Diagnostics.Debug クラスを使う場合
このクラスのメソッド一覧は、次のリファレンスページに載っています：
　Debug Methods
　http://msdn.microsoft.com/en-us/library/system.diagnostics.debug_methods(v=vs.90).aspx
System.Diagnostics.Debug クラスの Write() や WriteLine() を呼び出すと、アプリケーションをデバッガから実行している場合には、デバッガの出力ウィンドウに文字列が出力されます。デバッガから実行していない場合（通常実行の場合）には、何も起きません。この振る舞いは、OutputDebugString() を呼び出した場合と同様です。
このクラスの Write() や WriteLine() の呼び出しが、OutputDebugString() の呼び出しと同様になるのは、System.Diagnostics.DefaultTraceListener クラスの働きによるものです。DefaultTraceListener クラスの説明は、次のリファレンスページに載っています。
　DefaultTraceListener Class
　http://msdn.microsoft.com/en-us/library/system.diagnostics.defaulttracelistener(v=vs.90).aspx
System.Diagnostics.DefaultTraceListener のインスタンスは、System.Diagnostics.Debug クラスの Listeners プロパティのデフォルト要素であり、そのため、Write() [...]]]></description>
			<content:encoded><![CDATA[<p>C# や Visual Basic で WinCE/WEC のアプリケーションを開発する際に、デバッグメッセージを出力する方法は、いくつかあります：</p>
<ul>
<li>System.Console クラスの Write(), WriteLine() を呼び出す
<li>System.Diagnostics.Debug クラスの Write()/WriteIf(), WriteLine()/WriteLineIf() を呼び出す
<li>P/Invoke を使って NKDbgPrintfW() を呼び出す
</ul>
<p>上に挙げた三つの方法について、それらがどう違うのか、以下に述べます。なお、ここでは、.NET CF (.NET Compact Framework) の 3.5 について記します。</p>
<p>■System.Console クラスを使う場合<br />
このクラスのメソッド一覧は、次のリファレンスページに載っています：</p>
<p>　Console Methods<br />
　<a href="http://msdn.microsoft.com/en-us/library/system.console_methods(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/system.console_methods(v=vs.90).aspx</a></p>
<p>System.Console クラスの Write() や WriteLine() を呼び出すと、コンソールウィンドウが開いて、引数に渡した文字列が表示されます。これは、ネイティブコードの場合に、printf() などを使って標準出力へ出力した場合と同じ振る舞いです。クラス名の通り、コンソールデバイスに出力する API というわけです。</p>
<p>■System.Diagnostics.Debug クラスを使う場合<br />
このクラスのメソッド一覧は、次のリファレンスページに載っています：</p>
<p>　Debug Methods<br />
　<a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.debug_methods(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/system.diagnostics.debug_methods(v=vs.90).aspx</a></p>
<p>System.Diagnostics.Debug クラスの Write() や WriteLine() を呼び出すと、アプリケーションをデバッガから実行している場合には、デバッガの出力ウィンドウに文字列が出力されます。デバッガから実行していない場合（通常実行の場合）には、何も起きません。この振る舞いは、<a href="http://msdn.microsoft.com/en-us/library/ee488209.aspx">OutputDebugString()</a> を呼び出した場合と同様です。</p>
<p>このクラスの Write() や WriteLine() の呼び出しが、OutputDebugString() の呼び出しと同様になるのは、System.Diagnostics.DefaultTraceListener クラスの働きによるものです。DefaultTraceListener クラスの説明は、次のリファレンスページに載っています。</p>
<p>　DefaultTraceListener Class<br />
　<a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.defaulttracelistener(v=vs.90).aspx">http://msdn.microsoft.com/en-us/library/system.diagnostics.defaulttracelistener(v=vs.90).aspx</a></p>
<p>System.Diagnostics.DefaultTraceListener のインスタンスは、System.Diagnostics.Debug クラスの Listeners プロパティのデフォルト要素であり、そのため、Write() や WriteLine() によるデバッグメッセージ出力は、通常は System.Diagnostics.DefaultTraceListener の Write(), WriteLine() に渡されます。そして、System.Diagnostics.DefaultTraceListener は、Write() や WriteLine() に渡された文字列を引数にして OutputDebugString() を呼び出す、というわけです。</p>
<p>ただし、ネイティブコードで  OutputDebugString() を直接呼び出した場合とは異なり、アプリケーションをデバッガから実行していないと、System.Diagnostics.Debug の Write() や WriteLine() を呼び出しても、一切出力されません。一方、ネイティブコードから OutputDebugString() を呼び出した場合は、アプリケーションデバッガから実行していなくても、WinCE/WEC のカーネルデバッガを実行していれば、カーネルデバッガの出力ウィンドウに文字列が出力されます。.NET CF のアプリケーションから P/Invoke で直接 OutputDebugString() を呼び出した場合にどうなるかは、確認していませんが、おそらく、ネイティブコードから OutputDebugString() を呼び出した場合と同じになるのではないかと思います。興味のある方は、試してみて下さい。</p>
<p>System.Diagnostics.Debug クラスの使い方については、次のサポートページも参考になるでしょう：</p>
<p>　How to trace and debug in Visual C#<br />
　<a href="http://support.microsoft.com/kb/815788/en-us">http://support.microsoft.com/kb/815788/en-us</a></p>
<p>■NKDbgPrintfW() を P/Invoke で呼び出す場合<br />
さて、ネイティブコードの場合、デバッグメッセージの出力は、<a href="http://msdn.microsoft.com/en-us/library/ee488430.aspx">DEBUGMSG()</a> マクロを使うのが普通です。C# や Visual Basic では、このマクロは使えませんので、その代わりに、DEBUGMSG() マクロが使っている <a href="http://msdn.microsoft.com/en-us/library/ee488771.aspx">NKDbgPrintfW()</a> を、P/Invoke で直接呼び出す必要があります。</p>
<p>たとえば、次のページにあるフォーラムの質問でも、そのような回答がなされています：</p>
<p>　DebugMsg macro in C#<br />
　<a href="http://us.generation-nt.com/answer/debugmsg-macro-c-help-51715972.html">http://us.generation-nt.com/answer/debugmsg-macro-c-help-51715972.html</a></p>
<p>.NET CF アプリケーションから、P/Invoke で NKDbgPrintfW() を直接呼び出した場合は、アプリケーションをデバッガから実行していない場合でも、WinCE/WEC のカーネルデバッガを実行していれば、カーネルデバッガの出力ウィンドウに文字列が出力されます。.NET CF アプリケーションを、ネイティブコードのアプリケーションやライブラリと一緒にデバッグしていて、追跡したい個所の呼び出しタイミングの関係を知りたい場合には、NKDbgPrintfW() を使うと、デバッグメッセージをカーネルデバッガの出力に渡すことができます。つまり、.NET CF アプリケーションのデバッグメッセージも、ネイティブコードのデバッグメッセージも（さらには、デバイスドライバのデバッグメッセージも）同じ出力に出せますので、それらを同時に追跡したい場合には、便利でしょう。</p>
<p>なお、P/Invoke で NKDbgPrintfW() を直接呼び出すと、ネイティブコードで DEBUGMSG() マクロを使った場合とは異なり、リリースビルドでもデバッグメッセージが出力されてしまいます。その点は、注意して下さい。デバッグビルドの時にだけデバッグメッセージが出力されるようにするには、<a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute(v=vs.90).aspx">System.Diagnostics.ConditionalAttribute</a> を使ってデバッグビルドに対してのみ有効にするか、または、#if &#8230; #endif を使って条件コンパイル指定して下さい。ちなみに、System.Diagnostics.Debug クラスの Write() や WriteLine() では、System.Diagnostics.ConditionalAttribute を使っています。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/03/29/net-cf-%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%8b%e3%82%89%e3%83%87%e3%83%90%e3%83%83%e3%82%b0%e3%83%a1%e3%83%83%e3%82%bb%e3%83%bc%e3%82%b8%e5%87%ba%e5%8a%9b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WinCE/WEC のウォッチドッグタイマ機能（補足）</title>
		<link>http://www.stprec.co.jp/ceblog/2012/03/27/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd%ef%bc%88%e8%a3%9c%e8%b6%b3%ef%bc%89/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/03/27/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd%ef%bc%88%e8%a3%9c%e8%b6%b3%ef%bc%89/#comments</comments>
		<pubDate>Tue, 27 Mar 2012 14:04:21 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[OS の内部動作]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=305</guid>
		<description><![CDATA[前回のエントリでは、WinCE/WEC のウォッチドッグタイマ機能について、ハードウェアタイマとソフトウェアタイマの二種類があることを述べました。そして、ハードウェアタイマとソフトウェアタイマでは、ソフトウェアタイマの方が、より柔軟であり、以下の点が異なると説明しました：

ソフトウェアタイマは、複数の監視対象を設定できる（タイマを複数作成できる）
ソフトウェアタイマは、タイマ満了時に発火させるアクションとして、デバイスをリセットする以外の動作を設定できる
ソフトウェアタイマの場合は、カーネルではなく、監視対象のアプリケーションやスレッドが、タイマのリフレッシュ動作を行う

今回のエントリでは、上に示した、ハードウェアタイマとソフトウェアタイマの違いのうち、2. について補足します。ウォッチドッグタイマ機能において、ソフトウェアタイマとハードウェアタイマのどちらも、タイマの満了時にデバイスをリセットできますが、リセット動作の詳細は、異なるのです。
■ハードウェアタイマについての注意点
前回のエントリでは、ソフトウェアタイマがタイマ満了時に発火させることのできる三通りのアクションのうち、WDOG_RESET_DEVICE のことを、「OS をリブート（デバイスをリセット）する」動作だと説明しました。この「OS をリブート」というのが、ハードウェアタイマとは違う点です。ハードウェアのウォッチドッグタイマは、プロセッサを強制リセットします。強制リセットする際、OS がどのように動作しているのかは関知しません。
ここで、ハードウェアタイマの場合には、WinCE/WEC のカーネルが（より正確には、ウォッチドッグタイマ用のカーネルスレッドが）タイマのリフレッシュ動作を行います。従って、ハードウェアタイマが満了したということは、カーネルが動作を停止しているということですから、プロセッサを強制リセットするのが妥当です。通常は、その通りです。しかし、何らかの不具合で OS 全体がフリーズしているわけではないのに、カーネルが動作を停止する場合もあるのです。どんな場合でしょうか？
それは、カーネルデバッガを使っている時です。カーネルデバッガを使ってデバッグ動作している状態では、ブレークポイントにヒットすると、OS 全体の動作が停止します。OS 全体の動作が停止した状態であっても、ハードウェアタイマは動作し続けますから、ハードウェアタイマの満了時間が過ぎると、その時点で、ハードウェアのウォッチドッグタイマの働きにより、プロセッサが強制リセットされてしまうのです。ソフトウェアタイマの場合には、タイマの動作も停止していますから、そのようなことは、起きません。
カーネルデバッガを使ったデバッグ中に、予期せぬ強制リセットが起きしてしまわないよう、カーネルデバッガを使う時にはハードウェアタイマを動かさないか、または、タイマの満了時間をできるだけ長い値に設定して下さい。
■ソフトウェアタイマによるリセット動作
さて、ソフトウェアタイマのリセット動作は、「OS をリブート」する点がハードウェアタイマと違うと上で述べました。これについて、説明します。
まず、ハードウェアタイマのリセット動作では、プロセッサのリセット信号線を使って、ハードウェアによるリセットを行います。つまり、ハードウェアのウォッチドッグタイマが、プロセッサに対してリセット信号を出力する（プロセッサのリセット入力信号線に供給する電圧の論理値を、リセット用の値に変化させる）ことによって、プロセッサを強制リセットします。
一方、ソフトウェアタイマでは、ウォッチドッグタイマ用のカーネルスレッドが、OS のリブート処理を始動することによって、リセット動作を起こします。具体的には、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出します。興味のある方は、ソースコードをご覧になってみて下さい。ウォッチドッグタイマ用のカーネルスレッドのソースは、前回述べた通り、
　%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/NK/KERNEL/watchdog.c
にあり、WatchDogTimerThrd() が、そのスレッドが実行する手続きです。WatchDogTimerThrd() を見ると、ソフトウェアタイマのどれか一つが満了すると、タイマの状態が WD_STATE_SIGNALED になり、その結果、そのタイマに設定されたアクションが実行されることが分かります。タイマに設定されたアクションの実行は、WDTakeDfltAction() を呼び出すことによって行われますが、WDTakeDfltAction() では、タイマに設定されたアクションが WDOG_RESET_DEVICE の場合、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出す実装になっています。
IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出すことによって OS のリブート動作が起きる仕組みについては、2009/07/20 のエントリ（「アプリケーションからリブート（または電源 OFF）～その１（1/2）」）に書いています。もし興味があれば、お読みになってみて下さい。
2009/07/20 のエントリで書いたように、カーネルスレッドが、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出した場合には、レジストリやファイルシステムのフラッシュ動作が実行されたのち、OALIoCtlHalReboot() が呼び出されます。OALIoCtlHalReboot() は、カーネル移植レイヤ（OAL）で実装されており、プロセッサごとに異なるリセット動作を実行します。一般的には、ハードウェアのウォッチドッグタイマの発火時間を短い値にセットしたのち、無限ループを実行し、ハードウェアタイマによってプロセッサを強制リセットさせます。
■ハードウェアタイマについて、もう一度だけ注意
ここで重要なことは、ソフトウェアタイマによるリセット動作では、プロセッサをリセットする前に、レジストリやファイルシステムのフラッシュ動作を実行する、という点です。このことにより、ウォッチドッグタイマが発火してリセット動作が起きた際に、永続記憶域上のレジストリ内容やファイルシステムが、壊れてしまう危険性が抑えられるのです。ソフトウェアタイマが発火する原因になった監視対象のアプリケーションが、ファイル操作を行っていた場合には、ファイル内容が壊れてしまう可能性はあります（ファイルの書き込みを行っている途中でフリーズした場合など）。しかし、ディレクトリが壊れて読めなくなってしまったりするといった致命的なことは、起きないようにするというわけです。
この点も、ハードウェアタイマとは違う点です。通常動作中に OS 全体がフリーズしてしまった場合は、強制リセットが起きるのは仕方ありません。しかし、カーネルデバッガを使ったデバッグ中に、ハードウェアタイマが満了してしまい、予期せぬ強制リセットによってレジストリやファイルシステムが壊れてしまう、というようなことが起きないよう、ハードウェアタイマを使う場合には、ご注意ください。
]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.stprec.co.jp/ceblog/2012/03/19/">前回のエントリ</a>では、WinCE/WEC のウォッチドッグタイマ機能について、ハードウェアタイマとソフトウェアタイマの二種類があることを述べました。そして、ハードウェアタイマとソフトウェアタイマでは、ソフトウェアタイマの方が、より柔軟であり、以下の点が異なると説明しました：</p>
<ol>
<li>ソフトウェアタイマは、複数の監視対象を設定できる（タイマを複数作成できる）
<li>ソフトウェアタイマは、タイマ満了時に発火させるアクションとして、デバイスをリセットする以外の動作を設定できる
<li>ソフトウェアタイマの場合は、カーネルではなく、監視対象のアプリケーションやスレッドが、タイマのリフレッシュ動作を行う
</ol>
<p>今回のエントリでは、上に示した、ハードウェアタイマとソフトウェアタイマの違いのうち、2. について補足します。ウォッチドッグタイマ機能において、ソフトウェアタイマとハードウェアタイマのどちらも、タイマの満了時にデバイスをリセットできますが、リセット動作の詳細は、異なるのです。</p>
<p>■ハードウェアタイマについての注意点<br />
前回のエントリでは、ソフトウェアタイマがタイマ満了時に発火させることのできる三通りのアクションのうち、WDOG_RESET_DEVICE のことを、「OS をリブート（デバイスをリセット）する」動作だと説明しました。この「OS をリブート」というのが、ハードウェアタイマとは違う点です。ハードウェアのウォッチドッグタイマは、プロセッサを強制リセットします。強制リセットする際、OS がどのように動作しているのかは関知しません。</p>
<p>ここで、ハードウェアタイマの場合には、WinCE/WEC のカーネルが（より正確には、ウォッチドッグタイマ用のカーネルスレッドが）タイマのリフレッシュ動作を行います。従って、ハードウェアタイマが満了したということは、カーネルが動作を停止しているということですから、プロセッサを強制リセットするのが妥当です。通常は、その通りです。しかし、何らかの不具合で OS 全体がフリーズしているわけではないのに、カーネルが動作を停止する場合もあるのです。どんな場合でしょうか？</p>
<p>それは、カーネルデバッガを使っている時です。カーネルデバッガを使ってデバッグ動作している状態では、ブレークポイントにヒットすると、OS 全体の動作が停止します。OS 全体の動作が停止した状態であっても、ハードウェアタイマは動作し続けますから、ハードウェアタイマの満了時間が過ぎると、その時点で、ハードウェアのウォッチドッグタイマの働きにより、プロセッサが強制リセットされてしまうのです。ソフトウェアタイマの場合には、タイマの動作も停止していますから、そのようなことは、起きません。</p>
<p>カーネルデバッガを使ったデバッグ中に、予期せぬ強制リセットが起きしてしまわないよう、カーネルデバッガを使う時にはハードウェアタイマを動かさないか、または、タイマの満了時間をできるだけ長い値に設定して下さい。</p>
<p>■ソフトウェアタイマによるリセット動作<br />
さて、ソフトウェアタイマのリセット動作は、「OS をリブート」する点がハードウェアタイマと違うと上で述べました。これについて、説明します。</p>
<p>まず、ハードウェアタイマのリセット動作では、プロセッサのリセット信号線を使って、ハードウェアによるリセットを行います。つまり、ハードウェアのウォッチドッグタイマが、プロセッサに対してリセット信号を出力する（プロセッサのリセット入力信号線に供給する電圧の論理値を、リセット用の値に変化させる）ことによって、プロセッサを強制リセットします。</p>
<p>一方、ソフトウェアタイマでは、ウォッチドッグタイマ用のカーネルスレッドが、OS のリブート処理を始動することによって、リセット動作を起こします。具体的には、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出します。興味のある方は、ソースコードをご覧になってみて下さい。ウォッチドッグタイマ用のカーネルスレッドのソースは、前回述べた通り、<br />
　%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/NK/KERNEL/watchdog.c<br />
にあり、WatchDogTimerThrd() が、そのスレッドが実行する手続きです。WatchDogTimerThrd() を見ると、ソフトウェアタイマのどれか一つが満了すると、タイマの状態が WD_STATE_SIGNALED になり、その結果、そのタイマに設定されたアクションが実行されることが分かります。タイマに設定されたアクションの実行は、WDTakeDfltAction() を呼び出すことによって行われますが、WDTakeDfltAction() では、タイマに設定されたアクションが WDOG_RESET_DEVICE の場合、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出す実装になっています。</p>
<p>IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出すことによって OS のリブート動作が起きる仕組みについては、2009/07/20 のエントリ（「<a href="http://www.stprec.co.jp/ceblog/2009/07/20/">アプリケーションからリブート（または電源 OFF）～その１（1/2）</a>」）に書いています。もし興味があれば、お読みになってみて下さい。</p>
<p>2009/07/20 のエントリで書いたように、カーネルスレッドが、IOCTL_HAL_REBOOT を引数として KernelIoctl() を呼び出した場合には、レジストリやファイルシステムのフラッシュ動作が実行されたのち、OALIoCtlHalReboot() が呼び出されます。OALIoCtlHalReboot() は、カーネル移植レイヤ（OAL）で実装されており、プロセッサごとに異なるリセット動作を実行します。一般的には、ハードウェアのウォッチドッグタイマの発火時間を短い値にセットしたのち、無限ループを実行し、ハードウェアタイマによってプロセッサを強制リセットさせます。</p>
<p>■ハードウェアタイマについて、もう一度だけ注意<br />
ここで重要なことは、ソフトウェアタイマによるリセット動作では、プロセッサをリセットする前に、レジストリやファイルシステムのフラッシュ動作を実行する、という点です。このことにより、ウォッチドッグタイマが発火してリセット動作が起きた際に、永続記憶域上のレジストリ内容やファイルシステムが、壊れてしまう危険性が抑えられるのです。ソフトウェアタイマが発火する原因になった監視対象のアプリケーションが、ファイル操作を行っていた場合には、ファイル内容が壊れてしまう可能性はあります（ファイルの書き込みを行っている途中でフリーズした場合など）。しかし、ディレクトリが壊れて読めなくなってしまったりするといった致命的なことは、起きないようにするというわけです。</p>
<p>この点も、ハードウェアタイマとは違う点です。通常動作中に OS 全体がフリーズしてしまった場合は、強制リセットが起きるのは仕方ありません。しかし、カーネルデバッガを使ったデバッグ中に、ハードウェアタイマが満了してしまい、予期せぬ強制リセットによってレジストリやファイルシステムが壊れてしまう、というようなことが起きないよう、ハードウェアタイマを使う場合には、ご注意ください。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/03/27/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd%ef%bc%88%e8%a3%9c%e8%b6%b3%ef%bc%89/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WinCE/WEC のウォッチドッグタイマ機能</title>
		<link>http://www.stprec.co.jp/ceblog/2012/03/19/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/03/19/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd/#comments</comments>
		<pubDate>Mon, 19 Mar 2012 07:11:28 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[OS の内部動作]]></category>
		<category><![CDATA[カーネルモジュールの実装]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=299</guid>
		<description><![CDATA[■ウォッチドッグタイマの必要性
組み込み機器、特に、PC のような対話的な操作ができない、ヘッドレスのデバイスなどでは、より高い信頼性や耐障害性が求められます。逆に、タッチパネルディスプレイのような、表示と入力のインタフェースを備えたデバイスであれば、PC と同様に、アプリケーションがフリーズするなど異常が起きた時に、デバイスを操作するユーザが異常に気づき、アプリケーションを終了したり、デバイスをリセットしたりして、すぐに対処できます。
しかし、ユーザが対話的に操作せずに、常時稼働するタイプの組み込み機器の場合には、アプリケーションや OS がフリーズした場合、自動的に異常を復旧する仕組みがないと、機器が異常を起こしたままになってしまいます。その結果、深刻な問題を引き起こすおそれがあります。たとえば、自動販売機がフリーズして動かなくなってしまったり、工場のラインの自動制御システムで、制御装置のアプリケーションがフリーズした場合のことを、考えてみて下さい。
アプリケーションや OS がフリーズした場合に、デバイスをリセットしたり、あるいは、フリーズしたアプリケーションを終了して再始動したりするための仕組みとして、ウォッチドッグタイマがあります。Linux であれば、/dev/watchdog として提供されており、ウォッチドッグタイマを更新する watchdog デーモンも提供されています：
　The Linux Watchdog driver API
　http://kernel.org/doc/Documentation/watchdog/watchdog-api.txt
　watchdog(8)
　http://linux.die.net/man/8/watchdog
組み込み機器向けのプロセッサは、ウォッチドッグタイマを内蔵しているものが多く、それを利用するのが一般的です。ハードウェアのウォッチドッグタイマ機能がない場合には、OS のカーネルで、ソフトウェアによるウォッチドッグタイマ機能が提供されることもあります。Linux の場合、/dev/watchdog のウォッチドッグタイマ機能は、ハードウェアで実装されることもあれば、/dev/watchdog はデフォルト実装のカーネル内部のソフトウェアタイマとして提供し、プロセッサ内蔵のウォッチドッグタイマに対しては、別途ブートローダで設定する、という仕組みで提供されることもあるようです。PC 用の Linux の場合、/dev/watchdog は、BIOS の機能を使ったハードウェアタイマとして実装されているようです。
■ハードウェアタイマとソフトウェアタイマ
ウォッチドッグタイマ機能の、ハードウェアによる実装（ハードウェアタイマ）とソフトウェアによる実装（ソフトウェアタイマ）の違いは、何でしょう？
答えは、OS がフリーズした場合に動作できるかどうかです。ハードウェアタイマの場合は、OS が動作しているかどうかに関係なく、独立したハードウェアの機能として動作しますから、たとえ OS 全体がフリーズしていても、設定した通りに動作します。つまり、設定したタイマ満了時間以内にタイマを更新（リフレッシュ）しなければ、タイマが満了した時点で、プロセッサが強制リセットされます。一方、ソフトウェアタイマの場合は、OS 全体がフリーズすると、タイマの動作も停止してしまいますから、タイマ満了時間が過ぎても、何も起きません。つまり、ソフトウェア実装のウォッチドッグタイマ機能は、OS がフリーズした場合には、役に立たないのです。
■ソフトウェアタイマの長所
OS 全体がフリーズした場合（カーネルのスケジューラも停止した場合）には機能しない、という点は、ソフトウェアタイマの短所です。一方、長所もあります。ハードウェアタイマは、一つしか満了時間を設定できませんが、ソフトウェアタイマには、ハードウェアの制約がありませんので、複数のタイマを設定できるように実装することが可能です。
ウォッチドッグタイマを使う目的は、OS や、特定のアプリケーション（プロセス）、あるいはスレッドが、正常に動作し続けているかどうかを監視することにあります。ウォッチドッグタイマが満了した場合、何らかの障害が起きて、それらの監視対象が動作していないということを意味します。そのため、ウォッチドッグタイマが満了した場合は、OS をリブートするなどの、異常からの復旧処理を発火させる、というわけです。
さて、ソフトウェアタイマによって、複数のウォッチドッグタイマを設定できるということは、監視対象を複数にできる、ということです。つまり、複数のウォッチドッグタイマを設定できるなら、常時稼働していなければならないアプリケーション（プロセス）やスレッドが複数ある場合に、それらに対して個別にウォッチドッグタイマを設定し、どれか一つでも動作していないことが検出されたら、復旧処理を発火できます。WinCE/WEC では、それが可能です。さらに、ウォッチドッグタイマの満了時に発火させる復旧処理として、OS をリブートする以外の動作を設定できます。
■WinCE/WEC のウォッチドッグタイマ機能
WinCE/WEC のウォッチドッグタイマ機能には、ハードウェアタイマとソフトウェアタイマがあり、ソフトウェアタイマは、カーネルの API を使って利用できます。ソフトウェア実装のウォッチドッグタイマについては、リファレンスの次のページをご覧下さい：
　CreateWatchDogTimer (Windows Embedded CE 6.0)
　http://msdn.microsoft.com/en-US/library/ee482966(v=winembedded.60).aspx
　CreateWatchDogTimer (Windows Embedded Compact 7)
　http://msdn.microsoft.com/en-us/library/ee482966.aspx
上のページで説明されている CreateWatchDogTimer() を使って、ソフトウェア実装のウォッチドッグタイマを設定できます。この関数では、タイマの満了時間に加え、タイマ満了時に発火させるアクションを指定できます。指定できるアクションは、次の三つです：

WDOG_KILL_PROCESS 　　　監視対象のプロセスを強制終了する
WDOG_NO_DFLT_ACTION 　何もしない
WDOG_RESET_DEVICE 　　　OS をリブート（デバイスをリセット）する

CreateWatchDogTimer() は、戻り値として、ウォッチドッグタイマのハンドルを返します。このハンドルは、non signaled [...]]]></description>
			<content:encoded><![CDATA[<p>■ウォッチドッグタイマの必要性<br />
組み込み機器、特に、PC のような対話的な操作ができない、ヘッドレスのデバイスなどでは、より高い信頼性や耐障害性が求められます。逆に、タッチパネルディスプレイのような、表示と入力のインタフェースを備えたデバイスであれば、PC と同様に、アプリケーションがフリーズするなど異常が起きた時に、デバイスを操作するユーザが異常に気づき、アプリケーションを終了したり、デバイスをリセットしたりして、すぐに対処できます。</p>
<p>しかし、ユーザが対話的に操作せずに、常時稼働するタイプの組み込み機器の場合には、アプリケーションや OS がフリーズした場合、自動的に異常を復旧する仕組みがないと、機器が異常を起こしたままになってしまいます。その結果、深刻な問題を引き起こすおそれがあります。たとえば、自動販売機がフリーズして動かなくなってしまったり、工場のラインの自動制御システムで、制御装置のアプリケーションがフリーズした場合のことを、考えてみて下さい。</p>
<p>アプリケーションや OS がフリーズした場合に、デバイスをリセットしたり、あるいは、フリーズしたアプリケーションを終了して再始動したりするための仕組みとして、ウォッチドッグタイマがあります。Linux であれば、/dev/watchdog として提供されており、ウォッチドッグタイマを更新する watchdog デーモンも提供されています：</p>
<p>　The Linux Watchdog driver API<br />
　<a href="http://kernel.org/doc/Documentation/watchdog/watchdog-api.txt">http://kernel.org/doc/Documentation/watchdog/watchdog-api.txt</a></p>
<p>　watchdog(8)<br />
　<a href="http://linux.die.net/man/8/watchdog">http://linux.die.net/man/8/watchdog</a></p>
<p>組み込み機器向けのプロセッサは、ウォッチドッグタイマを内蔵しているものが多く、それを利用するのが一般的です。ハードウェアのウォッチドッグタイマ機能がない場合には、OS のカーネルで、ソフトウェアによるウォッチドッグタイマ機能が提供されることもあります。Linux の場合、/dev/watchdog のウォッチドッグタイマ機能は、ハードウェアで実装されることもあれば、/dev/watchdog はデフォルト実装のカーネル内部のソフトウェアタイマとして提供し、プロセッサ内蔵のウォッチドッグタイマに対しては、別途ブートローダで設定する、という仕組みで提供されることもあるようです。PC 用の Linux の場合、/dev/watchdog は、BIOS の機能を使ったハードウェアタイマとして実装されているようです。</p>
<p>■ハードウェアタイマとソフトウェアタイマ<br />
ウォッチドッグタイマ機能の、ハードウェアによる実装（ハードウェアタイマ）とソフトウェアによる実装（ソフトウェアタイマ）の違いは、何でしょう？</p>
<p>答えは、OS がフリーズした場合に動作できるかどうかです。ハードウェアタイマの場合は、OS が動作しているかどうかに関係なく、独立したハードウェアの機能として動作しますから、たとえ OS 全体がフリーズしていても、設定した通りに動作します。つまり、設定したタイマ満了時間以内にタイマを更新（リフレッシュ）しなければ、タイマが満了した時点で、プロセッサが強制リセットされます。一方、ソフトウェアタイマの場合は、OS 全体がフリーズすると、タイマの動作も停止してしまいますから、タイマ満了時間が過ぎても、何も起きません。つまり、ソフトウェア実装のウォッチドッグタイマ機能は、OS がフリーズした場合には、役に立たないのです。</p>
<p>■ソフトウェアタイマの長所<br />
OS 全体がフリーズした場合（カーネルのスケジューラも停止した場合）には機能しない、という点は、ソフトウェアタイマの短所です。一方、長所もあります。ハードウェアタイマは、一つしか満了時間を設定できませんが、ソフトウェアタイマには、ハードウェアの制約がありませんので、複数のタイマを設定できるように実装することが可能です。</p>
<p>ウォッチドッグタイマを使う目的は、OS や、特定のアプリケーション（プロセス）、あるいはスレッドが、正常に動作し続けているかどうかを監視することにあります。ウォッチドッグタイマが満了した場合、何らかの障害が起きて、それらの監視対象が動作していないということを意味します。そのため、ウォッチドッグタイマが満了した場合は、OS をリブートするなどの、異常からの復旧処理を発火させる、というわけです。</p>
<p>さて、ソフトウェアタイマによって、複数のウォッチドッグタイマを設定できるということは、監視対象を複数にできる、ということです。つまり、複数のウォッチドッグタイマを設定できるなら、常時稼働していなければならないアプリケーション（プロセス）やスレッドが複数ある場合に、それらに対して個別にウォッチドッグタイマを設定し、どれか一つでも動作していないことが検出されたら、復旧処理を発火できます。WinCE/WEC では、それが可能です。さらに、ウォッチドッグタイマの満了時に発火させる復旧処理として、OS をリブートする以外の動作を設定できます。</p>
<p>■WinCE/WEC のウォッチドッグタイマ機能<br />
WinCE/WEC のウォッチドッグタイマ機能には、ハードウェアタイマとソフトウェアタイマがあり、ソフトウェアタイマは、カーネルの API を使って利用できます。ソフトウェア実装のウォッチドッグタイマについては、リファレンスの次のページをご覧下さい：</p>
<p>　CreateWatchDogTimer (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee482966(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee482966(v=winembedded.60).aspx</a></p>
<p>　CreateWatchDogTimer (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee482966.aspx">http://msdn.microsoft.com/en-us/library/ee482966.aspx</a></p>
<p>上のページで説明されている CreateWatchDogTimer() を使って、ソフトウェア実装のウォッチドッグタイマを設定できます。この関数では、タイマの満了時間に加え、タイマ満了時に発火させるアクションを指定できます。指定できるアクションは、次の三つです：</p>
<ul>
<li>WDOG_KILL_PROCESS 　　　監視対象のプロセスを強制終了する</li>
<li>WDOG_NO_DFLT_ACTION 　何もしない</li>
<li>WDOG_RESET_DEVICE 　　　OS をリブート（デバイスをリセット）する</li>
</ul>
<p>CreateWatchDogTimer() は、戻り値として、ウォッチドッグタイマのハンドルを返します。このハンドルは、non signaled な状態ですが、タイマが満了した時点で、signaled な状態にセットされます。タイマ満了時に発火させるアクションとして、WDOG_NO_DFLT_ACTION を指定すると、ウォッチドッグタイマのハンドルに対して WaitForSingleObject() を呼び出すことにより、タイマが満了したことを検出できます。アプリケーション内で、特定のスレッドが動作しなくなったことを検出したいなどの場合には、WDOG_NO_DFLT_ACTION を使うのが便利でしょう。</p>
<p>CreateWatchDogTimer() で生成したウォッチドッグタイマを更新（リフレッシュ）するには、RefreshWatchDogTimer() を呼び出します。ウォッチドッグタイマを生成した後、StartWatchDogTimer() でタイマを始動したら、CreateWatchDogTimer() の dwPeriod 引数で指定したタイムアウト時間よりも短い間隔で、RefreshWatchDogTimer() を繰り返し呼び出さなければいけません。そうしないと、タイマが満了してしまいます。</p>
<p>次に、ハードウェアタイマについて見てみましょう。</p>
<p>ハードウェアタイマに対しては、API は提供されておらず、実装用のインタフェースが、カーネル移植レイヤ（OAL）で定義されています。ハードウェアがウォッチドッグタイマをサポートしていない場合は、OAL の初期化関数である OEMInit() の中で、ウォッチドッグタイマのインタフェース設定を変更せず、デフォルトのままにします。デフォルトでは、ハードウェアタイマ機能は、使用されません。ハードウェアタイマを有効にする場合は、OAL において、ハードウェアタイマの初期化処理とリフレッシュ処理を実装し、ウォッチドッグタイマのインタフェースを設定します。具体的には、OEMGLOBAL 構造体の dwWatchDogPeriod と pfnRefreshWatchDog を、OEMInit() で設定し、pfnRefreshWatchDog に設定した関数で、リフレッシュ処理（および、必要ならハードウェアタイマの初期化処理も）を実行します。 dwWatchDogPeriod は、デフォルトでは 0 に設定されており、この値が 0 の場合は、ハードウェアタイマが存在しないものとして扱われます。なお、pfnRefreshWatchDog のデフォルト値は、NULL ではなく、何もしない関数を指す関数ポインタです。</p>
<p>　dwOEMWatchDogPeriod (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee479342(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee479342(v=winembedded.60).aspx</a></p>
<p>　pfnOEMRefreshWatchDog (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee479294(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee479294(v=winembedded.60).aspx</a></p>
<p>　OEMGLOBAL (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee478176(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee478176(v=winembedded.60).aspx</a></p>
<p>上は、WinCE 6.0 のリファレンスです。WEC 7 のリファレンスは、以下にあります。</p>
<p>　dwWatchDogPeriod (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee479342.aspx">http://msdn.microsoft.com/en-us/library/ee479342.aspx</a></p>
<p>　OEMRefreshWatchDog (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee479294.aspx">http://msdn.microsoft.com/en-us/library/ee479294.aspx</a></p>
<p>　OEMGLOBAL (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee478176.aspx">http://msdn.microsoft.com/en-us/library/ee478176.aspx</a></p>
<p>ハードウェアタイマに対しては、OAL で設定したリフレッシュ間隔（dwWatchDogPeriod）以内の間隔で、WinCE/WEC カーネルが定期的に pfnRefreshWatchDog の関数を呼び出すことにより、リフレッシュ動作を行います。一方、ソフトウェアタイマの場合は、CreateWatchDogTimer() を呼び出したアプリケーション自身が、リフレッシュ動作を行います（あるいは、監視対象のアプリケーションと、監視するアプリケーションの二つに役割を分割し、監視する役割のアプリケーションが CreateWatchDogTimer() を呼び出し、監視対象のアプリケーションが RefreshWatchDogTimer() を呼び出す、というやり方も考えられます）。</p>
<p>■ウォッチドッグタイマ機能の実装を見てみる<br />
上で述べた、WinCE/WEC のウォッチドッグタイマ機能について、どのように実装されているのか、カーネルのソースコードを見てみるのも、面白いでしょう。カーネルが提供するウォッチドッグタイマ機能のソースコードは、<br />
　%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/NK/KERNEL/watchdog.c<br />
にあります。wachdog.c で実装されている WatchDogTimerThrd() が、ウォッチドッグタイマのカーネルスレッドが実行する手続きです。</p>
<p>ウォッチドッグタイマのカーネルスレッドは、ハードウェアタイマが存在する場合か、または、CreateWatchDogTimer() が呼び出された場合に生成されます<b>（*1）</b>。ハードウェアタイマが存在する場合、つまり、OEMInit() の中で、OAL によって OEMGLOBAL 構造体の dwWatchDogPeriod と pfnRefreshWatchDog が設定された場合は、カーネルのスケジューラが初期化時に呼び出す InitWatchDog() の中で、ウォッチドッグタイマのカーネルスレッドを生成・始動します。ハードウェアタイマが存在しない場合には、CreateWatchDogTimer() が最初に呼び出された時に、ウォッチドッグタイマのカーネルスレッドが生成・始動されます<b>（*1）</b>。</p>
<blockquote><p>
（2012-04-19 追記）<br />
*1: <b>これは、WinCE 6.0 の場合です。WEC 7 のカーネルでは、ハードウェアタイマが存在するか否かに関わらず、<br />
InitWatchDog() において、ウォッチドッグタイマのカーネルスレッドが生成されます。</b>
</p></blockquote>
<p>ウォッチドッグタイマのカーネルスレッドは、ハードウェアタイマとソフトウェアタイマの両方を管理します。WatchDogTimerThrd() の実装を見ると分かりますが、システムが終了するまで実行を続けるループの中で、ハードウェアタイマと全てのソフトウェアタイマのタイマ満了時刻に従って待ち動作を行い、適切なタイミングで、各タイマをリフレッシュします。ハードウェアタイマもソフトウェアタイマも存在しない状態では、待ち時間を無限に設定して待ち動作を行います。この待ち動作は、CreateWatchDogTimer() や StartWatchDogTimer(), StopWatchDogTimer() を呼び出した際に解除され、ウォッチドッグタイマのカーネルスレッドが、待ち時間を再調整します。</p>
<p>ハードウェアタイマについては、たとえば、WinCE 6.0 のデバイスエミュレータであれば、以下のソースコードを見て下さい：<br />
　%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/src\oal/oallib/watchdog.c<br />
　%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/src\oal/oallib/timer.c<br />
　%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/src\oal/oallib/init.c</p>
<p>上のソースファイルのうち、watchdog.c にある SMDKInitWatchDogTimer() と RefreshWatchdogTimer() が、ハードウェアタイマ用の初期設定と、ハードウェアタイマのリフレッシュ処理を行う関数です。SMDKInitWatchDogTimer() は、timer.c にある OALTimerInit() を経由して、init.c にある OEMInit() から呼び出されます。つまり、OEMInit() の中で、（SMDKInitWatchDogTimer() によって）OEMGLOBAL 構造体の dwWatchDogPeriod と pfnRefreshWatchDog が設定されます。RefreshWatchdogTimer() は、最初に呼び出された時は、プロセッサ（S3C2410）内蔵のウォッチドッグタイマを初期化し、それ以降の呼び出しでは、ウォッチドッグタイマをリフレッシュ（タイマのカウンタをリセット）します。</p>
<p>なお、SMDKInitWatchDogTimer() を見ると、dwWatchDogPeriod と pfnRefreshWatchDog という変数に対して代入を行っていますが、これらの名前は、OEMGLOBAL 型の大域変数のメンバを指すように #define されています。興味のある方は、<br />
　%_WINCEROOT%/PUBLIC/COMMON/OAK/INC/bcoemglobal.h<br />
をご覧になってみて下さい。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/03/19/wincewec-%e3%81%ae%e3%82%a6%e3%82%a9%e3%83%83%e3%83%81%e3%83%89%e3%83%83%e3%82%b0%e3%82%bf%e3%82%a4%e3%83%9e%e6%a9%9f%e8%83%bd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WinCE/WEC でシリアルコンソール</title>
		<link>http://www.stprec.co.jp/ceblog/2012/03/15/wincewec-%e3%81%a7%e3%82%b7%e3%83%aa%e3%82%a2%e3%83%ab%e3%82%b3%e3%83%b3%e3%82%bd%e3%83%bc%e3%83%ab/</link>
		<comments>http://www.stprec.co.jp/ceblog/2012/03/15/wincewec-%e3%81%a7%e3%82%b7%e3%83%aa%e3%82%a2%e3%83%ab%e3%82%b3%e3%83%b3%e3%82%bd%e3%83%bc%e3%83%ab/#comments</comments>
		<pubDate>Thu, 15 Mar 2012 00:15:03 +0000</pubDate>
		<dc:creator>koga</dc:creator>
				<category><![CDATA[OS の内部動作]]></category>
		<category><![CDATA[付属機能の使い方]]></category>

		<guid isPermaLink="false">http://www.stprec.co.jp/ceblog/?p=294</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>Windows Embedded CE/Compact には、PC 用の Windows と同様のコマンドプロセッサ（cmd.exe）が付属しているのは、皆さんご存じの通りです。今回は、このコマンドプロセッサをシリアルケーブル経由で PC から操作する手順について述べます。</p>
<p>さて、Platform Builder のカタログ項目ビューで依存項目を見ると、コマンドプロセッサには、コンソールウィンドウと Telnet サーバーが依存していることが分かります。コンソールウィンドウは、「コマンドプロンプト」のウィンドウですが、コンソールアプリケーションから printf() などで標準出力に出力した場合も、コンソールウィンドウが開いて出力内容が表示されます。PC 用の Windows でも、同様にコンソールウィンドウが開きますよね。つまり、コンソールウィンドウは、標準出力と結びつく（同様に、標準入力とも結びつく）コンソールデバイスとして機能するというわけです。Telnet サーバについては、後で述べます。</p>
<p>■コンソールの出力先に対するレジストリ設定<br />
コマンドプロセッサを、シリアルケーブル経由で PC から操作するには、コンソールの出力先の設定を変更します。この設定は、リファレンスの次のページで説明されています：</p>
<p>　Command Processor Registry Settings (Windows Embedded CE 6.0)<br />
　<a href="http://msdn.microsoft.com/en-US/library/ee506287(v=winembedded.60).aspx">http://msdn.microsoft.com/en-US/library/ee506287(v=winembedded.60).aspx</a></p>
<p>　Command Processor Registry Settings (Windows Embedded Compact 7)<br />
　<a href="http://msdn.microsoft.com/en-us/library/ee506287.aspx">http://msdn.microsoft.com/en-us/library/ee506287.aspx</a></p>
<p>上のページで説明されているように、[HKEY_LOCAL_MACHINE\Drivers\Console] キー直下の OutputTo の値により、コンソールの出力先を、コンソールウィンドウ以外に設定できるのです。たとえば、2番のシリアルポート（COM2）であれば、次のような設定になります。</p>
<pre><code>
[HKEY_LOCAL_MACHINE\Drivers\Console]
    "OutputTo"=dword:2
    "COMSpeed"=dword:1C200
</code></pre>
<p>上の例は、COM2 をコンソールの出力先とし、COM2 のボーレートを 115200 としています。</p>
<p>上記のように設定することで、シリアルケーブルで接続した PC から、ターミナルソフトを使ってコマンドプロセッサを操作できます。このように設定しておくと、コマンドプロセッサ（cmd.exe）を起動すると、コンソールウィンドウは開かず、代わりに、シリアルケーブルで接続した PC のターミナルソフトのウィンドウに、コマンドプロセッサのプロンプトが出力されます。そして、コマンドプロセッサのプロンプトに対して、コマンド名やプログラム名をタイプ入力すれば、Windows Embedded CE/Compact 上のコンソールウィンドウの場合と同様、コマンドやプログラムを実行できます。</p>
<p>なお、コンソール出力をデフォルト（0）以外に設定すると、「コマンドプロンプト」を起動しても、ウィンドウが開かず、コンソール出力に設定したデバイスにプロンプトが出力されます。この点は、注意して下さい。</p>
<p>■シリアルコンソールに関する注意点<br />
シリアルケーブル経由でコマンドプロセッサを使う設定については、たとえば、次の Blog エントリでも紹介されています：</p>
<p>　Command line (Console) over serial<br />
　<a href="http://ce4all.blogspot.com/2007/06/command-line-console-over-serial.html">http://ce4all.blogspot.com/2007/06/command-line-console-over-serial.html</a></p>
<p>このエントリには、いくつか読者コメントが付いており、最後のコメントにある「入力がエコーバックされない」という質問には、回答が付いていないままです。実は、エコーバックされないのが通常動作です。コマンドプロセッサ（cmd.exe）のソースを見ると分かりますが、cmd.exe 自身は、コンソールに対して入力をエコーバックしません。バッチファイル（.bat, .cmd）の中で、echo コマンドによるエコーバックの ON/OFF 切り替えは出来ますが、コンソールに対しては、echo コマンドの実行有無とは関係無く、エコーバック動作は行いません。</p>
<p>cmd.exe のソースコードは、WinCE/WEC のソースツリーの、以下の場所にあります：</p>
<p>　%_WINCEROOT%/private/winceos/UTILS/cmd2/</p>
<p>コンソールに対する入力処理は、cmd.cxx にある cmd_GetInput() で実装されていますが、バッチファイル（.bat, .cmd）の実行ではなく、コンソールからの入力、つまり標準入力に対する入力処理では、エコーバックは行いません。そもそも、コマンド入力を _fgetts() で取得していますので、タイプ入力の1文字ごとにエコーバックする仕組み自体がないのです。従って、シリアルコンソールで入力のエコーバックを実現するには、自前でエコーバック処理を実装する必要があります。</p>
<p>■タイプ入力に対するエコーバック動作<br />
コマンドプロセッサのプロンプトに対してエコーバックする処理は、コマンドプロセッサが標準入出力を行う先の、「コンソールデバイス」が担当する役割です。たとえば、コンソールウィンドウは、コマンドプロセッサがデフォルトで使用するコンソールデバイスであり、コンソールウィンドウにおけるタイプ入力のエコーバック処理は、コンソールウィンドウが行います。</p>
<p>もう一つの例は、Telnet サーバです。以下のページでも言及されていますが、Telnet サーバは、自身をコンソールデバイスとして登録したうえで、コマンドプロセッサ（cmd.exe）を起動します。</p>
<p>　<a href="http://msdn.microsoft.com/en-us/library/aa446909.aspx">http://msdn.microsoft.com/en-us/library/aa446909.aspx</a> (Implementing a Network Service on Windows CE)</p>
<p>そして、Telnet クライアントでのタイプ入力を受け取り、それを、コンソールデバイスのインタフェースを介してコマンドプロセッサへ渡し、コマンドの実行を依頼します。コマンドプロセッサがコマンドを実行し、標準出力へ出力を行うと、それが（入力とは逆の向きに）コンソールインタフェースを介して Telnet サーバに渡され、Telnet クライアントに送信される、というわけです。なお、Telnet の場合には、Telnet クライアントでのタイプ入力のエコーバック処理は、Telnet クライアント自身が行うことになるはずです。</p>
<p>コンソールウィンドウのソースコードは、開示されていないため、コンソールデバイスとしての振る舞いの詳細を見ることができません。一方、Telnet サーバの方は、<br />
　%_WINCEROOT%/public/servers/sdk/samples/telnetd/<br />
にソースコードが収録されていますので、興味のある方は、ご覧になってみて下さい。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() で、コンソール入出力の処理を行います。</p>
<p>Telnet サーバが cmd.exe を起動する処理は、telnetd.cpp にある TelnetLaunchCmd() ですが、ここでは、cmd.exe を起動する前に、SetStdioPathW() を呼び出して標準入出力を変更します。この時、コンソールデバイスとして、自分自身を実体に割り当てたデバイス（&#8221;TEL<インデックス>:&#8221; というデバイス名）を設定することにより、cmd.exe の標準入出力先として自身を設定します。Telnet サーバは、Windows の流儀でいう「サービス」、つまり、ユーザモードのデバイスドライバとして動作しますから、このような動作が可能なのです。</p>
<p>■エコーバック動作の実現方策<br />
では、シリアルコンソールの場合に、タイプ入力のエコーバック動作を実現するには、どうすればよいのでしょうか？</p>
<p>方策は、二つ考えられます。一つは、Telnet サーバのように、自前でコンソールデバイス機能を実装し、コマンドプロセッサとシリアルポートの間に介在することにより、シリアルポートからの入力を、一文字ごとにエコーバックする、という方策です。</p>
<p>もう一つは、シリアルポートに対して直接入出力を行い、シリアルポートから文字列が入力されたら、入力された文字列をコマンド名として、都度コマンドプロセッサ（cmd.exe）を起動する、という方策です。</p>
<p>二つの方策のうち、後者の方が、より簡単に実装できます。cmd.exe にコマンドを実行させるには、/Q と /C オプション付きで cmd.exe を実行すればよいのです。つまり、<br />
　&#8221;/Q /C <コマンド名>[ コマンド引数]&#8221;<br />
という文字列を、CreateProcess() の第二引数として渡し、第一引数に &#8220;cmd.exe&#8221; を渡して呼び出せば、cmd.exe がコマンドを実行し、その結果が、レジストリで設定したコマンドプロセッサのコンソール、つまりシリアルポートへ出力されます。その際、CreateProcess() が返したプロセスハンドルに対して WaitForSingleObject() を呼び出せば、コマンド実行の完了を待つことが出来ます。</p>
<p>ただし、二番目の方策については、次の点を注意して下さい：</p>
<p>・コマンド実行のたびに、都度 cmd.exe を起動するので、シリアルコンソールから cd コマンドが実行されても、その結果が保持されない。<br />
　（cd コマンドに対応するとしたら、移動先に指定されたディレクトリを、cmd.exe を起動するプログラム自身が記録することによって、履歴動作を実現する必要がある。）</p>
<p>・cmd.exe を実行している間は、シリアルポートを閉じて、cmd.exe がシリアルコンソールを使用できるようにしなければならない。</p>
<p>二番目の注意点ですが、これは、シリアルポートのデバイスドライバの制約によるものです。シリアルポートは、一つの実体に対し、デバイスを一つしかオープンできないのです。たとえば、COM1 であれば、&#8221;COM1:&#8221; を指定して CreateFile() を呼び出すことにより、シリアルポートのデバイスをオープンします。この時、&#8221;COM1:&#8221; のデバイスをオープンしたままの状態で、同じシリアルポート（&#8221;COM1:&#8221;）に対して CreateFile() を呼び出すと、エラーとなり、オープンできません。この制約は、シリアルポートのデバイスドライバの、MDD レイヤの実装によるものです。</p>
<p>シリアルポートのデバイスドライバの MDD レイヤのソースコードは、<br />
　%_WINCEROOT%/public/COMMON/oak/drivers/serial/com_mdd2/<br />
に収録されています。このディレクトリにある mdd.c で実装されている COM_Open() を見ると、既にオープン済みのシリアルポートに対して呼び出された場合は、ERROR_INVALID_ACCESS をエラーコードとしてセットして、NULL を返すことが分かります。</p>
<p>ところで、シリアルコンソールにバックスペース（&#8217;\b&#8217;）が入力された場合、エコーバック処理では、入力を一文字消さなければいけません。単純に &#8216;\b&#8217; をエコーバックしただけでは、PC のターミナルソフトでは、カーソルが一文字戻るだけで、文字が消えないのではないかと思います。カーソルを戻すだけでなく、直前にあった文字を消したい場合には、&#8217;\b&#8217; を単純にエコーバックする代わりに、&#8221;\b \b&#8221; という文字列を出力すればよいでしょう。つまり、カーソルを一文字分戻した後、空白文字を出力することにより、直前にあった文字を消し、再度 &#8216;\b&#8217; を出力してカーソルを戻す、というわけです。</p>
<p>■シリアルコンソールを利用した管理機能の実現<br />
上で述べた、シリアルコンソールのタイプ入力に対するエコーバック動作の二つの実現方策のうち、後者の方は、シリアルコンソールを使った管理機能を実装する際には、却って向いているかも知れません。前者の方策の場合、OS (WinCE/WEC) の起動完了直後にコマンドプロセッサを起動するように設定すれば、シリアルコンソールにコマンドプロセッサのプロンプトが出力されて、Linux などと同じ感覚で使うことが可能です。しかし、そのようにしてしまうと、全てのプログラムをコマンドプロセッサから実行できてしまいます。</p>
<p>シェルをカスタマイズして、特定の操作しかできないようにしたデバイスや、あるいは、ヘッドレスのデバイスの場合には、限られたコマンドだけをシリアルコンソールから実行できるように制限したり、また、ログイン動作を実装してセキュリティを確保する必要があるでしょう。そのような場合は、単純にコマンドプロセッサをシリアルコンソールへ割り当てるのではなく、コマンドプロセッサを呼び出す「ラッパー」／「ドライバ」プログラムを割り当てて、そのプログラムが、ログイン動作や、シリアルコンソールから実行可能なプログラムの名前だけを受け付けて実行する、という仕組みにするのが良いと思います。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stprec.co.jp/ceblog/2012/03/15/wincewec-%e3%81%a7%e3%82%b7%e3%83%aa%e3%82%a2%e3%83%ab%e3%82%b3%e3%83%b3%e3%82%bd%e3%83%bc%e3%83%ab/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

