2020年9月アーカイブ

WindowsのGUIプログラムと呼ぶには何かが足りない? そうでした、ウィンドウを利用する際に代表的な操作方法である「メニュー」が抜け落ちていました。

メニューは拡張子が .rc のテキストファイルに、リソーススクリプトを所定の書式で定義してコンパイル、ウィンドウクラスに登録する事で表示されます。(リソースには、メニューやアイコン、ダイアログボックス、文字列などなどが該当します)

試しに、Win32APIでメニューを表示&動作させるだけのサンプルを作ってみました。


<注意点>

・前回のサンプルに、メニューの操作部分を追加しただけです。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API メニュー 追加 --------------- コマンドライン ---------------*/

D:\vc2008\dllchk>rc resource
D:\vc2008\dllchk>cl test.c user32.lib gdi32.lib resource.res

/*----------------------------------------------------------------------------*/


/*---- resource.h -------------------- お試しソース ------------------------*/

// Resource ID
#define IDR_MENU_MAIN   2000

// Menu Item ID
#define IDM_FILE_OPEN   2001
#define IDM_EXIT        2002
#define IDM_SYSINF_ARCH 2003
#define IDM_SYSINF_TYPE 2004
#define IDM_HELP_ABOUT  2005

/*----------------------------------------------------------------------------*/


/*---- resource.rc ------------------- お試しソース ------------------------*/

#include <windows.h>
#include "resource.h"

IDR_MENU_MAIN MENU
begin
    popup "ファイル(&F)"
    begin
        menuitem "開く(&O)...", IDM_FILE_OPEN
        menuitem separator
        menuitem "終了(&X)",    IDM_EXIT
    end
    popup "ヘルプ(&H)"
    begin
        popup "システム情報"
        begin
            menuitem "OS環境(&A)",    IDM_SYSINF_ARCH
            menuitem "CPU型(&T)",    IDM_SYSINF_TYPE
        end
        menuitem "バージョン情報(&A)",  IDM_HELP_ABOUT
    end
end

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>
#include <process.h>  // _beginthreadex 関数
#include "resource.h"

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

unsigned WINAPI subThread1(void *hCtr)
{
    int i, len;
    char s[] = ", i =   ";

    for(i = 0; i < 5; i++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", i = %d", i);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1000);
    }
    return(0);
}

unsigned WINAPI subThread2(void *hCtr)
{
    int j, len;
    char s[] = ", j =    ";

    for(j = 5; j < 15; j++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", j = %d", j);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(500);
    }
    return(0);
}

unsigned WINAPI subThread3(void *hCtr)
{
    int k, len;
    char s[] = ", k =    ";

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    for(k = 15; k < 19; k++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", k = %d", k);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1500);
    }
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hBtn1, hBtn2, hBtn3, hEdit;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";
    SYSTEM_INFO sysinfo;

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "スレッド");
                _beginthreadex(NULL, 0, subThread1, hEdit, 0, NULL);
                _beginthreadex(NULL, 0, subThread3, hEdit, 0, NULL);
                break;
            case IDM_FILE_OPEN :
                // To Do List
                break;
            case IDM_EXIT :
                DestroyWindow(hWnd);
                break;
            case IDM_SYSINF_ARCH :
                GetNativeSystemInfo(&sysinfo);
                switch(sysinfo.wProcessorArchitecture) {
                case PROCESSOR_ARCHITECTURE_INTEL : // x86
                    MessageBox(NULL, "32ビットです。", "This OS's Version", MB_OK);
                    break;
                case PROCESSOR_ARCHITECTURE_ARM : // ARM
                    break;
                case PROCESSOR_ARCHITECTURE_IA64 : // Intel Itanium-based
                    break;
                case PROCESSOR_ARCHITECTURE_AMD64 : // x64(AMD or Intel)
                    MessageBox(NULL, "64ビットです。", "This OS's Version", MB_OK);
                    break;
//              case PROCESSOR_ARCHITECTURE_ARM64 : // ARM64
//                  break;
                case PROCESSOR_ARCHITECTURE_UNKNOWN :
                default :
                    break;
                }
                break;
            case IDM_SYSINF_TYPE :
                GetNativeSystemInfo(&sysinfo);
                switch(sysinfo.dwProcessorType) {
                case PROCESSOR_INTEL_386 :
                    break;
                case PROCESSOR_INTEL_486 :
                    break;
                case PROCESSOR_INTEL_PENTIUM :
                    MessageBox(NULL, "ペンティアム4です。", "This CPU's Version", MB_OK);
                    break;
                case PROCESSOR_INTEL_IA64 :
                    break;
                case PROCESSOR_AMD_X8664 :
                    MessageBox(NULL, "AMD Ryzen7です。", "This CPU's Version", MB_OK);
                    break;
//              case PROCESSOR_ARM :
                default :
                    break;
                }
                break;
            case IDM_HELP_ABOUT :
                // To Do List
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN);
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsは一時、マルチタスクが死に掛けましたが(高々携帯電話機、i何チャラに引き摺られて)、Ver.10に格下げ?されてマルチタスクが息を吹き返しました。

Windowsには「プロセス」と「スレッド」の2種類のタスクが有りますが、プログラム起動時にはプロセスで、プログラム内ではスレッドでと使い分けると簡単な気が・・・、プロセスは複数のスレッドを持てるようですし。

試しに、Win32APIでスレッドを呼び出すだけのサンプルを作ってみました。


<注意点>

・前回のサンプルに、スレッド部分を追加しただけです。

・通常プロセスを起動すると、その中に一つスレッドが自動的に生成されるので、メインスレッド、サブスレッド1、サブスレッド2、サブスレッド3とあります。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【_beginthreadexの引数】

_beginthreadex APIのパラメータ
パラメータ名説明
thrdAttr void * 新しいスレッドのセキュリティ属性。NULLの場合は実行元と同じになる
stackSize unsigned 新しいスレッドのスタックサイズ。0の場合は作成元と同じになる
thrdProc unsigned (*)(void *) スレッド・プロシージャのアドレス
param void * スレッド・プロシージャへのパラメータ
createFlag unsigned CREATE_SUSPENDED を指定すると、実行を開始せずに作成。0は作成して即実行 *1
thrdId unsigned * 新しいスレッドのスレッドIDを受け取る


 *1:CREATE_SUSPENDED を指定した場合、実行開始するには ResumeThread API を呼び出す必要があります。


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API スレッド 追加 --------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib  gdi32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>
#include <process.h>  // _beginthreadex 関数

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_BUTTON3 1003
#define ID_EDITBOX 1011

unsigned WINAPI subThread1(void *hCtr)
{
    int i, len;
    char s[] = ", i =   ";

    for(i = 0; i < 5; i++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", i = %d", i);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1000);
    }
    return(0);
}

unsigned WINAPI subThread2(void *hCtr)
{
    int j, len;
    char s[] = ", j =    ";

    for(j = 5; j < 15; j++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", j = %d", j);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(500);
    }
    return(0);
}

unsigned WINAPI subThread3(void *hCtr)
{
    int k, len;
    char s[] = ", k =    ";

    _beginthreadex(NULL, 0, subThread2, hCtr, 0, NULL);

    for(k = 15; k < 19; k++) {
        len = SendMessage((HWND)hCtr, WM_GETTEXTLENGTH, 0, 0);
        SendMessage((HWND)hCtr, EM_SETSEL, (WPARAM)len, (LPARAM)len);
        sprintf(s, ", k = %d", k);
        SendMessage((HWND)hCtr, EM_REPLACESEL, 0, (LPARAM)s);
        Sleep(1500);
    }
    return(0);
}

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hBtn1, hBtn2, hBtn3, hEdit;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn3 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン3", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 245, 220, 70, 20, hWnd, (HMENU)ID_BUTTON3, hInstance, NULL)) == NULL) {
            printf("ボタン3コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            case ID_BUTTON3 :
                EnableWindow(hBtn3, FALSE);
                SetWindowText(hBtn3, "スレッド");
                _beginthreadex(NULL, 0, subThread1, hEdit, 0, NULL);
                _beginthreadex(NULL, 0, subThread3, hEdit, 0, NULL);
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
            SetWindowText(hBtn3, "ボタン3");
            EnableWindow(hBtn3, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

ウィンドウを画面に表示しただけでは、UIと呼ぶには余りにもお粗末。

Windowsプログラムの処理動作には、「手続き型」と呼ばれるコンソールプログラムと「イベントドリブン型」と呼ばれるGUIプログラムの2種類があります。

イベントには、キー入力やマウス操作、メニュー選択などなどが有り、ウィンドウはこうしたイベントをシステムから受け取ります。

試しに、コントロール(部品)を2~3配置して、マウスに応答させるだけのサンプルを作ってみました。

前回のサンプルでは、ウィンドウが直ぐ閉じない様にメッセージボックス関数を return の前に入れていましたが、今回は本来のイベント待ち処理である「メッセージループ」(while 文)に置き換えています。

メッセージループの基本動作は前述したイベントがシステム(Windows)から通知されると、その処理を実行するウィンドウプロシージャを呼び出す、の繰り返しです。

ウィンドウには共通コントロールとして、「ボタン」や「エディットボックス」、「リストボックス」、「コンボボックス」、「スタティックコントロール」などなど、システムのウィンドウクラスが多数用意されています。

つまり、色々なアプリケーションで使われている「ボタン」コントロールは、システム側が提供するウィンドウクラスですので、CreateWindowEx API を使用して、ウィンドウ(=サブウィンドウ)を作成することになります。


<注意点>

・共通コントロールはサブウィンドウですので、ウィンドウ・スタイルには WS_CHILD を含めて指定することになります。メインウィンドウのスタイルは、WS_OVERLAPPEDWINDOW を指定するのが標準のようです。

・ウィンドウは、作成時に必ず、WM_CREATEメッセージの通知を受け取るので、そこでサブウィンドウなどを作成する事になります。

・メインとサブのウィンドウ名の表示効果に違いが有ります。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【MSGの構造体】

MSG構造体のメンバー
メンバー名説明
hwnd HWND メッセージの宛先ウィンドウハンドル
message UINT メッセージID
wParam WPARAM メッセージパラメータ1
lParam LPARAM メッセージパラメータ2
time DWORD メッセージがポストされた時刻
pt POINT メッセージがポストされた時のカーソル位置


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API ウィンドウ 作成 ------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib  gdi32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>

#define WINCLASS_NAME "Form.003"
#define ID_BUTTON1 1001
#define ID_BUTTON2 1002
#define ID_EDITBOX 1011

LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static HWND hLbl1, hEdit, hBtn1, hBtn2;
    static unsigned short x, y, z;
    HINSTANCE hInstance;
    int wmid, wmevent;
    PAINTSTRUCT ps;
    HDC hdc;
    char str[] = "X =       , Y =       , Button =       ";

    switch(uMsg) {
    case WM_CREATE :
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

        if((hLbl1 = CreateWindowEx(WS_EX_LEFT, "STATIC", "ラベル1", WS_CHILD | WS_VISIBLE, 200, 120, 150, 20, hWnd, NULL, hInstance, NULL)) == NULL) {
            printf("ラベル1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hEdit = CreateWindowEx(WS_EX_LEFT, "EDIT", "エディットボックス1", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE, 50, 50, 100, 150, hWnd, (HMENU)ID_EDITBOX, hInstance, NULL)) == NULL) {
            printf("エディットボックスコントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn1 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン1", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 75, 220, 70, 20, hWnd, (HMENU)ID_BUTTON1, hInstance, NULL)) == NULL) {
            printf("ボタン1コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        if((hBtn2 = CreateWindowEx(WS_EX_LEFT, "BUTTON", "ボタン2", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 160, 220, 70, 20, hWnd, (HMENU)ID_BUTTON2, hInstance, NULL)) == NULL) {
            printf("ボタン2コントロールを作成出来ませんでした。\n");
            return(FALSE);
        }
        break;
    case WM_COMMAND :
        wmid = LOWORD(wParam);
        wmevent = HIWORD(wParam);
        switch(wmevent) {
        case BN_CLICKED :
            switch(wmid) {
            case ID_BUTTON1 :
                EnableWindow(hBtn1, FALSE);
                SetWindowText(hBtn1, "押下1");
                SetWindowText(hLbl1, "コンパイルエラーが来たぁ!");
                break;
            case ID_BUTTON2 :
                EnableWindow(hBtn2, FALSE);
                SetWindowText(hBtn2, "押下2");
                SetWindowText(hEdit, "修正漏れが見つかったぞぉ!");
                break;
            default :
                break;
            }
            break;
        default :
            break;
        }
        break;
    case WM_LBUTTONDOWN :
    case WM_RBUTTONDOWN :
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        z = LOWORD(wParam);
        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_PAINT :
        hdc = BeginPaint(hWnd, &ps);
        sprintf(str, "X = %d, Y = %d, Button = %s", x, y, (z == 1 ? "Left" : "Right") );
        TextOut(hdc, 30, 10, str, (int)strlen(str));
        EndPaint(hWnd, &ps);
        break;
    case WM_CLOSE :
        if(MessageBox(hWnd, "ウィンドウを閉じます。\nよろしいですか?", "確認", MB_OKCANCEL | MB_ICONWARNING) == IDOK) {
            DestroyWindow(hWnd);
        } else {
            SetWindowText(hLbl1, "ラベル1");
            SetWindowText(hBtn1, "ボタン1");
            EnableWindow(hBtn1, TRUE);
            SetWindowText(hBtn2, "ボタン2");
            EnableWindow(hBtn2, TRUE);
        }
        break;
    case WM_DESTROY :
        PostQuitMessage(0);
        break;
    default :
        return DefWindowProc(hWnd , uMsg , wParam , lParam);
    }

    return(0);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    MSG msg;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = WinProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    /* メッセージループ */
    while((ret = GetMessage(&msg, NULL, 0, 0)) > 0 ) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    if(ret == -1) {
        printf("メッセージの取得に失敗しました。\n");
        return(FALSE);
    }
    return((int)msg.wParam);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Windowsアプリケーションの特徴はウィンドウ。GUIプログラムの基本もウィンドウ。って事は、全てのWindowsアプリケーションはGUIアプリケーション?

などと考えている内に、Win32APIでも触ってみるかという事に。

試しに、Windowsのウィンドウを表示させるだけのサンプルを作ってみました。

ただ、素人にはウィンドウ・プログラムの基本動作は任せられないとマイクロソフトが想定したかどうかは分かりませんが、ウィンドウクラスの登録(RegisterClassEx API)とウィンドウの生成(CreateWindowEx API)で、ほぼ最低限の機能を持つウィンドウが表示されたのには感心しました。


<注意点>

・サンプルを実行すると、プログラム終了と同時にウィンドウが閉じてしまうので、最低限の動作を確認できるように return の直前でメッセージボックス関数を呼び出し、何某かの入力待ちとなるようにしています。

・今回はウィンドウを表示するだけなので、Windowsメッセージを処理する関数は登録しません。代わりにWNDCLASSEXの lpfnWndProc には、デフォルトの処理を実行してくれる DefWindowProc API を登録します。

「stdio.h」を「cstdio」に変更することで、.cppファイルとしてもコンパイル出来ます。


【WNDCLASSEXの構造体】

WNDCLASSEX構造体のメンバー
メンバー名説明
cbSize UINT WNDCLASSEX 構造体のサイズ
style UINT クラスのスタイル
lpfnWndProc WNDPROC ウィンドウ・プロシージャ(関数)のポインタ
cbClsExtra UINT ウィンドウ・クラスの追加データサイズ
cbWndExtra UINT ウィンドウ毎の追加データサイズ
hInstance HINSTANCE 実行プログラムのインスタンス・ハンドル
hIcon HICON ウィンドウを最小化した時に表示に使用されるアイコン
hCursor HCURSOR マウスカーソルがウィンドウ上にある時のカーソル形状
hbrBackground HBRUSH ウィンドウのクライアント領域を塗り潰すのに使うブラシ
lpszMenuName LPCTSTR メニュー名(デフォルトでリソースID)
lpszClassName LPCTSTR ウィンドウ・クラス名
hIconSm HICON システム・メニューに表示される小さなアイコン


【CreateWindowExの引数】

CreateWindowEx APIのパラメータ
パラメータ名説明
dwExStyle DWORD 拡張ウィンドウスタイル(CreateWindowEx API のみ)
lpClassName LPCTSTR ウィンドウ・クラス名
lpWindowName LPCTSTR ウィンドウ名(タイトルバーなどに表示)
dwStyle DWORD ウィンドウ・スタイル
x int ウィンドウの初期位置(左上隅、x座標)
y int ウィンドウの初期位置(左上隅、y座標)
nWidth int ウィンドウの初期サイズ、横幅
nHeight int ウィンドウの初期サイズ、高さ
hWndParent HWIND 子ウィンドウ、親ウィンドウのハンドル
上記以外、 所有ウィンドウのハンドル
hMenu HMENU 子ウィンドウ、子ウィンドウID
上記以外、 メニュー・ハンドル(デフォルトの場合NULL)
hInstance HINSTANCE ウィンドウ・クラスのインスタンス・ハンドル
lpParam LPVOID 任意のパラメータ


お試し環境
  WindowsXP 32bit Edition
  Visual C++ 2008


/*---- Win32API ウィンドウ 作成 ------------- コマンドライン ---------------*/

D:\vc2008\dllchk>cl test.c user32.lib

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------------ お試しソース ------------------------*/

#include <stdio.h>
#include <windows.h>

#define WINCLASS_NAME "Form.003"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wcx;
    int ret;

    /* ウィンドウクラスの登録 */
    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW | CS_VREDRAW;
    wcx.lpfnWndProc = DefWindowProc;
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.hInstance = hInstance;
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcx.lpszMenuName = NULL;
    wcx.lpszClassName = WINCLASS_NAME;
    wcx.hIconSm = NULL;

    if(!RegisterClassEx(&wcx)) {
        printf("ウィンドウクラスの登録が出来ませんでした。\n");
        return(0);
    }

    /* 登録したクラスのウィンドウを生成 */
    hWnd = CreateWindowEx(WS_EX_LEFT, WINCLASS_NAME, "ウインドウタイトル3", WS_OVERLAPPEDWINDOW, 50, 50, 400, 300, NULL, NULL, hInstance ,NULL);
    if(hWnd == NULL) {
        printf("ウィンドウが作成出来ませんでした。\n");
        return(FALSE);
    }

    /* ウィンドウの表示 */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    ret = MessageBox(NULL, "人為的地球温暖化はあやかしだと思うか?", "気候変動", MB_YESNOCANCEL | MB_ICONQUESTION);
    switch(ret) {
    case IDYES :
        MessageBox(NULL, "地球は訴えかけている\n命を護る行動を", "全てを救え、頼んだぞ", MB_OK | MB_ICONWARNING);
        break;
    case IDNO :
        MessageBox(NULL, "宇宙線は囁いている\n坐して死を待つのかと", "地球寒冷化だ", MB_OK | MB_ICONERROR);
        break;
    default :
        MessageBox(NULL, "防御態勢を整えながら\n次の衝撃波に備えよ", "総員配置に付け", MB_CANCELTRYCONTINUE | MB_ICONSTOP);
        break;
    }

    return(0);
}

/*----------------------------------------------------------------------------*/
/*============================================================================*/