/*---------------------------------------
WAKEUP.C -- Alarm Clock Program
(c) Charles Petzold, 1998
---------------------------------------*/
#include <windows.h>
#include <commctrl.h> //包含通用控件頭文件
// ID values for 3 child windows三個子窗口ID
#define ID_TIMEPICK 0 //時間控件ID
#define ID_CHECKBOX 1 //核取方塊ID
#define ID_PUSHBTN 2 //關閉響鈴按鈕ID
// Timer ID計時器ID
#define ID_TIMER 1
//數量
#define FTTICKSPERHOUR (60 * 60 * (LONGLONG) 10000000)
// Defines and structure for waveform "file" 定義音頻流結構文件
#define SAMPRATE 11025 //取樣頻率
#define NUMSAMPS (3 * SAMPRATE) //取樣的數量
#define HALFSAMPS (NUMSAMPS / 2) //取樣數量的一半
typedef struct
{
char chRiff[4] ;
DWORD dwRiffSize ;
char chWave[4] ;
char chFmt [4] ;
DWORD dwFmtSize ;
PCMWAVEFORMAT pwf ;
char chData[4] ;
DWORD dwDataSize ;
BYTE byData[0] ;
}
WAVEFORM ; //定義一個音頻流結構體
// The window proc and the subclass proc主窗口過程與子窗口類過程函數
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //主窗口過程函數
LRESULT CALLBACK SubProc (HWND, UINT, WPARAM, LPARAM) ; //子窗口類過程函數
// Original window procedure addresses for the subclassed windows儲存原子窗口過程函數數組
WNDPROC SubbedProc [3] ;
// The current child window with the input focus 具有輸入焦點的當前子窗口
HWND hwndFocus ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst, //主函數
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName [] = TEXT ("WakeUp") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = 0 ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (1 + COLOR_BTNFACE) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, szAppName,
WS_OVERLAPPED | WS_CAPTION |
WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndDTP, hwndCheck, hwndPush ; //定義時間、核取與響鈴關閉按鈕控件句柄
static WAVEFORM waveform = { "RIFF", NUMSAMPS + 0x24, "WAVE", "fmt ",
sizeof (PCMWAVEFORMAT), 1, 1, SAMPRATE,
SAMPRATE, 1, 8, "data", NUMSAMPS } ; //初始化音頻流結構體
static WAVEFORM * pwaveform ; //定義音頻流結構體指針
FILETIME ft ; //定義一個文件時間變量
HINSTANCE hInstance ; //定義一個執行實例句柄
INITCOMMONCONTROLSEX icex ; //定義一個初始化通用控件結構體句柄
int i, cxChar, cyChar ;
LARGE_INTEGER li ; //定義一個用於儲存時間的大整數結構體變量
SYSTEMTIME st ; //定義一個系統時間結構體變量
switch (message)
{
case WM_CREATE: //處理窗口創建消息
// Some initialization stuff初始化某些數據
hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ; //取得窗口執行實例句柄
icex.dwSize = sizeof (icex) ;
icex.dwICC = ICC_DATE_CLASSES ;
InitCommonControlsEx (&icex) ;
// Create the waveform file with alternating square waves用方波數據交替創建波形文件
pwaveform = malloc (sizeof (WAVEFORM) + NUMSAMPS) ; //爲音頻流結構分配內存
* pwaveform = waveform ; //填入音頻流結構體數據
for (i = 0 ; i < HALFSAMPS ; i++) //用方波數據給byData賦值
if (i % 600 < 300)
if (i % 16 < 8)
pwaveform->byData[i] = 25 ;
else
pwaveform->byData[i] = 230 ;
else
if (i % 8 < 4)
pwaveform->byData[i] = 25 ;
else
pwaveform->byData[i] = 230 ;
// Get character size and set a fixed window size.取得字符尺寸和設置主窗口尺寸
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;
SetWindowPos (hwnd, NULL, 0, 0, //設置主窗口的位置與尺寸
42 * cxChar,
10 * cyChar / 3 + 2 * GetSystemMetrics (SM_CYBORDER) +
GetSystemMetrics (SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE) ;
// Create the three child windows創建三個子窗口控件
hwndDTP = CreateWindow (DATETIMEPICK_CLASS, TEXT (""), //創建時間窗口控件
WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_TIMEFORMAT,
2 * cxChar, cyChar, 12 * cxChar, 4 * cyChar / 3,
hwnd, (HMENU) ID_TIMEPICK, hInstance, NULL) ;
hwndCheck = CreateWindow (TEXT ("Button"), TEXT ("Set Alarm"), //創建鬧鐘核取控件
WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
16 * cxChar, cyChar, 12 * cxChar, 4 * cyChar / 3,
hwnd, (HMENU) ID_CHECKBOX, hInstance, NULL) ;
hwndPush = CreateWindow (TEXT ("Button"), TEXT ("Turn Off"), //創建響鈴關閉按鈕控件
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_DISABLED,
28 * cxChar, cyChar, 12 * cxChar, 4 * cyChar / 3,
hwnd, (HMENU) ID_PUSHBTN, hInstance, NULL) ;
hwndFocus = hwndDTP ; //設置時間控件具有輸入焦點
// Subclass the three child windows子窗口類化三個子窗口控件
SubbedProc [ID_TIMEPICK] = (WNDPROC)
SetWindowLong (hwndDTP, GWL_WNDPROC, (LONG) SubProc) ;
SubbedProc [ID_CHECKBOX] = (WNDPROC)
SetWindowLong (hwndCheck, GWL_WNDPROC, (LONG) SubProc);
SubbedProc [ID_PUSHBTN] = (WNDPROC)
SetWindowLong (hwndPush, GWL_WNDPROC, (LONG) SubProc) ;
// Set the date and time picker control to the current time將時間控件設爲當前時間加上9小時,
// plus 9 hours, rounded down to next lowest hour 並調整爲最接近的小時數
GetLocalTime (&st) ; //獲取用戶當前時間
SystemTimeToFileTime (&st, &ft) ; //將時間轉換爲文件時間
li = * (LARGE_INTEGER *) &ft ; //將時間存入一個大整數結構體
li.QuadPart += 9 * FTTICKSPERHOUR ; //加上9個小時包含的100納秒的數量
ft = * (FILETIME *) &li ; //將大整數結構體轉換爲文件時間
FileTimeToSystemTime (&ft, &st) ; //將文件時間轉換爲系統時間
st.wMinute = st.wSecond = st.wMilliseconds = 0 ;
SendMessage (hwndDTP, DTM_SETSYSTEMTIME, 0, (LPARAM) &st) ; //設置時間控件的時間
return 0 ;
case WM_SETFOCUS: //處理設置輸入焦點消息
SetFocus (hwndFocus) ; //設置控件具有輸入焦點
return 0 ;
case WM_COMMAND: //處理控件命令
switch (LOWORD (wParam)) // control ID
{
case ID_CHECKBOX: //處理鬧鐘核取控件
// When the user checks the "Set Alarm" button, get the當按下時,取得控件時間並減去當前
// time in the date and time control and subtract from 系統時間
// it the current PC time.
if (SendMessage (hwndCheck, BM_GETCHECK, 0, 0)) //測試控件是否選取
{
SendMessage (hwndDTP, DTM_GETSYSTEMTIME, 0, (LPARAM) &st) ;//獲取控件時間
SystemTimeToFileTime (&st, &ft) ;
li = * (LARGE_INTEGER *) &ft ;
GetLocalTime (&st) ; //獲取用戶當前時間
SystemTimeToFileTime (&st, &ft) ;
li.QuadPart -= ((LARGE_INTEGER *) &ft)->QuadPart ; //減去當前時間
// Make sure the time is between 0 and 24 hours!確定時間位於24小時內
// These little adjustments let us completely ignore這些小調整讓我們完全忽略系統時間
// the date part of the SYSTEMTIME structures.結構體的日期部分
while (li.QuadPart < 0)
li.QuadPart += 24 * FTTICKSPERHOUR ;
li.QuadPart %= 24 * FTTICKSPERHOUR ;
// Set a one-shot timer! (See you in the morning.)設置一個一次性的計時器(早上好)
SetTimer (hwnd, ID_TIMER, (int) (li.QuadPart / 10000), 0) ; //設置計時器
}
// If button is being unchecked, kill the timer.如果沒選中核取方塊,刪除計時器
else
KillTimer (hwnd, ID_TIMER) ;
return 0 ;
// The "Turn Off" button turns off the ringing alarm, and also關閉按鈕關閉響鈴,同時也使
// unchecks the "Set Alarm" button and disables itself.鬧鐘核取方塊不選中
case ID_PUSHBTN: //處理關閉按鈕
PlaySound (NULL, NULL, 0) ; //播放關閉警告聲音
SendMessage (hwndCheck, BM_SETCHECK, 0, 0) ; //不選中核取方塊
EnableWindow (hwndDTP, TRUE) ; //允許時間控件
EnableWindow (hwndCheck, TRUE) ; //允許核取方塊
EnableWindow (hwndPush, FALSE) ; //不允許關閉按鈕
SetFocus (hwndDTP) ; //設置時間控件具有輸入焦點
return 0 ;
}
return 0 ;
// The WM_NOTIFY message comes from the date and time picker.這個WM_NOTIFY消息來自
// If the user has checked "Set Alarm" and then gone back to 時間控件,如果用戶選取核取方塊
// change the alarm time, there might be a discrepancy between然後又改變響鈴時間,那顯示時
// the displayed time and the one-shot timer. So the program間與計時器之間可能是不同的,因此
// unchecks "Set Alarm" and kills any outstanding timer.程序不選中核取方塊且刪除所有不一樣
case WM_NOTIFY:
switch (wParam) // control ID
{
case ID_TIMEPICK:
switch (((NMHDR *) lParam)->code) // notification code提醒代碼
{
case DTN_DATETIMECHANGE: //時間改變消息
if (SendMessage (hwndCheck, BM_GETCHECK, 0, 0)) //測試核取方塊是否選中
{
KillTimer (hwnd, ID_TIMER) ; //如果選中則刪除計時器
SendMessage (hwndCheck, BM_SETCHECK, 0, 0) ; //不選中核取方塊
}
return 0 ;
}
}
return 0 ;
// The WM_COMMAND message comes from the two buttons.
case WM_TIMER:
// When the timer message comes, kill the timer (because we only當響鈴時間到來時,刪除計時
// want a one-shot) and start the annoying alarm noise going.器(因爲只需一次)並開始響鈴
KillTimer (hwnd, ID_TIMER) ; //刪除計時器
PlaySound ((PTSTR) pwaveform, NULL, //用異步方式循環播放響鈴
SND_MEMORY | SND_LOOP | SND_ASYNC);
// Let the sleepy user turn off the timer by slapping the 讓用戶自己用按鈕關閉響鈴,如果窗口最
// space bar. If the window is minimized, it's restored; then it's小化,自動恢復;然後窗口置前;
// brought to the forefront; then the pushbutton is enabled and並讓按鈕取得輸入焦點
// given the input focus.
EnableWindow (hwndDTP, FALSE) ; //不允許時間控件
EnableWindow (hwndCheck, FALSE) ; //不允許核取方塊
EnableWindow (hwndPush, TRUE) ; //允許按鈕
hwndFocus = hwndPush ; //設置按鈕具有輸入焦點
ShowWindow (hwnd, SW_RESTORE) ; //恢復主窗口
SetForegroundWindow (hwnd) ; //將主窗口設置到前臺,並且激活該窗口
return 0 ;
// Clean up if the alarm is ringing or the timer is still set.清除響鈴或者計時器如果存在的話
case WM_DESTROY: //處理註銷窗口消息
free (pwaveform) ; //釋放內存
if (IsWindowEnabled (hwndPush)) //測試按鈕控件
PlaySound (NULL, NULL, 0) ;
if (SendMessage (hwndCheck, BM_GETCHECK, 0, 0)) //測試核取方塊
KillTimer (hwnd, ID_TIMER) ;
PostQuitMessage (0) ; //發送退出消息
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ; //返回默認窗口過程
}
LRESULT CALLBACK SubProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) //子窗口控件回調函數
{
int idNext, id = GetWindowLong (hwnd, GWL_ID) ; //獲取窗口控件ID
switch (message) //處理窗口控件消息
{
case WM_CHAR: //處理字符消息
if (wParam == '\t') //處理按下Tab鍵消息
{
idNext = id ;
idNext = (idNext +
(GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3 ; //在三個窗口控件間切換
while (!IsWindowEnabled (GetDlgItem (GetParent (hwnd), idNext))); //當窗口控件允許時使idNext等於窗口控件ID
SetFocus (GetDlgItem (GetParent (hwnd), idNext)) ; //使窗口控件具有輸入焦點
return 0 ;
}
break ;
case WM_SETFOCUS: //設置輸入焦點消息
hwndFocus = hwnd ; //使輸入焦點句柄等於窗口控件句柄
break ;
}
return CallWindowProc (SubbedProc [id], hwnd, message, wParam, lParam) ; //返回默認子窗口控件回調函數
}