ちぃさいずWindowEXE

注意:ここではある程度デモの話,C言語の基礎,WinAPIがわかるという前提で話を進めていきます。
その辺の話からよくわからない方はそういうサイトを回ってからの方が良いと思われます。
あと、VisualC++(以下VC)限定なので他の方にはあまり有効でないかもしれません。


前回の続きですが今回はデモのキャンバス(?)となるWindowを出したいと思います。
このウインドウを出す手順は普通のWinAPIプログラムと手順はなんら変わりません。
ただ、普通のAPIプログラムよりもサイズを少し稼げる方法でやっていきたいと思います。
注:この方法は4k/64kデモでは一般的な話らしいですが、他ではそうでないと思うので
そういう時は使わないほうがいいかもしれません。
では、ソースを見ていきたいと思います

#pragma comment(linker,"/subsystem:windows")
#pragma comment(linker,"/NODEFAULTLIB")
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

HINSTANCE hInstance;
void OnIdle()
{
    //ここにアイドル時の処理を書く   
}

void WinMainCRTStartup()
{
    HWND hWnd;
    hWnd =  CreateWindowEx(WS_EX_APPWINDOW,"STATIC",
                          "TITLE", WS_POPUP, 0, 0,
                          640, 480, NULL, NULL, hInstance,NULL);
    ShowWindow(hWnd,1); // メイン  
                             
    //メッセージループ
    MSG msg;
    while(TRUE){
        if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
            if((msg.message==WM_CHAR)&&(msg.wParam==0x1B)) break;//ESC
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }else{
            OnIdle();//アイドル
        }
    }

    //後処理
    ExitProcess(0);//全スレッド強制終了
    return ;
}

青い部分前回説明したところなので飛ばします。
では、解説していきます。 まず、WinMainCRTStartup()の中の
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_APPWINDOW,"STATIC",
                                 "TITLE", WS_POPUP,
                                  0, 0, 640, 480,
                                  NULL, NULL, hInstance,NULL);

これはいわずと知れたCreateWindowEx関数ですね。
ただ、いつもならこの関数の前にWNDCLASSEXの構造体を宣言して
その構造体のいくつかの情報を入れてRegisterClassEx関数で登録し
この関数の第2引数にWNDCLASSの名前を渡すはずですが、
このプログラムではそうはせずに、なぜか"STATIC"を渡しています。
"STATIC"は定義済みのWNDCLASSでウインドウ上に文字列を表示するための
コントロールとして使われます。(まぁボタンとかリストボックスとかの仲間です)
なんでそんなものを使うのかというと、WNDCLASS構造体のサイズとRegisterClassEx関数の
コードサイズをケチるためです。(w
たった数十バイトですから64kの時はあまり気にしなくていいサイズではありますが
4kのときは結構ありがたいサイズです。
あと、最後のほうのhInstanceですが、明らかに何も代入されてないのに関数に渡しています。
通常ならWinMain()の引数としてInstanceがWindowsから渡されますが、WinMainCRTStartup()から
はじめているので、Instanceを手に入れる方法がありません。
ここは私もよくわからないのですが、なぜかグローバルでHINSTANCE宣言してそれをCreateWindowEx()
に渡してやるとちゃんと動きます。(もちろんローカルで宣言しても動きます)
まぁおそらく"STATIC"などのWindowsがはじめからもってるウインドウクラスなんで
別にインスタンスは何でもいいのではないかと・・・(謎)


ShowWindow(hWnd, 1);
これはWindowを表示させるようにする関数ですね。
これをやんないとWindowが見えないので。


メッセージループ
これはいわずと知れたメッセージループです。
ただ、よく毎回使われるのは
while(TRUE){
GetMessage(&msg, NULL, 0, 0);
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
   while(TRUE){
        if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }else{
            OnIdle();
        }
   }
左側のようなGetMessage()でやるものが多いですが、
ここではIdle関数を作りたいがために、PeekMessage()を使っています。
詳しくはAPIを調べてもらえばいいのですが、簡単にその違いを言うと
それぞれの関数を呼び出すと、GetMessageはWindowsからメッセージを
受け取るまで、関数から処理が返りませんが、PeekMessageはメッセージが
なくても、すぐに処理が返ってきます。
これにより、PeekMessageで処理するメッセージがないときは
Idle()関数を実行するようにしているというわけです。
注:アイドル関数とはウインドウメッセージなどがないときに
CPUのパワーを100%使うための関数です。デモやゲームなどでは
メッセージなどを受け取ってから処理をしいては遅いので
このような形のアイドル関数を使うことがしばしばあります。
MFCやGLUTなどにもあると思います。


if((msg.message==WM_CHAR)&&(msg.wParam==0x1B)) break;
これは本来ならWndProcなどのメッセージを処理するコールバック関数の中に
書くのが普通ですが、CreateWindowExで"STATIC"を渡してウインドウを作ったので
コールバック関数が設定できないので、仕方なくメッセージループに割り込む形で
ここに書いています。処理の内容はESCを押されたらメッセージループから抜けるという
まぁデモお決まりの終了スタイルですね。


というわけでサンプルのなかのminiwin.exeを実行してみてください

ちゃんとウインドウがが出ますね。ウインドウといっても単なる下地だけで
何があるわけでもないし、タイトルバーもないですからねぇ
タイトルバーを出したい場合はCreateWindowのところで"WS_POPUP"ではなく "WS_OVERLAPPEDWINDOW"を指定したらタイトルバーが出てくると思います。
でサイズは・・・2KBですね。
あれ、前回のMessageBoxを出したやつとサイズが一緒じゃん・・・。
実はこの程度のWindowを出したりすることは数十バイト程度のコード量なのですが、
WindowsのEXEが一定サイズ区切りでしか作れないことに原因があります
なので、実はEXEの中は0がいっぱいです。(バイナリエディタなどで見てみてください)
前回のminiexe.exeと一緒に見て比べて見るのもいいかもしれません。
今回追加したコードでどれくらい増えたのか・・・


●まとめ
さて、これでWindowも出せるようになったのでメガデモの下準備はほぼ終わりです。
あとはこれにグラフィックのコードやサウンドのコードを追加していけば!
まぁメガデモの4k/64kはは、普段は何気なく楽をして標準関数やらSTLやらで書いてる
コードをがりがり自分で書きまくってつくっていくようなものだと思います。
そういう中で、いかにサイズを削りながらプログラムを組んでいくかということにつきる
と思います。で、そこで案外意外なのが、デモを見てる側からみると64kという小ささなのに
なんてこれほどまでにデモがつまってるんだ!と思うんですが、作ってみると逆に
64kはなんて大きいんだ!ぜんぜんサイズがうまらねー!ってことになります。
(まぁこれはちゃんと作ればの話ですよ。どっかで楽をするととたんにサイズが増えます)
というわけで、 次回はこれにOpenGLベースの64k/4kデモテンプレートをつくってみたいと思います。



感想・質問・間違い指摘はBBSまで・・・