今回も引き続き、80LED.cppの中身の話である。実行ファイルを含むソースファイル一覧は前回の記事の中にリンクを用意している。ちなみに一箇所重大なバグを発見、これを修正したバージョンと入れ替えたので(詳細は次回説明する)、すでに入手された方にはお手数だがダウンロードしなおしていただきたい。
さて、一番手が入っているのがCallback routineのWinProc()である。Visual Studio.NET 2010が自動生成したWinProc()はList 1の様なシンプルなもので、実際ハンドリングしているMessageもWM_COMMAND/WM_PAINT/WM_DESTROYの3種類しかない。今回はここにメインプログラムのほぼすべてをまとめてしまった形だ。
さて、まず変数であるがPathList~transferchar]までが新規追加した部分である。これは[379回で説明したDisplay_Consoleでも同じ様に記述しているので、こちらを参照してほしい。これに続き、実際にMessageのCallback routineを追加してゆく形だ。
まず最初に追加したのがWM_CREATEに対するものだ。これはWindowが生成されるタイミングで発生するMessageである。たとえば普段はタスクトレイに常駐し、何かあるたびにメインのWindowを生成する、なんていうプログラムではこのMessageが複数回呼ばれる可能性があるのだが、今回のプログラムは一度Windowを生成すると、プログラム終了までそのWindowが消えない(つまりこのMessageが来るのは最初の1回だけ)ということで、初期化ロジックを全部ここに突っ込んでいる。逆に将来、たとえばタスクトレイに格納してWindowを破棄するなんてロジックに変更する場合、ここの処理はたとえば_tWinMain()の中で、Initinstance()やLoadAccelerators()を呼び出した後の、GetMessage()のメッセージループに制御が移る前あたりに移動したほうがいいかもしれない(もしくは、内部でカウンタを持っておき、最初の1回のWM_CREATEのみ初期化作業を行うなんてアイディアもあるだろう)。
さて、WM_CREATEであるが、まず最初に初期値100(=100ms)でSetTimer()を呼び出している。これが380回の最後でちょっと触れた、「一定期間ごとにMessageを発する」Timerである。一度これを仕掛けると、あとは一定期間ごとにWM_TIMERのMessageが到来するので、これにあわせてデータの取得や画面表示、データ転送を行うというわけだ。次に呼び出しているCreateFont()は、画面表示用フォントの設定である。ここは筆者の好みでMSゴシックを使う指定で決め打ちにしているが、気に入らなければ好みの設定にしてもらえればと思う。
続くPdhOpenQuery~SetTitle()の手前あたりまでは、Display_Consoleの初期化部分と同一である。その後にprocLoad[]を初期化したり、いくつかのパラメータを設定したり(なぜかtransferFlagを2回設定しているが、これは最初の"transferFlag = 1;"を消し忘れて残っているだけである)した後OpenRS232C()を呼び出して、Arduino側との通信ポートを初期化している部分がやや追加されているだけである。以上で初期化は終了なので、そのままbreakして終わりである。
さて、初期化が終わると後は基本的にWM_TIMERのMessageが到着し、これにあわせてCPU負荷の取得と表示・通信を行うのがメインの処理となる。まず最初にwParamという変数をチェックしているが、これはWinProc()に渡される引数の一つ。WM_TIMERは「全ての」TimerでMessageが飛んでくる可能性があるので、自分のTimer以外のMessageは無視する必要がある。そこで自分のTimerかどうかの判別に、WM_CREATEでSetTime()を呼んだときの第二引数を使っている。WM_TIMER Messageでは、wParamにそのTimerのIDが渡されるので、これが自分の設定したものと同じならば処理を行うが、そうでなければ何もせずに終了というわけだ。肝心の処理のほうだが、こちらはDisplay_Console()とやっていることはほとんど変わらない。PdhGetFormattedCounterValue()で全CPUの負荷をまず取得し、ついで画面表示用とArduino通信用にそれぞれ値を丸めた後で、必要ならWriteFile()を呼び出して値を送る形になる。問題は画面表示の方で、unicodeBuf[]に文字列を格納するが、これを描画する作業は別のMessage処理の中になる。文字列描画そのものはDrawText()を呼び出せばいいのだが、これが正しく画面に反映されるとは限らないため、強制的に再描画をかけてやる必要がある。そこで、InvalidateRect()を呼び出し、Windowの再描画イベントを発生させてこれを実施しているわけだ。この部分は次回ご紹介する。
(続く)
List 1:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 選択されたメニューの解析:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 描画コードをここに追加してください...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}