前回はRCファイルの記述を行ったが、実はあれだけだとまだプログラムではロードできない。たとえば"Transfer"→"Start"を選択するとIDM_XFER_STARTというMessageが飛ぶことになるわけだが、まだIDM_XFER_STARTという値そのものを定義していないから、このままビルドするとUndefined Symbolということでエラーになる。
この値の定義を行っているのは、resource.hというinclude fileである。Visual Studio.NET 2010 Professionalだとこのinclude fileも勝手に作ってくれるが、Expressではこれもユーザーが作らなければならない。
といっても、こちらはほかと重ならない様に適当に値を割り振るだけである。List1に、実際に今回使ったresource.hの関係部分を示すが、IDC_MYICON~IDR_MAINFRAMEまでの値は、一番最初のWizardを掛けた時に自動的に生成される分、続くID_SETTINGS_PORT~IDM_PORT_COM7までが、後から(Resource Editorを使って)生成した分である。この結果、たとえば最初に設定したけど後で「要らない」と削除した項目(32778が振られているID_PORT_COM)も残っていたりするのはご愛嬌である。手作業で入力の場合は、こういう無駄なIDは削除してかまわない。自分でカスタマイズする場合に備えて説明すれば、IDの数字は16bitで、前半(0~32768)+2つ(32769/32770)はリザーブとされているので、32771から順に振ってゆけばよい(ここは自分の好みでカスタマイズできるので、たとえば2番飛ばしとかでも可能)。
さて、今回は全ソースを用意した(80LED.zip)。流石にソースを全部掲載すると長すぎる(80LED.cppは545行)ので、こちらをダウンロードして、適当なエディタなどで開きながら記事を読んでいただければと思う。同時に80LED.rcやresource.h、(全く変更はしていないが)80LED.hも添付したので、前回の手順で作成したプロジェクトのファイルと置き換えてビルドすれば、プログラムも完成する筈である。またZIPファイルの中には80LED.exeと、ランタイムのmsvcr100.dllも添付している。80LED.exeは、Arduinoを接続しなくても一応単体で動作して、CPUの負荷率をリアルタイム表示してくれるから、理解の参考になればということで添付した。msvcr100.dllはVisual Studio.NET 2010をインストールしている環境では不要だが、もしまだインストールしていない場合は、80LED.exeと同じディレクトリにおいてほしい。
さて、では実際のプログラムである。includeに関しては、379回で説明したとおりいくつか必要なものを追加する事になる。#defineに関しても、いくつか追加しているのが判る筈だ。全く新しいのはTIMER_IDで、これは前回説明したとおり定期的にCPU負荷を取得するための割り込みを掛けるために使うタイマー用のもの。FONTHEIGHTとかWINDOWWIDTH/WINDOWHEIGHTは画面サイズやフォントの大きさの指定を行っているものだ。
続く_tWinMain()とMyRegisterClass()は一切無変更であるが、InitInstance()だけはちょっと手を入れている。このInitInstance()の中でCreateWindow()を呼び出して、80LEDのウィンドウの実体を生成しているが、ここのオプションをいじって、最大化ボタンを無効にし、かつウィンドウのリサイズを不可能にしている。また、先に#defineで定義した画面の縦と横の幅を指定している。画面一杯にこれを表示しても仕方ないし、ということでの対処である。
次のSetTitle()とOpenRS232C()は、今回新規に作成した関数である。前者は? というと、ウィンドウのタイトルバーに現在の設定を表示するというものである。プログラムを立ち上げてもらうと判るが、タイトルバーに通信ポートと通信速度、それとCPU負荷取得のインターバルが表示されるようになっている。これは設定を変更するたびに呼び出される関数で、やることは簡単(wsprintf()を呼んで、プログラムタイトル/通信ポート/通信速度/更新インターバルを順に並べた文字列をまず作り、ついでそれをSetWindwoText()で反映するだけ)なのだが、これをいちいち全部のcallbackに記述するのも馬鹿なのでまとめたものだ。OpenRS232C()の方は、というとこれはもう名前のままで、COMポートをオープンし、かつパラメータを設定するだけである。なんでこれを別関数としたかというと、たとえば一時的に転送を中止し、再度復活させるなんてケースでは、一度COMポートをCloseする仕組みにしているので、
- プログラムの起動時にCOMポートをOpen
- プログラム実行中に一度COMポートをCloseし、再度Open
の2つのケースが考えられる。だからといって、二箇所で同じコードを書くのも馬鹿なので、ひとつにまとめたという次第だ。COMポートのCloseは、CloseHandle()一発で済むし、エラー処理の必要もない(エラー処理をしようにも、せいぜいがプログラムを終了させる以外の案がない)から、こちらは特にまとめる必要もないと判断した。
(続く)
List 1:
#define IDC_MYICON 2
#define IDD_MY80LED_DIALOG 102
#define IDS_APP_TITLE 103
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_MY80LED 107
#define IDI_SMALL 108
#define IDC_MY80LED 109
#define IDR_MAINFRAME 128
#define ID_SETTINGS_PORT 32771
#define ID_PORT_COM1 32772
#define ID_PORT_COM2 32773
#define ID_PORT_COM3 32774
#define ID_PORT_COM4 32775
#define ID_PORT_COM5 32776
#define ID_PORT_COM6 32777
#define ID_PORT_COM 32778
#define ID_PORT_COM8 32779
#define IDM_PORT_COM1 32780
#define IDM_PORT_COM2 32781
#define IDM_PORT_COM3 32782
#define IDM_PORT_COM4 32783
#define IDM_PORT_COM5 32784
#define IDM_PORT_COM6 32785
#define IDM_PORT_COM 32786
#define IDM_PORT_COM8 32787
#define ID_SETTINGS_SPEED 32788
#define IDM_SPEED_300 32789
#define ID_SPEED_600BPS 32790
#define ID_SPEED_1200BPS 32791
#define ID_SPEED_2400BPS 32792
#define ID_SPEED_4800BPS 32793
#define ID_SPEED_9600BPS 32794
#define ID_SPEED_19200BPS 32795
#define ID_SPEED_38400BPS 32796
#define IDM_SPEED_600 32797
#define IDM_SPEED_1200 32798
#define IDM_SPEED_2400 32799
#define IDM_SPEED_4800 32800
#define IDM_SPEED_9600 32801
#define IDM_SPEED_19200 32802
#define IDM_SPEED_38400 32803
#define ID_SETTINGS_REFRESH 32804
#define ID_REFRESH_0 32805
#define ID_REFRESH_1 32806
#define ID_REFRESH_2 32807
#define ID_REFRESH_1SEC 32808
#define IDM_Refresh_100 32809
#define IDM_Refresh_200 32810
#define IDM_Refresh_500 32811
#define IDM_Refresh_1000 32812
#define ID_TRANSFER_START 32813
#define ID_TRANSFER_STOP 32814
#define IDM_XFER_START 32815
#define IDM_XFER_STOP 32816
#define IDM_PORT_COM7 32817
#define IDC_STATIC -1