C/C++でBrain用アプリを開発する

ここでは、C/C++ 言語を用いて Brain (Windows CE) 用アプリケーションを開発するうえでのノウハウを掲載しています。

### 学習

まず、**C か C++ を学習してコーディングに慣れましょう**。Brain の OS は Windows CE なので次は Windows CE 開発の情報を読んで進みたいところですが、CE 開発について取り扱うサイトは**かなり少ない**です。そのため、一般的な Windows PC 向けの **Win32 API プログラミング**を学んで CE 開発に応用することをお勧めします。以下に、参考にできるサイトを掲載します。

* Windows プログラミング入門 - Web/DB プログラミング徹底解説
* Win32 API入門

Win32 API は原始的な構造を引きずっているため開発が難しいです。焦らず、ゆっくり時間をかけて理解してください。サンプルをコピペして大まかに理解したら、サンプルを書き換えたり自力で行を追加したりしながら理解を進めるのが良いでしょう。**Win32 API入門** は内容が多いぶん詳細に解説されているので、全て読まずに必要になったときに参照すると便利です。

実際に **Brain 用アプリを作成**するには、以下のサイトがよくまとまっていて参考になります。

* WindowsCE+APIプログラミング SDK

Pocket PC 向けの解説となっていますが、基本的には同じです。先述の一般的な Windows PC 向けプログラミングと共通点が多いですが、**Brain で動作する新鮮さ**は何とも言えないものがあるはずです。

「もっといろんな Windows CE の機能を知って高度なアプリを作りたい」とか、「Windows PC で使える○○関数が Windows CE で使えるかを確認したい」と思ったら、公式ドキュメントや Windows CE Developers FAQ を参照してください。

知見
--

### メニューバーが出ない

「Win32 API におけるメニューバーの出し方は色んなサイトに載っているが、どれを試しても PC では表示されても Brain で表示されない」というよくある問題があります。これは、**Windows CE におけるメニューバーの扱いが PC 版と異なる**ためです。

Windows CE は低解像度なモバイル端末用の OS です。タイトルバーやメニューバーなどの要素を PC と同じように並べると画面が狭くなってしまうため、Windows CE においてはこれらを1行にまとめる**コマンドバー**を使用します。具体的には、

1. `InitCommonControls()` または `InitCommonControlsEx()` でコモンコントロールを初期化する
2. `CommandBar_Create()` でコマンドバーを作成する
3. `CommandBar_InsertMenubar()` または `CommandBar_InsertMenubarEx()` でメニューバーをコマンドバーに挿入する
4. `CommandBar_Show()` でコマンドバーを表示する

という流れで表示します。

コマンドバーを表示すると、描画領域の上部分が**コマンドバーで隠れる**問題が起きます。Windows PC であれば、メニューバーの下から描画領域が始まるためこのような問題は発生しません。解決法としては面倒ではありますが、`CommandBar_Height()` によってコマンドバーの高さを取得し、その分だけ手動で描画領域をずらすという方法を取ります。

以上のコマンドバーの詳細な仕様に関しては
Command Bars Reference に掲載されているので、実際にプログラムを書く際はご確認ください。

### フォントダイアログでフォントがないと言われる

CHOOSEFONTW の Flags メンバで **`CF_BOTH`** の指定が必要です。そして、そのためには hDC メンバに GetDC で取得できる適当なウィンドウ (メインウィンドウで良い) のデバイスコンテキストを入れておき、ChooseFontW の呼び出し後に ReleaseDC で解放する、という手順が必要です。面倒ですが、これで Brain にある全てのフォントが表示されるようになります。

### 自作ダイアログの表示がおかしい

リソーススクリプトで、**DIALOGEX ではなく DIALOG** を使います。文法は同じなので、単にここを書き換えるだけで済むはずです。なお、そもそもコンパイルに失敗する場合は、こちらにあるような旧式の記法に書き換えます。

### 自作ダイアログで Windows CE 特有の OK ボタンを使いたい

リソーススクリプトで、DIALOG の EXSTYLE として **`WS_EX_CAPTIONOKBTN`** を指定します。ヘッダがこの定数を定義していない環境では、直接 `0x80000000L` と数値で指定します。ユーザがこのボタンを押すとダイアログのウィンドウプロシージャに **`IDOK`** の `WM_COMMAND` が送信されるので、これを適切に処理します。

### 高解像度モードで最大化すると、ボタンなどの右の表示がおかしくなる

表示位置のずれが一定幅の場合に、本来再描画しなければならない場所が OS にマークされず、再描画が実行されずにおかしな表示になるようです。ウィンドウを動かすなどして強制的に再描画しなければならないような状態にすることで直りますが、プログラム的に対策することは不可能だと思われます。唯一、コマンドバー上においては、`WM_SIZE` で **`MoveWindow(コマンドバーのハンドル, 0, 0, 0, 0, TRUE);`** を行うと対策可能です。

### Brain のキーボードに刻印されている数字に従って、数字として入力を受け取りたい

これは、残念ながら力技しかありません。例えば「**q の入力をプログラム内で受け取ったら 1 を変数に代入する**」といったことです。エディットコントロールの場合は、サブクラス化で用意したウィンドウプロシージャ内で「`L'q'` の `WM_CHAR` を受け取ったら `SendMessageW(hWnd, EM_REPLACESEL, 0, (WPARAM)L"1");` を実行する」という形で実装できます。

### 第2世代以降の辞書アプリで使われている滑らかなフォントを使いたい

ゴシック体は **DFGHSGothic-W5-SH**、明朝体は **DFGHSMincho-W5-SH** としてインストールされています。CreateFontIndirectW 等でこのフォント名を指定すれば使用できますが、Brain ではシステムレベルでのアンチエイリアスが無効化されているため、そのままではぐちゃぐちゃの見づらい表示になります。ダイアログなどではオーナードローにしないと回避不能ですが、それ以外の場面では、CreateFontIndirectW に渡す lfQuality に **`ANTIALIASED_QUALITY`** (値は 4) を指定すると、辞書アプリと同じ綺麗な表示になります。

なお、Windows Embedded CE 6.0 は Windows PC で用いられている「ClearType」という高度なアンチエイリアスにも対応し、`CLEARTYPE_QUALITY` (値は 5) とすることで使用できますが、Brain の画面が横倒しに取り付けられているため綺麗な表示にはなりません。

### ウィンドウサイズを限定したい

Windows PC であれば `WM_GETMINMAXINFO` を処理してやればいいですが、Windows CE はこのメッセージを送信しないため使えません。代わりに、「**`WM_SIZE` を受け取った際に、制限を満たさないサイズなら SetWindowPos 等で再度サイズを変える**」という対応を行います。Windows PC を対象とする記事では「この方法を使うと `WM_SIZE` が繰り返し送信されてしまうためその対応が必要」とありますが、Windows CE では多重送信されないようなので、単に強制的にサイズを変えるのみで大丈夫です。

### 変数型が変

Win32 API プログラミングでは、一般に Win32 API ヘッダが提供する変数型を利用します。各型の詳細は Windows Data Types を参照してください。`P` から始まる型と `LP` から始まる型は 16-bit Windows 時代の名残であり、CE を含む 32-bit Windows では**同一なので気にしなくても構いません**。`LP` の L は long で、通常の 16-bit のポインタに対して 32-bit の長いポインタという意味でしたが、32-bit Windows ではどちらも 32-bit となり、以後差異が無くなりました。

なお、Windows CE では **`TCHAR` 系は必ず `WCHAR` 系になります**。これは、Windows CE に ANSI 版 API が実装されていないため、`UNICODE`、`_UNICODE` マクロが標準で定義されていることによります。現在は UTF-8 対応の観点で再興の兆しを見せている ANSI 版 API ですが、当時は互換性のためだけに残されていたものだったため、容量や性能が貧弱な CE デバイスでは取り除かれていました。例えば、CreateWindowW は存在する一方 CreateWindowA は存在しません。

### あるはずの API 関数が使えない

先述の公式ドキュメントには記載されているのに関数が存在せず呼べない場合、まずは**スペルミス**を確認してください。その次に、公式ドキュメントに記載のある**必要なヘッダファイルとライブラリファイル**を追加していることを確認してください。ライブラリファイルの追加方法は開発環境によりプロジェクト設定のリンカ設定から行う、コマンドラインオプションに追加するなど様々です。それでも解消せず、かつ Brain にその API が実装されているのであれば、**それが含まれる DLL からライブラリファイルを作成して追加するか、`LoadLibrary()` で動的に読み出す**ことで使用できます。

### 正しいはずの C/C++ プログラムがコンパイルできない

まずは、新しい C/C++ 規格で書かれていることが考えられます。C/C++ 規格は数年おきに更新されますが、Windows CE の開発環境はもはや更新されておらず、C89 や C++98 等の古い C/C++ 規格にしか対応しないことが多いです。古い入門サイトを参考にするなどして対応してください。CeGCC は GCC 9.3.0 と比較的新しく、`-std=gnu18` や `-std=c18 `を指定することによって C18、`std=gnu++17` や `std=c++17` を指定することによって C++17 に準拠しますが、**C23** (`std=gnu2x`/`std=c2x`) **や C++20** (`-std=gnu++2a`/`-std=c++2a`) **への準拠はドラフト段階のものであり不完全**です。``

``他には、windows.h を読み込んだ後に C++ STL のヘッダを読み込んでいることが考えられます。windows.h は、`NOMINMAX` マクロが定義されていない場合に `min` や `max` といったマクロを定義することがあります。しかし、詳しくは説明しませんが、マクロ機能はあまりに強力な機能のため、C++ STL の中の min や max を置き換えて破壊してしまいます。これを防ぐには、**windows.h を読み込む前に `NOMINMAX` マクロを定義する**か、**C++ STL の読み込みを windows.h より先に書きます**。``

``どれにも合致しない場合は、エラーメッセージを上から丁寧に読み、よく分からない場合はコピペして検索しましょう。``

``C/C++ の躓きやすいポイント
----------------````Win32 API 関連のサンプルコードをあたっていると、意味の分からないコードに出会うことが多いです。これは、Win32 API 呼び出しによる実装が通常のアプリケーション開発と比べるとかなり原始的で、C/C++ の入門レベルではあまり扱われないような高度な機能を駆使していることが多いためです。初心者が躓きがちな点を紹介します。````### if/while/for 文の括弧の中身が条件式のように見えない````if/while/for の条件式の部分には**任意の式**が入ります。式には値・演算子・変数・関数とその組み合わせが含まれ、式の評価結果が**ゼロ**か**非ゼロ**かで分岐します。**ゼロであれば偽 (false)、非ゼロであれば真 (true)** です。式の左にたまにあるビックリマークは**右の真偽を反転させる演算子**です。ですから、関数呼び出しが条件式として書いてある場合、その返り値が条件分岐に使われていることがわかります。

比較演算子である `==` や `<` 等は**計算結果を持つ演算子**で、C では整数値、C++ では真偽値です。cout や printf 等で一度試してみると面白いですよ。

ちなみに、メッセージループは無限ループですが、while 文の「条件式」部分に入れている `GetMessage()` は「メッセージが来たときだけ値を返し、それ以外は待つ」というものなので、ビジーループにはなりません。````### if/switch 文の括弧の中に「;」が入っている`````if(初期化式; 条件式)`
「初期化式」では変数に値を代入するなど、「条件式」の前に行う処理を指定します。「初期化式」が実行された後、「条件式」の判定が行われます。
`switch(初期化式; 条件値)`
上と同様、「初期化式」を実行した後に `switch` ブロック内で条件と「条件値」の比較が行われます。````この文法は C++17 という比較的新しい規格で導入されましたが、これに現在対応しているのは CeGCC のみとなっています。それ以外の開発環境を使っているのであれば該当部分を書き換えましょう。````### 計算式に「&」「|」「^」「~」「<<」「>>」が出てくるのは何？````これらは**ビット演算子**という演算子の一種です。「コンピュータは2進数で動いている」という話は聞いたことがあるかと思いますが、ビット演算ではその2進数(ビット単位)の演算を行います。C/C++ に代表されるシステムプログラミング言語では頻繁に用いる計算ですが、初心者が使うことはあまりありません。`CreateWindow()` のスタイル指定等で bitwise OR を表す「|」が現れますが、「そんなものがあるんだ」程度で問題ありません。````### 条件式に「&」や「=」が出てくるのはなぜ？````まず、**「&」**は一つ上で説明したビット演算子です。具体的には bitwise AND であり、「左右の式を見て共に1であるビットのみ1にした値を返す」計算をします。これが条件式に登場するのは、例えば**「ある変数 `a` を2進数で見たとき、下から5番目の桁が1になっているか確認したい」**という場面で**「a & 0b10000 の計算結果が0なら違い、0以外なら真である」**という形で確認できるからです。なお、右の数値は一般的にマクロか、10進数で `16` のように記述されます。おそらく、あなたが遭遇したのもこのようなケースでしょう。

次に、**「=」** は代入の記号ですが、こいつは**代入した結果を返す**機能も持っています。これを駆使すると、**「変数にバックアップを取りながら、その値で分岐する」**というのを1行で書けます。````### 計算結果、変数、関数の先頭に括弧書きで変数型が書いてあるのは何？````これは**明示的キャスト (explicit cast)** と呼ばれるもので、**元々その型ではないものをその型の値に変換する操作**です。例えば、`(int)1.6` とあれば、double 型の 1.6 が int 型の 1 に変換されます。キャストは、例えば int 型整数に double 型の値を代入するなどの場面では暗黙的に(=勝手に)起こります。こちらは**暗黙のキャスト (implicit cast)** と言います。プログラマーが意図してなさそうな暗黙的キャストは、たまに警告やエラーになるものがあります。その場合に、**間違っていないことをコンパイラに伝える**ときにも明示的キャストを使います。なお、C ではポインタの暗黙的キャストが許可されますが、C++ では明示的キャストが必要です。````### 計算式じゃないところに & が出てくるのは何？````& の右に続く変数の**番地** (address) を取得する演算子です。「番地」や「アドレス」と聞くと小難しい印象を受けるかもしれませんが、要するに**「メモリ上でその変数が置かれた場所」**のことです。そして、この番地を格納した変数をポインタ変数と呼びます。番地を取得する用途は様々あります。

例えば C/C++ では配列変数を丸ごと関数に渡せませんが、番地は1つの値なので渡せます。関数内ではその番地から相対的に見る位置をずらすことで配列全体を読み書きできます。

また、変数を関数に渡したときには普通その値のコピーが渡されますが、コピーではなく**呼び出し元が渡した変数そのもの**に結果を入れてほしいことがあります。ここで、関数に変数のコピーではなく番地を渡せば、受け取った側はその番地に結果を入れることで呼び出し元に結果を返せます。

さらに、構造体は丸ごと普通に渡すこともできますが、一般にコピー処理が重くなるため避けられます。このように**無駄なコピー処理を避ける**ためにも有用です。

おまけ: ウィンドウクラスの登録で、ウィンドウプロシージャの関数名を書く部分があります。実行時には変数のようなデータのほかに CPU の命令もメモリに格納されるため、**各関数がメモリ上のどこにあるか**も変数と同様に定義できます。C/C++ においては、**関数名の後にカッコを書かない**と、それは**関数の場所(番地)**を表す式になります。よって、該当箇所では**ウィンドウプロシージャ関数はここにあるよ**という情報を WNDCLASS 構造体に記録していて、その構造体の場所を `RegisterClass()` に与えることで、`RegisterClass()` は**何をウィンドウプロシージャとして実行すればよいか**を知ることができる、ということです。この関数が失敗したときのエラー処理の書き方は、上記で説明した条件文の話につながりますね。````脚注
--````1. ↑ 例えば int 型は(本来) CPU のレジスタサイズの整数型であり、CPU アーキテクチャによってサイズが変わります。これによる移植性などの問題を避けるために導入されたものだと思われます。
2. ↑ C++ では真偽値ですが、整数などの真偽値でない値を与えた場合はゼロか非ゼロかで真偽値に変換するため、結果的には C と同じ挙動になります。
3. ↑ 左右はどちらでも構いませんが、大抵この順番で書きます。
4. ↑ 古い C/C++ では逆にこの記法しかできません。
5. ↑ このような変数の渡し方を**ポインタ渡し**と言います。C++ では、似た機能の「参照渡し」も利用できます。``