第344章 時計を作る ストップウォッチを付ける


今回は、簡単なストップウォッチ機能を付けます。



クライアント領域を右クリックすると「ストップウォッチ」というメニュー項目がありまます。



メニューから「ストップウォッチ」を選択すると、左の図のような ダイアログボックスが出現します。

「スタート」ボタンを押すと本体の方で、計測時間が刻々と表示されます。 また、「スタート」ボタンを押すと「ストップ」ボタンが使用可能となります。 「ストップ」ボタンを押すと計測が止まり、「リセット」ボタンが使用可能となります。

「リセット」ボタンを押すと表示が「0:00:00-000」となります。



左の図は、ストップウォッチで計測中のものです。(一応1/1000秒単位で 表示されますが、それほど精密ではありません。)



では、プログラムを見てみましょう。

// clock08.rcの一部

/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

MYMENU MENU 
BEGIN
    POPUP "ダミーです"
    BEGIN
        POPUP "ファイル(&F)"
        BEGIN
            MENUITEM "終了(&X)...",                 IDM_END
        END
        POPUP "オプション(&O)"
        BEGIN
            MENUITEM "背景色(&B)...",               IDM_BACKGROUND
            MENUITEM "文字色(&T)...",               IDM_TEXT
            MENUITEM "文字盤の色(&P)...",           IDM_PLATE
            MENUITEM "長針の色(&L)...",             IDM_LONG
            MENUITEM "短針の色(&S)...",             IDM_SHORT
            MENUITEM "秒針の色(&O)...",             IDM_SECOND
        END
        POPUP "アラーム(&L)"
        BEGIN
            MENUITEM "時刻設定(&S)...",             IDM_ALARMSET
        END
        POPUP "タイマー(&T)"
        BEGIN
            MENUITEM "タイマーセット(&T)...",       IDM_TIMER
            MENUITEM "ストップウォッチ(&S)...",     IDM_STOPWATCH
        END
        POPUP "ストップウォッチ(&S)"
        BEGIN
            MENUITEM "ストップウォッチ(&S)...",     IDM_STOPWATCH
        END
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

CAT                     BITMAP                  "bitmap1.bmp"
MASK                    BITMAP                  "bmp00001.bmp"

/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

MYALARM DIALOGEX 0, 0, 115, 73
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | 
    WS_SYSMENU
CAPTION "アラーム設定"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
    DEFPUSHBUTTON   "OK",IDOK,7,52,50,14
    PUSHBUTTON      "キャンセル",IDCANCEL,58,52,50,14
    CONTROL         "アラームをセットする",IDC_CHECK1,"Button",
                    BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,15
    CONTROL         "",IDC_DATETIMEPICKER1,"SysDateTimePick32",
                    DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP,30,25,54,17
END

MYTIMER DIALOGEX 0, 0, 123, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | 
    WS_SYSMENU
CAPTION "タイマー設定"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
    DEFPUSHBUTTON   "スタート",IDOK,7,33,50,14
    PUSHBUTTON      "ストップ",IDCANCEL,65,33,50,14
    CONTROL         "",IDC_DATETIMEPICKER1,"SysDateTimePick32",
                    DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP,35,7,53,19
END

MYSTOPWATCH DIALOGEX 0, 0, 131, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | 
    WS_SYSMENU
CAPTION "ストップウォッチ"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
    DEFPUSHBUTTON   "スタート",IDOK,7,7,50,14
    PUSHBUTTON      "閉じる",IDCANCEL,71,31,50,14
    PUSHBUTTON      "ストップ",IDC_STOP,71,7,50,14
    PUSHBUTTON      "リセット",IDC_RESET,7,31,50,14
END

/////////////////////////////////////////////////////////////////////////////
//
// WAVE
//

BELL                    WAVE                    "ringin.wav"
リソース・スクリプトですが、メニューに「ストップウォッチ」「ストップウォッチ」が増えました。

また、ストップウォッチ用のダイアログが増えました。

// clock08.cpp

#define hParentKey HKEY_CURRENT_USER
#define lpszSubKey "Software\\Kumei\\Clock"
#define PAI 3.14159

#define CLOCK_WIDTH 250        //時計全体のウィンドウ幅
#define CLOCK_HEIGHT 60        //高さ
#define MYTIMER 1            //タイマーID
#define MYSTOP 2
#include <windows.h>
#include <windowsx.h>
#include <math.h>
#include <commctrl.h>
#include <Mmsystem.h>
#include "resource.h"

typedef struct MYDATA {
    COLORREF cr_bg;        //背景色
    COLORREF cr_txt;    //文字色
    COLORREF cr_plate;    //文字盤の色
    COLORREF cr_short;    //短針の色
    COLORREF cr_long;    //長針の色
    COLORREF cr_second;    //秒針の色
    int x;
    int y;
    char szAlarmSet[8];
    char szAlarmTime[16];
    char szTimerTime[16];
    char szStopWatch[8]; //ストップウォッチモードかどうか
    char szStopTime[16]; //ストップウォッチモードの時の時間
} INIDATA;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyAlarmProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyTimerProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyStopWatchProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
HFONT SetMyFont(LPCTSTR, int);
BOOL GetMyColor(HWND, COLORREF *, COLORREF);
void GetInitialSettings(INIDATA *);
BOOL GetDataDWORD(char *, DWORD *);
BOOL GetDataString(char *, char *);
BOOL SetInitialSettings(INIDATA);
BOOL SetDataDWORD(char *, DWORD);
BOOL SetDataString(char *, char *);
BOOL ShowClock(HDC, char *, INIDATA);
BOOL GetHMS(char *, int *, int *, int *);
BOOL ShowBitmap(HDC, INIDATA);
BOOL MyTimerShow(HDC, int, int, INIDATA, DWORD);
BOOL SetHMS(char *, DWORD);
BOOL SetStopHMS(char *, DWORD);

char szClassName[] = "clock08";    //ウィンドウクラス
char szAppName[] = "猫クロック"; //アプリケーション名
HINSTANCE hInst;
新しくストップウォッチ用タイマーIDのMYSTOPをdefineしました。

INIDATA構造体のメンバが増えました。

ストップウォッチ用ダイアログボックスのプロシージャが増えました。

また、システム時間からストップウォッチの時間の文字列を作るSetStopHMS関数が増えました。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    
    hInst = hCurInst;

    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hCursor = (HCURSOR)LoadImage(NULL,
        MAKEINTRESOURCE(IDC_ARROW),
        IMAGE_CURSOR,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);

    return (RegisterClassEx(&wc));
}

//ウィンドウの生成

BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;


    hWnd = CreateWindowEx(WS_EX_TOPMOST, //拡張ウィンドウスタイル
            szClassName,
            "猫でもわかるWindowsプログラミング", //タイトルバーにこの名前が表示されます
            WS_POPUP, //ウィンドウの種類
            0,    //X座標
            0,    //Y座標
            CLOCK_WIDTH,    //幅
            CLOCK_HEIGHT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
これらの関数に変更はありません。
//ウィンドウプロシージャ

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id, x, y;
    SYSTEMTIME st;
    PAINTSTRUCT ps;
    HDC hdc;
    HFONT hFont;
    SIZE s;
    HRGN hRgn, hRgn1, hRgn2, hRound1Rgn, hRound2Rgn, hRectRgn;
    HBRUSH hBrush;
    HMENU hMenu, hSubMenu;
    POINT pt;
    COLORREF cr;
    static INIDATA inidata;
    RECT rc;
    char szYobi[8];
    static char szBuf[64], szBuf2[64]; //時刻表示用
    INITCOMMONCONTROLSEX ic;
    static BOOL bTimer; //タイマ表示かどうか
    static DWORD dwStartMs;
    MMTIME mm;

    switch (msg) {
        case WM_CREATE:
            ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
            ic.dwICC = ICC_DATE_CLASSES;
            InitCommonControlsEx(&ic);
            GetInitialSettings(&inidata);
            strcpy(inidata.szStopTime, "00:00:00");
            MoveWindow(hWnd,
                inidata.x, 
                inidata.y, 
                CLOCK_WIDTH, 
                CLOCK_HEIGHT, 
                TRUE);
            SetTimer(hWnd, MYTIMER, 1000, NULL);

            hRgn = CreateRectRgn(0, 0, 1, 1);
            hRgn1 = CreateRectRgn(0, 0, 1, 1);
            hRgn2 = CreateRectRgn(0, 0,1, 1);

            hRound1Rgn = CreateEllipticRgn(0, 0, CLOCK_HEIGHT, CLOCK_HEIGHT);
            hRectRgn = CreateRectRgn(CLOCK_HEIGHT / 2,
                0, 
                CLOCK_WIDTH - CLOCK_HEIGHT / 2,
                CLOCK_HEIGHT);
            CombineRgn(hRgn1, hRound1Rgn, hRectRgn, RGN_OR);
            hRound2Rgn = CreateEllipticRgn(CLOCK_WIDTH - CLOCK_HEIGHT,
                0,
                CLOCK_WIDTH,
                CLOCK_HEIGHT);
            CombineRgn(hRgn2, hRound2Rgn, hRectRgn, RGN_OR);
            CombineRgn(hRgn, hRgn1, hRgn2, RGN_OR);
            SetWindowRgn(hWnd, hRgn, TRUE);

            DeleteObject(hRound1Rgn);
            DeleteObject(hRound2Rgn);
            DeleteObject(hRectRgn);
            DeleteObject(hRgn1);
            DeleteObject(hRgn2);
            
            break;
        case WM_RBUTTONDOWN:
            pt.x = LOWORD(lp);
            pt.y = HIWORD(lp);
            hMenu = LoadMenu(hInst, "MYMENU");
            hSubMenu = GetSubMenu(hMenu, 0);
            ClientToScreen(hWnd, &pt);
            TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
            DestroyMenu(hMenu);
            break;
        case WM_LBUTTONDOWN:
            PostMessage(hWnd, WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lp);
            break;
        case WM_TIMER:
            if (wp != MYTIMER)
                return DefWindowProc(hWnd, msg, wp, lp);
            GetLocalTime(&st);
            wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
            switch (st.wDayOfWeek) {
                case 0:
                    strcpy(szYobi, "Sun");
                    break;
                case 1:
                    strcpy(szYobi, "Mon");
                    break;
                case 2:
                    strcpy(szYobi, "Tue");
                    break;
                case 3:
                    strcpy(szYobi, "Wed");
                    break;
                case 4:
                    strcpy(szYobi, "Thu");
                    break;
                case 5:
                    strcpy(szYobi, "Fri");
                    break;
                case 6:
                    strcpy(szYobi, "Sat");
                    break;
            }

            wsprintf(szBuf2, "%d/%02d/%02d(%s)", st.wYear, st.wMonth, st.wDay, szYobi);
                        
            if (strcmp(inidata.szAlarmSet, "Yes") == 0 && strcmp(szBuf, inidata.szAlarmTime) == 0) {
                PlaySound("BELL", hInst, SND_RESOURCE | SND_ASYNC | SND_LOOP);
                MessageBox(hWnd, "時間ですよ", szAppName, MB_OK);
                PlaySound(NULL, hInst, SND_PURGE);
            }
            InvalidateRect(hWnd, NULL, TRUE);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            hBrush = CreateSolidBrush(inidata.cr_bg);
            SelectObject(hdc, hBrush);
            PatBlt(hdc, 0, 0, CLOCK_WIDTH, CLOCK_HEIGHT, PATCOPY);
            ShowBitmap(hdc, inidata);

            hFont = SetMyFont("MS ゴシック", 30);
            SelectObject(hdc, hFont);
            GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
            
            x = (CLOCK_WIDTH - s.cx) / 2 + 4;
            y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
            
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, inidata.cr_txt);
            if (strcmp(inidata.szStopWatch, "Yes") == 0) {
                hFont = SetMyFont("MS ゴシック", 24);
                SelectObject(hdc, hFont);
                GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
            
                x = (CLOCK_WIDTH - s.cx) / 2;
                y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
                TextOut(hdc, x, y, inidata.szStopTime, (int)strlen(inidata.szStopTime));
                DeleteObject(hFont);
            } else {
                hFont = SetMyFont("MS ゴシック", 30);
                SelectObject(hdc, hFont);
                GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
            
                x = (CLOCK_WIDTH - s.cx) / 2 + 4;
                y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
                TextOut(hdc, x, y, szBuf, (int)strlen(szBuf));
                DeleteObject(hFont);
            }
            

            hFont = SetMyFont("MS ゴシック", 16);
            SelectObject(hdc, hFont);
            GetTextExtentPoint32(hdc, szBuf2, (int)strlen(szBuf2), &s);
            x = (CLOCK_WIDTH - s.cx) / 2 + 4;
            if (!bTimer) {
                TextOut(hdc, x, 4, szBuf2, (int)strlen(szBuf2));
            } else {
                if (MyTimerShow(hdc, x, 4, inidata, dwStartMs)) {
                    bTimer = FALSE;
                    PlaySound("BELL", hInst, SND_RESOURCE | SND_ASYNC | SND_LOOP);
                    MessageBox(hWnd, "時間ですよ", szAppName, MB_OK);
                    PlaySound(NULL, hInst, SND_PURGE);
                }
            }
            DeleteObject(hFont);
            DeleteObject(hBrush);

            ShowClock(hdc, szBuf, inidata);
            
            EndPaint(hWnd, &ps);
            break;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDM_END:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);
                    break;
                case IDM_BACKGROUND:
                    if (GetMyColor(hWnd, &cr, inidata.cr_bg)) {
                        inidata.cr_bg = cr;
                    }
                    break;
                case IDM_TEXT:
                    if (GetMyColor(hWnd, &cr, inidata.cr_txt)) {
                        inidata.cr_txt = cr;
                    }
                    break;
                case IDM_PLATE:
                    if (GetMyColor(hWnd, &cr, inidata.cr_plate)) {
                        inidata.cr_plate = cr;
                    }
                    break;
                case IDM_SHORT:
                    if (GetMyColor(hWnd, &cr, inidata.cr_short)) {
                        inidata.cr_short = cr;
                    }
                    break;
                case IDM_LONG:
                    if (GetMyColor(hWnd, &cr, inidata.cr_long)) {
                        inidata.cr_long = cr;
                    }
                    break;
                case IDM_SECOND:
                    if (GetMyColor(hWnd, &cr, inidata.cr_second)) {
                        inidata.cr_second = cr;
                    }
                    break;
                case IDM_ALARMSET:
                    DialogBoxParam(hInst, "MYALARM", hWnd, (DLGPROC)MyAlarmProc, (LPARAM)&inidata);
                    break;
                case IDM_TIMER:
                    if (DialogBoxParam(hInst,
                        "MYTIMER", 
                        hWnd, 
                        (DLGPROC)MyTimerProc, 
                        (LPARAM)&inidata) == IDOK) {
                            bTimer = TRUE;
                            memset(&mm, 0, sizeof(MMTIME));
                            mm.wType = TIME_MS;
                            timeGetSystemTime(&mm, sizeof(MMTIME));
                            dwStartMs = mm.u.ms;
                        } else {
                            bTimer = FALSE;
                        }
                    break;
                case IDM_STOPWATCH:
                    DialogBoxParam(hInst, "MYSTOPWATCH", hWnd, (DLGPROC)MyStopWatchProc, (LPARAM)&inidata);
                    break;
            }
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd,
                "終了してもよろしいですか",
                szAppName,
                MB_YESNO | MB_ICONQUESTION);
            if (id == IDYES) {
                GetWindowRect(hWnd, &rc);
                inidata.x = rc.left;
                inidata.y = rc.top;
                DestroyWindow(hWnd);
            }
            break;
        case WM_DESTROY:
            SetInitialSettings(inidata);
            KillTimer(hWnd, MYTIMER);
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
メインウィンドウのプロシージャです。

WM_PAINTメッセージが来た時の処理がちょっと増えました。

inidata.szStopWatchが"Yes"(現在ストップウォッチモードである)の時と そうでない時で、表示する内容を変えています。

ストップウォッチモードの時は、フォントサイズを小さめにして、 inidata.szStopTimeを表示します。

メニューからIDM_STOPWATCHが選択されたら、ダイアログを出します。

HFONT SetMyFont(LPCTSTR face, int h)
{
    HFONT hFont;
    hFont = CreateFont(h,    //フォント高さ
        0,                    //文字幅
        0,                    //テキストの角度
        0,                    //ベースラインとx軸との角度
        FW_REGULAR,            //フォントの重さ(太さ)
        FALSE,                //イタリック体
        FALSE,                //アンダーライン
        FALSE,                //打ち消し線
        SHIFTJIS_CHARSET,    //文字セット
        OUT_DEFAULT_PRECIS,    //出力精度
        CLIP_DEFAULT_PRECIS,//クリッピング精度
        PROOF_QUALITY,        //出力品質
        FIXED_PITCH | FF_MODERN,//ピッチとファミリー
        face);    //書体名
    
    return hFont;
}

BOOL GetMyColor(HWND hWnd, COLORREF *lpcr, COLORREF org_cr)
{
    CHOOSECOLOR cc;
    static DWORD dwCustColors[16];

    cc.lStructSize = sizeof(CHOOSECOLOR);
    cc.hwndOwner = hWnd;
    cc.lpCustColors = dwCustColors;
    cc.rgbResult = org_cr;
    cc.Flags = CC_RGBINIT;

    if (ChooseColor(&cc)) {
        *lpcr = cc.rgbResult;
        return TRUE;
    }

    return FALSE;
}
これらの関数に変更はありません。
void GetInitialSettings(INIDATA *lpini)
{
    DWORD dwData;
    char szData[32];

    if (GetDataDWORD("background-color", &dwData)) {
        lpini->cr_bg = (COLORREF)dwData;
    } else {
        lpini->cr_bg = RGB(0, 255, 255);
    }
    if (GetDataDWORD("text-color", &dwData)) {
        lpini->cr_txt = (COLORREF)dwData;
    } else {
        lpini->cr_txt = RGB(0, 0, 0);
    }
    if (GetDataDWORD("x", &dwData)) {
        lpini->x = (int)dwData;
    } else {
        lpini->x = 0;
    }
    if (GetDataDWORD("y", &dwData)) {
        lpini->y = (int)dwData;
    } else {
        lpini->y = 0;
    }
    if (GetDataDWORD("short-color", &dwData)) {
        lpini->cr_short = (COLORREF)dwData;
    } else {
        lpini->cr_short = RGB(0, 0, 255);
    }
    if (GetDataDWORD("long-color", &dwData)) {
        lpini->cr_long = (COLORREF)dwData;
    } else {
        lpini->cr_long = RGB(0, 0, 0);
    }
    if (GetDataDWORD("second-color", &dwData)) {
        lpini->cr_second = (COLORREF)dwData;
    } else {
        lpini->cr_second = RGB(255, 0, 0);
    }
    if (GetDataDWORD("plate-color", &dwData)) {
        lpini->cr_plate = (COLORREF)dwData;
    } else {
        lpini->cr_plate = RGB(0, 255, 255);
    }
    if (GetDataString("alarm-set", szData)) {
        strcpy(lpini->szAlarmSet, szData);
    } else {
        strcpy(lpini->szAlarmSet, "No");
    }
    if (GetDataString("alarm-time", szData)) {
        strcpy(lpini->szAlarmTime, szData);
    } else {
        strcpy(lpini->szAlarmTime, "00:00:00");
    }
    if (GetDataString("timer-time", szData)) {
        strcpy(lpini->szTimerTime, szData);
    } else {
        strcpy(lpini->szTimerTime, "00:00:00");
    }
    if (GetDataString("stopwatch-mode", szData)) {
        strcpy(lpini->szStopWatch, szData);
    } else {
        strcpy(lpini->szStopWatch, "No");
    }
    return;
}
レジストリからデータを読み出してINIDATA構造体に書き込む関数です。 stopwatch-modeエントリが増えています。
BOOL GetDataDWORD(char *szName, DWORD *dwValue)
{
    HKEY hKey;
    DWORD dwPosition;
    DWORD dwType = REG_DWORD;
    DWORD dwByte = 32;
    RegCreateKeyEx(hParentKey,
        lpszSubKey,
        0,
        "",
        REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        NULL,
        &hKey,
        &dwPosition);
    if (RegQueryValueEx(hKey,
        szName,
        NULL,
        &dwType,
        (BYTE *)dwValue,
        &dwByte) != ERROR_SUCCESS) {
            RegCloseKey(hKey);
            return FALSE;
        }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL GetDataString(char *szName, char *szValue)
{
    HKEY hKey;
    DWORD dwPosition;
    DWORD dwType = REG_SZ;
    DWORD dwByte = 32;

    RegCreateKeyEx(hParentKey,
        lpszSubKey,
        0,
        "",
        REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        NULL,
        &hKey,
        &dwPosition);
    if (RegQueryValueEx(hKey,
        szName,
        NULL,
        &dwType,
        (BYTE *)szValue,
        &dwByte) != ERROR_SUCCESS) {
            return FALSE;
    }
    RegCloseKey(hKey);

    return TRUE;
}
これらの関数に変更はありません。
BOOL SetInitialSettings(INIDATA inidata)
{
    SetDataDWORD("background-color", inidata.cr_bg);
    SetDataDWORD("text-color", inidata.cr_txt);
    SetDataDWORD("x", inidata.x);
    SetDataDWORD("y", inidata.y);
    SetDataDWORD("short-color", inidata.cr_short);
    SetDataDWORD("long-color", inidata.cr_long);
    SetDataDWORD("second-color", inidata.cr_second);
    SetDataDWORD("plate-color", inidata.cr_plate);
    SetDataString("alarm-set", inidata.szAlarmSet);
    SetDataString("alarm-time", inidata.szAlarmTime);
    SetDataString("timer-time", inidata.szTimerTime);
    SetDataString("stopwatch-mode", inidata.szStopWatch);
    return TRUE;
}
INIDATA構造体のデータをレジストリに書き込みますが、stopwatch-mode メンバが増えています。しかし、実際には"No"しか書き込まれません。
BOOL SetDataDWORD(char *szName, DWORD dwData)
{
    HKEY hKey;
    DWORD dwPosition;
    
    RegCreateKeyEx(hParentKey,
        lpszSubKey,
        0,
        "",
        REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        NULL,
        &hKey,
        &dwPosition);
    RegSetValueEx(hKey,
        szName,
        0,
        REG_DWORD,
        (CONST BYTE *)&dwData,
        sizeof(DWORD));
    RegCloseKey(hKey);
    return TRUE;
}

BOOL SetDataString(char *szName, char *szData)
{
    HKEY hKey;
    DWORD dwPosition;
    LONG lResult;

    RegCreateKeyEx(hParentKey,
        lpszSubKey,
        0,
        "",
        REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS,
        NULL,
        &hKey,
        &dwPosition);
    lResult = RegSetValueEx(hKey,
        szName,
        0,
        REG_SZ,
        (CONST BYTE *)szData,
        lstrlen(szData));
    RegCloseKey(hKey);

    return TRUE;
}
これらの関数に変更はありません。
BOOL ShowClock(HDC hdc, char *lpszBuf, INIDATA inidata)
{
    HBRUSH hBrush;
    HPEN hPen;
    int h, m, s, x0, y0, x, y;
    int l = CLOCK_HEIGHT / 2;

    if (strcmp(lpszBuf, "") == 0)
        return FALSE;

    hBrush = CreateSolidBrush(inidata.cr_plate);
    SelectObject(hdc, hBrush);
    Ellipse(hdc, 1, 1, CLOCK_HEIGHT - 1, CLOCK_HEIGHT - 1);
    Ellipse(hdc, 4, 4, CLOCK_HEIGHT - 4, CLOCK_HEIGHT - 4);
    DeleteObject(hBrush);

    hPen = CreatePen(PS_SOLID, 0, RGB(0, 0, 0));
    SelectObject(hdc, hPen);
    MoveToEx(hdc, l, 1, NULL);
    LineTo(hdc, l, 4);
    MoveToEx(hdc, l, 2 * l -2, NULL);
    LineTo(hdc, l, 2 * l - 5);
    MoveToEx(hdc, 2*l - 2, l, NULL);
    LineTo(hdc, 2 * l - 5, l);
    MoveToEx(hdc, 1, l, NULL);
    LineTo(hdc, 4, l);
    DeleteObject(hPen);

    GetHMS(lpszBuf, &h, &m, &s);

    x0 = y0 = CLOCK_HEIGHT / 2;

    //短針
    if (h > 11)
        h -= 12;
    hPen = CreatePen(PS_SOLID, 4, inidata.cr_short);
    SelectObject(hdc, hPen);
    MoveToEx(hdc, x0, y0, NULL);
    x = (int)(x0 + (l - 16) * sin(h * PAI / 6 + m * PAI / 360));
    y = (int)(x0 - (l - 16) * cos(h * PAI / 6 + m * PAI / 360));
    LineTo(hdc, x, y);
    DeleteObject(hPen);

    //長針
    hPen = CreatePen(PS_SOLID, 2, inidata.cr_long);
    SelectObject(hdc, hPen);
    MoveToEx(hdc, x0, y0, NULL);
    x = (int)(x0 + (l - 10) * sin(m * PAI / 30 + s * PAI / 1800));
    y = (int)(y0 - (l - 10) * cos(m * PAI / 30 + s * PAI / 1800));
    LineTo(hdc, x, y);
    DeleteObject(hPen);

    //秒針
    hPen = CreatePen(PS_SOLID, 1, inidata.cr_second);
    SelectObject(hdc, hPen);
    MoveToEx(hdc, x0, y0, NULL);
    x = (int)(x0 + (l - 5) * sin(s * PAI / 30));
    y = (int)(y0 - (l - 5) * cos(s * PAI / 30));
    LineTo(hdc, x, y);
    DeleteObject(hPen);


    return TRUE;
}
文字盤と針を描画する関数ですが、文字盤に少しだけ目盛りを付けました。 (12,3,6,9時の位置)

BOOL GetHMS(char *lpszBuf, int *lpH, int *lpM, int *lpS)
{
    char szTemp[64], *token, szSep[] = ":";

    strcpy(szTemp, lpszBuf);
    token = strtok(szTemp, szSep);

    *lpH = atoi(token);
    token = strtok(NULL, szSep);
    *lpM = atoi(token);
    token = strtok(NULL, szSep);
    *lpS = atoi(token);

    return TRUE;
}

BOOL ShowBitmap(HDC hdc, INIDATA inidata)
{
    HBITMAP hBmp, hMask;
    BITMAP bmp_info;
    HDC hdc_mem;
    int wx,wy;
    HBRUSH hBrush;

    hBrush = CreateSolidBrush(inidata.cr_bg);
    SelectObject(hdc, hBrush);

    hBmp = (HBITMAP)LoadImage(hInst,
        "CAT", 
        IMAGE_BITMAP,
        0, 0, 
        LR_DEFAULTCOLOR);
    hMask = (HBITMAP)LoadImage(hInst,
        "MASK",
        IMAGE_BITMAP,
        0, 0,
        LR_DEFAULTCOLOR);
    if (hMask == NULL) {
        MessageBox(NULL, "Error", szAppName, MB_OK);
        return FALSE;
    }
    GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
    wx = bmp_info.bmWidth;
    wy = bmp_info.bmHeight;
    hdc_mem = CreateCompatibleDC(hdc);
    SelectObject(hdc_mem, hBmp);
    MaskBlt(hdc, CLOCK_WIDTH - wx - 5, 5, wx, wy, 
        hdc_mem, 0, 0, 
        hMask, 0, 0, MAKEROP4(SRCCOPY, PATCOPY));
    
    DeleteObject(hBmp);
    DeleteObject(hMask);
    DeleteObject(hBrush);
    DeleteDC(hdc_mem);
    return TRUE;
}

BOOL CALLBACK MyAlarmProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    SYSTEMTIME st;
    static HWND hTime, hCheck;
    char szBuf[32];
    static INIDATA *lpinidata;
    int h, m, s;

    switch (msg) {
        case WM_INITDIALOG:
            hTime = GetDlgItem(hDlg, IDC_DATETIMEPICKER1);
            
            hCheck = GetDlgItem(hDlg, IDC_CHECK1);
            SendMessage(hTime, DTM_SETFORMAT, 0, (LPARAM)"HH:mm:ss");
            lpinidata = (INIDATA *)lp;
            if (strcmp(lpinidata->szAlarmSet, "No") == 0) {
                EnableWindow(hTime, FALSE);
                Button_SetCheck(hCheck, BST_UNCHECKED);
            } else {
                EnableWindow(hTime, TRUE);
                Button_SetCheck(hCheck, BST_CHECKED);
                GetHMS(lpinidata->szAlarmTime, &h, &m, &s);
                GetLocalTime(&st);
                st.wHour = h;
                st.wMinute = m;
                st.wSecond = s;
                DateTime_SetSystemtime(hTime, GDT_VALID, &st);
            }
            return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDOK:
                    if (Button_GetCheck(hCheck) == BST_CHECKED) {
                        strcpy(lpinidata->szAlarmSet, "Yes");
                        DateTime_GetSystemtime(hTime, &st);
                        wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
                        strcpy(lpinidata->szAlarmTime, szBuf);
                    } else {
                        strcpy(lpinidata->szAlarmSet, "No");
                        strcpy(lpinidata->szAlarmTime, "00:00:00");
                    }
                    EndDialog(hDlg, IDOK);
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hDlg, IDCANCEL);
                    return TRUE;
                case IDC_CHECK1:
                    if (Button_GetCheck(hCheck) == BST_CHECKED) {
                        EnableWindow(hTime, TRUE);
                    }
                    else
                        EnableWindow(hTime, FALSE);
                    return TRUE;
            }
            return FALSE;
    }
    return FALSE;
}

BOOL CALLBACK MyTimerProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    static HWND hTime;
    SYSTEMTIME st;
    char szBuf[64];
    static INIDATA *lpini;
    int h, m, s;

    switch (msg) {
        case WM_INITDIALOG:
            hTime = GetDlgItem(hDlg, IDC_DATETIMEPICKER1);
            SendMessage(hTime, DTM_SETFORMAT, 0, (LPARAM)"HH:mm:ss");
            lpini = (INIDATA *)lp;
            GetLocalTime(&st);
            GetHMS(lpini->szTimerTime, &h, &m, &s);
            st.wHour = h;
            st.wMinute = m;
            st.wSecond = s;
            DateTime_SetSystemtime(hTime, GDT_VALID, &st);
            return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDOK:
                    DateTime_GetSystemtime(hTime, &st);
                    wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
                    strcpy(lpini->szTimerTime, szBuf);
                    EndDialog(hDlg, IDOK);
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hDlg, IDCANCEL);
                    return TRUE;
            }
            return FALSE;
    }
    return FALSE;
}

BOOL MyTimerShow(HDC hdc, int x, int y, INIDATA inidata, DWORD dwStart)
{
    MMTIME mm;
    char szBuf[64];
    DWORD dwNow, dwTimerSec;
    int h, m, s;

    GetHMS(inidata.szTimerTime, &h, &m, &s);
    dwTimerSec = h * 60 * 60 + m * 60 + s;


    timeGetSystemTime(&mm, (UINT)sizeof(MMTIME));
    dwNow = mm.u.ms;
    SetHMS(szBuf, dwTimerSec - (dwNow - dwStart) / 1000);
    TextOut(hdc, x, y, szBuf, (int)strlen(szBuf));
    if (strcmp(szBuf, "00:00:00") == 0)
        return TRUE;

    return FALSE;
}

BOOL SetHMS(char *lpszBuf, DWORD dwTm)
{
    int h, m, s;

    h = dwTm / (60 * 60);
    m = (dwTm - h * 60 * 60) / 60;
    s = dwTm - h * 60 * 60 - m * 60;

    if (h > 23) {
        h = 0;
        m = 0;
        s = 0;
    }

    wsprintf(lpszBuf, "%02d:%02d:%02d", h, m, s);

    return TRUE;
}
これらの関数に変更はありません。
BOOL CALLBACK MyStopWatchProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
    static INIDATA *lpini;
    static DWORD dwStart;
    static HWND hParent, hStart, hReset, hStop, hClose;
    DWORD dwTm;

    switch (msg) {
        case WM_INITDIALOG:
            lpini = (INIDATA *)lp;
            strcpy(lpini->szStopWatch, "Yes");
            strcpy(lpini->szStopTime, "0:00:00-000");
            hParent = GetParent(hDlg);
            hStart = GetDlgItem(hDlg, IDOK);
            hReset = GetDlgItem(hDlg, IDC_RESET);
            hStop = GetDlgItem(hDlg, IDC_STOP);
            hClose = GetDlgItem(hDlg, IDCANCEL);
            EnableWindow(hStop, FALSE);
            EnableWindow(hReset, FALSE);

            return TRUE;
        case WM_TIMER:
            dwTm = timeGetTime() - dwStart;
            SetStopHMS(lpini->szStopTime, dwTm);
            InvalidateRect(hParent, NULL, TRUE);
            return TRUE;
        case WM_COMMAND:
            switch (LOWORD(wp)) {
                case IDOK:
                    SetTimer(hDlg, MYSTOP, 50, NULL);
                    dwStart = timeGetTime();
                    EnableWindow(hStop, TRUE);
                    EnableWindow(hStart, FALSE);
                    EnableWindow(hReset, FALSE);
                    EnableWindow(hClose, FALSE);
                    SetFocus(hStop);
                    return TRUE;
                case IDCANCEL:
                    strcpy(lpini->szStopWatch,"No");
                    KillTimer(hDlg, MYSTOP);
                    EndDialog(hDlg, IDCANCEL);
                    return TRUE;
                case IDC_STOP:
                    KillTimer(hDlg, MYSTOP);
                    EnableWindow(hReset, TRUE);
                    EnableWindow(hStop, FALSE);
                    EnableWindow(hClose, TRUE);
                    SetFocus(hReset);
                    return TRUE;
                case IDC_RESET:
                    strcpy(lpini->szStopTime, "0:00:00-000");
                    EnableWindow(hStart, TRUE);
                    EnableWindow(hReset, FALSE);
                    SetFocus(hStart);
                    return TRUE;
            }
            return FALSE;
    }
    return FALSE;
}
ストップウォッチ用ダイアログボックスのプロシージャです。

WM_INITDIALOGメッセージが来たら、引数のlpからinidata構造体を取得します。

また、このダイアログが出ているということは、ストップウォッチモードであることは 明らかなので、INIDATA構造体のszStopWatchメンバに"Yes"をコピーします。

また、szStopTimeメンバに"0:00:00-000"をセットしておきます。

さらに、ボタン類のウィンドウハンドルを取得してスタティックな変数に格納しておきます。

WM_TIMERメッセージが来たら、現在のシステム時刻とdwStartの差を求めて、この値から ストップウォッチの表示文字列を作っておきます。

IDOK(「スタート」ボタン)が押されたら、SetTimer関数を呼んで計測を開始します。

また、この時のシステム時刻を取得してdwStartに保存しておきます。

IDCANCEL(「キャンセル」ボタン)が押されたら、INIDATA構造体のszStopWatch メンバを"No"に設定します。

その後、タイマを殺しますが、「ストップ」ボタンを押した後に、このボタンを 押した場合はすでにタイマは死んでいるのでKillTimer関数は失敗します。

IDC_STOP(「ストップ」ボタン)が押されたら、KillTimer関数を呼んでタイマを殺します。

IDC_RESET(「リセット」ボタン)が押されたらINIDATA構造体のszStopTime メンバを"0:00:00-000"にします。

BOOL SetStopHMS(char *lpszBuf, DWORD dwTime)
{
    int h, m, s, ms;

    h = (dwTime /1000) / (60 * 60);
    m = (dwTime - h * 60 * 60 * 1000) / (60 * 1000);
    s = (dwTime - h * 60 * 60 * 1000 - m * 60 * 1000) / 1000;
    ms = dwTime - h * 60 * 60 * 1000 - m * 60 * 1000 - s * 1000;

    wsprintf(lpszBuf, "%d:%02d:%02d-%03d", h, m, s, ms);
    return TRUE;
}
ミリセコンドから"0:00:00-000"の形の文字列を作る関数です。

今回作ったストップウォッチは、一度「ストップ」ボタンを押すと 「リセット」しないと、次の計測ができません。「ストップ」ボタンを押した後、 再度「スタート」ボタンを押すと、ストップした時間から計測を再開できるように 改良してみてください。


[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 03/Feb/2003 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。