第343章 時計を作る タイマーを付ける


今回は、時計にタイマーを付けます。アラームと似ていますが、アラームは あらかじめセットした時刻にwave音が鳴ります。これに対してアラームは 今から何分後、というようにセットします。カップラーメンなどを作る時に 便利です。



右クリックで出てくるメニューに「タイマー」「タイマーセット」が加わりました。



「タイマーセット」を選択すると左のようなダイアログボックスが出てきます。

これに希望の時間をセットします。左の例では1時間5秒(半端な時間です)にセットしてあります。

セットできる最大値は23時間59分59秒です。

「スタート」ボタンを押すと・・・



今まで、日付が表示されていた部分に残り時間が、刻々と表示されます。

0秒になるとWave音が鳴ります。途中でやめたい時は、右クリックして 「タイマー設定」ダイアログを出し、「ストップ」ボタンを押します。 (今回のプログラムでは一度ストップすると、その時間から再スタートすることはできません。)



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

// clock07.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
        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

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

BELL                    WAVE                    "ringin.wav"
リソース・スクリプトです。

メニュー項目が追加になっています。

タイマーセットのダイアログボックスが追加となっています。

// clock07.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
#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];
} INIDATA;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyAlarmProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyTimerProc(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);

char szClassName[] = "clock07";    //ウィンドウクラス
char szAppName[] = "猫クロック"; //アプリケーション名
HINSTANCE hInst;
MYDATA構造体にszTimerTimeメンバが増えました。一度セットした タイマー時間は、次回タイマーセットダイアログが出てきた時に反映されます。

タイマーセットダイアログのプロシージャ、MyTimerShow関数、SetHMS関数が増えました。

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);
            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);
            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;
            }
            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_TIMERメッセージが来た時、bTimerがTRUEの時(タイマー作動中の時) MyTimerShow関数を呼んでタイマーの残り時間を表示するようにしました。

また、MyTimerShow関数がTRUEを返した時(残り時間が0となった時)PlaySound関数で Wave音を鳴らします。この場合、タイマーはもう作動しなくてよいので、bTimerをFALSE にしておきます。

PlaySound関数については第342章に解説があります。

メニューからIDM_TIMERが選択された時は、DialogBoxParam関数でタイマーセット ダイアログを出します。

そして、このダイアログがIDOK(「スタート」ボタンが押された)を返したら タイマーをスタートさせないといけないので、bTimerをTRUEにします。

そして、timeGetSystemTime関数でこの時のシステム時間を取得します。 これは、Windowsが起動してからのミリセコンドです。これをstaticな変数dwStartMsに保存しておきます。

MMRESULT timeGetSystemTime(
  LPMMTIME pmmt, 
  UINT cbmmt     
);
システム時刻(Windowsが起動してからの経過時間)をミリセコンドで取得します。

pmmtには、MMTIME構造体のアドレスを指定します。

cbmmtには、MMTIME構造体のサイズ(バイト)を指定します。

この関数を使うにはmmsystem.hをインクルードし、プロジェクトにwinmm.libを参加 させる必要があります。

MMTIME構造体は次のように定義されています。

typedef struct mmtime_tag { 
    UINT wType; 
    union { 
        DWORD ms; 
        DWORD sample; 
        DWORD cb; 
        DWORD ticks; 
        struct { 
            BYTE hour; 
            BYTE min; 
            BYTE sec; 
            BYTE frame; 
            BYTE fps; 
            BYTE dummy; 
            BYTE pad[2] 
        } smpte; 
        struct { 
            DWORD songptrpos; 
        } midi; 
    } u; 
} MMTIME;
時間フォーマットを指定します。次の中から1つを選びます。

MIDIストリーム内のtick数
意味
TIME_BYTESファイルの先頭からの現在の位置のオフセット(バイト)
TIME_MIDIMIDI時間
TIME_MSミリセコンド
TIME_SAMPLESWAVE形式オーディオサンプルの数
TIME_SMPTESMPTE規格フォーマットの時間
TIME_TICKS

msには、ミリセコンドが格納されます。(TIME_MSを指定した場合)

sampleには、先頭からのサンプル数が格納されます。(TIME_SAMPLES)

cbには、ファイルの先頭からのオフセットが格納されます。(TIME_BYTES)

ticksには、MIDIストリーム内のtick数が格納されます。(TIME_TICKS)

hourはSMPTE企画の時です。

minは分、secは秒、frameはフレーム番号、fpsは1秒あたりのフレーム数、 songptrposは、ソングポインタの位置です。

さて、システムタイムをミリセコンドで取得するだけならtimeGetTime関数 の方が簡単ですが、ここではあえてtimeGetSystemTime関数を使いました。 気にくわない人は書き換えてください。

DWORD timeGetTime(VOID);
システム時間をミリセコンドで返します。
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");
    }
    return;
}
レジストリから値を読み出しINIDATA構造体にセットします。

timer-timeエントリが増えています。読み込みに失敗した時は"00:00:00"に設定します。

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);
    return TRUE;
}
INIDATA構造体の値をレジストリに書き込みます。timer-timeエントリへの 書き込みが増えています。
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);

    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;
}

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;
}
タイマー設定ダイアログのプロシージャです。

WM_INITDIALOGメッセージが来たら、Date and Time Pickerコントロールのフォーマットを 指定します。そして、INIDATA構造体に指定してある時間にセットします。

スタートボタン(IDOK)が押されたらDate and Time Pickerコントロールに指定してある時間を取得して、szBufに書き込みます。

また、INIDATA構造体に書き込みます。

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;
}
タイマーの残り時間を表示する関数です。

GetHMS関数を利用して、inidata構造体のszTimerTimeメンバの"XX:XX:XX"形式の文字列から時、分、秒を取得します。これから、セットした時間の秒数をdwTimerSecに格納します。

timeGetSystemTime関数で現在のシステム時間を取得します。

現在のシステム時間から、スタート時のシステム時間の差を1000で割ったものを、 dwTimerSecから引くと残り時間(秒)がわかります。

秒を"XX:XX:XX"形式の文字列に変換するのが、SetHMS関数です。

変換後の文字列が"00:00:00"となった場合はTRUEを返し、そうでない場合は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;
}
時、分、秒の値から"XX:XX:XX"形式の文字列を作ります。 これは、眺めるとわかりますね。

今回は、ちょっと面倒くさいところもありましたが比較的簡単でした。


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

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