さて、今回からはもう少しまともにプログラムを作ることにしたい。といってもこちらは凝り始めるときりが無いし、別にWin32環境でのプログラムの作り方を説明するのが主眼ではないので、ほどほどのところまで作りこんだものをご紹介する程度に留めておきたいと思う。

具体的には、こんな感じで立ち上がるものとした(Photo01)。操作はメニューのみにまとめており、File(Photo02)/ Transfer(Photo03)/ Settings(Photo04~07)/ Help(Photo08,09)を実装している。

Photo01: 画面表示をどうするかはちょっと悩んだのだが、出てないと寂しい(謎)ので入れることに。このあたりは好みの問題。

Photo02: 今はExitのみ。

Photo03: 転送の開始/終了を指定するだけ。ちなみに立ち上がるとデフォルトでStartが掛かる様に現在は設計している。

Photo04: Settingsは2階層メニューとした。

Photo05: ポートの指定。もっとも仮想シリアルドライバを入れるとこれでは足りない、という話は当然ある。

Photo06: 速度指定。Arduino Uno以降だともう少し上のスピードでも取り漏らしはないようだが、Arduino Duemilanoveだとこのあたりで留めておくのが無難、というのは筆者の経験による判断。もっとも1パケット5Bytesだから、仮に10ms(毎秒100回)更新しても500Bytes/sec=4000bpsで、9600bpsでもおつりがくる。

Photo07: そのリフレッシュレート。0.1secでもかなりうるさい感じで、これ以上レートをあげる必要はないと判断した。

Photo08: HelpはAboutのみ。

さて、これの作り方であるが、まずはVisual Studio.NET 2010 Expressを立ち上げて新規プロジェクトを生成する(Photo10)。ここで、これまでとは異なり"Win32 プロジェクト"を選択する。するとウィザードが立ち上がるわけだが(Photo11)、最初は「次へ」を選択してみよう。ここで多少オプションが選べるようになっている(Photo12)。

Photo09: About画面。このダイアログはVisual Studo.NET 2010 experssが自動生成。

Photo10: このあたりの手順は毎回同じ。

Photo11: ウィザード。慣れると次の画面は要らないので、ここで「完了」を選べば楽、という事だろう。

Photo12: 追加指定。普通はあまり無いが、DLLを作る必要があるなんて場合にはここで指定することになる。

ウィザードが完了すると、自動的にスケルトンのコードが生成され(Photo13)、これをビルドして(Photo14)実行すると、とりあえず立ち上がる(Photo15)。この時点で「ファイル」と「ヘルプ」は出来ており、終了とダイアログ表示までは自動的に完成している。あとはこれに肉付けをしてゆくわけだ。

Photo13: スケルトンといっても、これだけで190行もある。もっともこの大半は、そのままにしておけばいいのだが。

Photo14: ビルドは当然ながら、何事も無く終了する。

Photo15: 無駄に大きなサイズで立ち上がるが、これは何も初期設定していないから。

さて、肉付けの第一歩は(このあたりは人によっていろいろあるだろうが筆者の場合は)まずメニューの作成である。まずメニューの項目名を決め、ついでその項目名(オブジェクト名)に対するcallbackを記述してゆく、という方法が合理的であると思う。というわけで、今は「ファイル」と「ヘルプ」しかないメニューをPhoto01~09の様な構造に変えてゆくことにする。

実はここでVisual Studio.NET 2010 Expressの制限がモロに出てくる。Visual Studio.NT 2010 Professionalの場合、リソースエディタと呼ばれるツールがあり、これを使うことで画面から対話式にメニューを簡単に作ることが出来る(Photo16)。このリソースエディタで生成されたコードは.RCという拡張子を持つリソースファイルに収められ、これをVisual Studio.NET 2010が読み込んで実際のWindowsのプログラムに反映する形だ。

Photo16: たとえばメニューの場合、IDC_MY80LEDというMenu Objectが生成されるので、ここにSubmenuをどんどん対話式に追加してゆき、後で各項目のプロパティを必要なものに変更する、という形。

ところがVisual Studio.NET 2010 Expressにはリソースエディタが付属していない。このため利用者は、手でリソースファイルを書き換える必要がある。といっても、実はそれほど難しくない。List 1に書き換え前(つまり最初のスケルトンの状態)、List 2に書き換え後(つまりリソースエディタで書き換え後)のメニューの定義である。実はこの部分以外一切いじっておらず、逆に言えば生成された80LED.RCのうち、List 1の様になっている部分をエディタなどでList 2の様に書き換えれば、それでメニューが生成されるという仕組みだ。

ついでにここで簡単に説明しておこう。Win32のアプリケーションは、基本的には初期設定が終わった後は、GetMessage()という関数をひたすら呼びながら無限ループを繰り返しているだけである。ただ何か操作があると、たとえばメニューで転送速度として19200bpsが選択されると、IDM_SPEED_19200というメッセージ番号(この番号は先ほどリソースエディタで定義したもの)を持って割り込み(Windows用語ではMessage)がやってきて、制御がWinProc()というcallback関数(Messageの処理を専門に行う関数)に渡される。なので、後はWinProc()の中に「IDM_SPEED_19200がやってきたら、転送速度を19200bpsに設定する」という処理を追加すれば済むことになる。

ただこの方式だと、「んじゃ一定間隔ごとにCPUの負荷をどうやって取得するの?」という疑問があろう。前回の例ではSleep()を使って一定期間待機していたが、Win32のアプリケーションではこれは許されない。そこで、一定期間ごとにMessageを発するTimerを仕掛け、このTimerのMessage到着のタイミングでCPU負荷を取得、それを画面表示すると共にArduinoにパケットを送るという処理をする形となる。次回もう少し、実際のコードをみてみたい。

(続く)

List 1:

/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_MY80LED MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "アプリケーションの終了(&X)",                IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...",           IDM_ABOUT
    END
END

List 2:

IDC_MY80LED MENU
BEGIN
    POPUP "File(&F)"
    BEGIN
        MENUITEM "Exit(&X)",                    IDM_EXIT
    END
    POPUP "Transfer"
    BEGIN
        MENUITEM "Start",                       IDM_XFER_START
        MENUITEM "Stop",                        IDM_XFER_STOP
    END
    POPUP "Settings..."
    BEGIN
        POPUP "Port"
        BEGIN
            MENUITEM "COM1:",                       IDM_PORT_COM1
            MENUITEM "COM2:",                       IDM_PORT_COM2
            MENUITEM "COM3:",                       IDM_PORT_COM3
            MENUITEM "COM4:",                       IDM_PORT_COM4
            MENUITEM "COM5:",                       IDM_PORT_COM5
            MENUITEM "COM6:",                       IDM_PORT_COM6
            MENUITEM "COM7:",                       IDM_PORT_COM7
            MENUITEM "COM8:",                       IDM_PORT_COM8
        END
        POPUP "Speed"
        BEGIN
            MENUITEM "300bps",                      IDM_SPEED_300
            MENUITEM "600bps",                      IDM_SPEED_600
            MENUITEM "1200bps",                     IDM_SPEED_1200
            MENUITEM "2400bps",                     IDM_SPEED_2400
            MENUITEM "4800bps",                     IDM_SPEED_4800
            MENUITEM "9600bps",                     IDM_SPEED_9600
            MENUITEM "19200bps",                    IDM_SPEED_19200
            MENUITEM "38400bps",                    IDM_SPEED_38400
        END
        POPUP "Refresh"
        BEGIN
            MENUITEM "0.1sec",                      IDM_Refresh_100
            MENUITEM "0.2sec",                      IDM_Refresh_200
            MENUITEM "0.5sec",                      IDM_Refresh_500
            MENUITEM "1sec",                        IDM_Refresh_1000
        END
    END
    POPUP "Help(&H)"
    BEGIN
        MENUITEM "About(&A)...",                IDM_ABOUT
    END
END