第342章 時計を作る アラームを付ける


時計といえばたいてい、アラーム機能がついています。そこで、今回は 簡単なアラーム機能を付けます。

いろいろな方法があると思いますが、今回はWave音をリソースにして 指定された時刻になると、このWave音をユーザーが止めるまで繰り返し 再生するようにします。



Wave音を再生するにはPlaySound関数を使います。

前準備としては

1.Mmsystem.hをインクルードする
2.WinMM.Libをプロジェクトに加える
の2点が必要になります。
BOOL PlaySound(
  LPCSTR pszSound,
  HMODULE hmod,
  DWORD fdwSound
);
pszSoundには、再生する音を文字列で指定します。
指定する文字列の意味はfdwSoundにより異なります。

hmodには、リソースを持っている実行ファイルのインスタンスハンドルを指定します。 インスタンスハンドルを指定するのはfdwSoundがSND_RESOURCEの時のみで、 それ以外の時はNULLを指定します。

fdwSoundには、再生フラグを指定します。次の組み合わせで指定します。

意味
SND_ASYNC非同期再生。再生が始まるとすぐに制御を返します。
SND_SYNC同期再生。再生が終了するまで制御を返しません。
SND_RESOURCEpszSoundはリソース識別子を表します。
SND_FILENAMEpszSoundはファイル名です。
SND_LOOP繰り返し再生します。止めるにはpszSoundにNULLを指定してPlaySound関数を呼びます。
SND_APPLICATIONアプリケーション特有の関連づけで再生します。
SND_ALIASpszSoundはシステムイベントのエイリアスです。
SND_ALIAS_IDpszSoundは定義済みサウンド識別子("SystemStart"など)です。
SND_MEMORYサウンドイベントのファイルはメモリにロードされています。 pszSoundはメモリ内のサウンドへのポインタです。
SND_NODEFAULT指定のサウンドが見つからない時、警告音などを出さずに終了します。
SND_NOSTOPすでに他のサウンドが再生されている時、指定のサウンドを再生しません。
SND_NOWAITドライバがビジーの時はすぐに制御を返します。
SND_PURGEサウンドの再生を停止します。pszSound がNULLの時すべてのサウンド を停止します。

SND_ASYNCとSND_LOOPを指定した場合、いつまでも再生をやめないので、第1引数をNULL にして再度PlaySound関数を呼び出さなくてはなりません。

SND_RESOURCEで指定したサウンドを停止させる場合は、hmodにインスタンスハンドル、 fdwSoundにSND_PURGEを指定しなければなりません。

次にWave音をリソースにする方法ですが、カスタムリソースを使います。 リソースの種類は必ず"WAVE"にします。

VC++6.0の時はインポートでうまくいくのですが.NETの時はWAVEをインポートしてもうまくいきません。(やり方が悪いのか??)

筆者は次のようにしています。

1.メニューの「編集」「リソースの追加」で「カスタムを選択」
2.新規カスタムリソースダイアログに「WAVE」と記入して「OK」ボタンを押します。
3.リソースビューで今作った空のリソースを選択します。
4.そのプロパティウィンドウの「Filename」にリソースにしたいWave音のファイル名を指定します。
    「xxxを読み込みますか?」と聞かれるので「はい」と答えます。
5.IDをダブルクォーテーション付きの文字列にしておくと便利です。("BELL"など)
右クリックメニューに「アラーム設定」が増えました。



アラーム設定ダイアログです。

中央の時刻設定のコントロールはDate and Time Pickerコントロールです。

このコモンコントロールについてはすでに第178章で解説していますので参照してください。

「アラームをセットする」のチェックがはずれていると、時刻の入力はできません。 また、アラームも鳴りません。



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

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

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

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

メニューアイテムが増えました。

アラーム設定のためのダイアログリソースが増えました。

カスタムリソースの"WAVE"が追加になりました。
なお、カスタムリソースについては第317章も参照してみてください。

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

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

char szClassName[] = "clock06";    //ウィンドウクラス
char szAppName[] = "猫クロック"; //アプリケーション名
HINSTANCE hInst;
windowsx.hをインクルードしてコントロール類のマクロを使えるようにしています。

コモンコントロールを使うのでcommctrl.hをインクルードしています。プロジェクトに ComCtl32.Libを参加させるのを忘れないでください。

PlaySound関数を使うのでMmsystem.hをインクルードしています。プロジェクトに WinMM.Libを参加させるのを忘れないでください。

MYDATA構造体のメンバが増えています。

レジストリに文字列を読み書きする関数が追加になっています。

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;
}
ウィンドウを作るのにCreateWindowEx関数を使い、拡張ウィンドウスタイルに WS_EX_TOPMOSTを指定しています。これで、このウィンドウはいつも最前面に来ます。

オプションで「最前面に来なくてもよい」設定を追加してみてください。

//ウィンドウプロシージャ

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;

    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;
            TextOut(hdc, x, 4, szBuf2, (int)strlen(szBuf2));
            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;
            }
            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_CREATEメッセージが来たら、コモンコントロールの初期化をしておきます。

WM_TIMERメッセージが来てinidata構造体のszAlarmSetメンバが"Yes"でなおかつ szBufの文字列が、アラームに設定した時刻と一致する時は、 PlaySound関数で、リソースのWave音を鳴らし続けます。

この関数の第3引数にSND_ASYNCが含まれているので、再生が始まるとすぐに制御が 返され、「時間ですよ」のメッセージボックスが表示されます。

このメッセージボックスの「OK」ボタンを押すと PlaySound(NULL, hInst, SND_PURGE);

が実行されアラーム音が止まります。

メニューからIDM_ALARMSETが選択されたら、アラーム設定ダイアログを出します。 ダイアログを出すのにDialogBoxParam関数を使っている点に注意してください。 最後の引数にINIDATA構造体へのポインタをしています。これで、このダイアログの プロシージャにグローバル変数を使うことなくデータを渡すことができます。 この関数については第195章を参照してみてください。

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");
    }
    return;
}
レジストリから設定を読みとり、読みとりに失敗した時はデフォルト値を設定します。

アラーム設定をするかどうか、また、アラームを鳴らす時刻の項目が増えています。

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;
}
レジストリから文字列を取得する関数です。

アラームを設定するかどうか("Yes","No")を調べたり、アラーム時刻の 読みとりのために必要です。

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);
    return TRUE;
}
レジストリに書き込む関数です。

alarm-setとalarm-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;
}
アラームの設定ダイアログのプロシージャです。

このダイアログはDialogBoxParamで呼び出されているので WM_INITDIALOGメッセージが来た時のlParam値でデータを受け取ることができます。

受け取ったデータを元にチェックボックスなどのコントロール類の初期化をしています。

アラームを鳴らす設定になっている時はDate and Time Pickerコントロールに 設定時刻を表示します。この時DateTime_GetSystemtimeマクロの引数のSYSTEMTIME構造体の 日付部分にもきちんとした値が設定されていないとうまく働きません。

自分で適当な日付を設定してもよいのですが、ここでは一度GetLocalTime関数を呼んで 全部のメンバを埋めています。その後で設定時刻部分を改めて書き直しています。

今回はアラームは1つの時刻しか設定できませんが、改良して複数の時刻を設定できるようにしてみてください。

また、アラーム音をユーザーが自由に選択できる(ファイルを指定する)ようにしてみてください。


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

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