|  | 
 
 
 楼主|
发表于 2015-3-11 16:06:33
|
显示全部楼层 
| #include<Windows.h>
 #include<math.h>
 #include"resource.h"
 
 #define ID_TIME 1
 #define ID_TOP  100
 #define ID_HELP 101
 
 LRESULT CALLBACK MainWndProc(HWND vHwnd, UINT vMsg, WPARAM wParam, LPARAM lParam);
 void drawClockFace(HDC vHdc);//  绘制时钟的外观
 void setCoordinate(HDC vHdc, int vX, int vY);//设置笛卡尔坐标系
 void setFont(HDC vHdc, HFONT *vFont, int vFontBig);//设置字体大小
 void drawHand(HDC vHdc, int vLength, int vWidth, int vDegrees, COLORREF vColor);
 
 int APIENTRY WinMain(HINSTANCE hInstance,
 HINSTANCE hPreInstance,
 LPSTR lpCmdLine,
 int nCmdShow)
 {
 char ClassName[] = "MainWinClass";
 WNDCLASSEX wndClass;
 // 用描述主窗口的参数填充 WNDCLASSEX 结构
 wndClass.cbSize = sizeof(wndClass);// 结构的大小
 wndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;// 指定如果大小改变就重画
 //CS_OWNDC让Windows 将每次对DC的设置都保存下来
 wndClass.cbClsExtra = 0;// 没有额外的类内存
 wndClass.cbWndExtra = 0;// 没有额外的窗口内存
 wndClass.lpfnWndProc = MainWndProc;// 窗口函数指针
 wndClass.hInstance = hInstance;// 实例句柄
 wndClass.hIcon = ::LoadIcon(hInstance, (LPSTR)ID_CLOCKICON1);// 使用预定义图标
 wndClass.hCursor = ::LoadCursor(NULL, IDC_ARROW);//使用预定义的光标
 wndClass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);// 使用白色背景画刷
 wndClass.lpszMenuName = NULL;
 wndClass.lpszClassName = ClassName;// 窗口类的名称
 wndClass.hIconSm = NULL;//没有类的小图标
 
 // 注册这个窗口类
 ::RegisterClassEx(&wndClass);
 
 //创建主窗口
 HWND hwnd = ::CreateWindowEx(
 0,                                        // dwExStyle,扩展样式
 ClassName,                        // lpClassName,类名
 "时钟", //lpWindowName,标题
 WS_OVERLAPPEDWINDOW,//dwStyle,窗口风格
 CW_USEDEFAULT,                // X,初始 X 坐标
 CW_USEDEFAULT,                //Y,初始 Y 坐标
 500,                // nWidth,宽度
 500,                //nHeight,高度
 NULL,                                // hWndParent,父窗口句柄
 NULL,                                //hMenu,菜单句柄
 hInstance,                        //hlnstance,程序实例句柄
 NULL                                // lpParam,用户数据
 );
 if (hwnd == NULL)
 {
 ::MessageBox(NULL, "创建窗口出错! ", "error", MB_OK);
 return -1;
 }
 
 
 // 显示窗口,刷新窗口客户区
 ::ShowWindow(hwnd, SW_SHOWNORMAL);
 ::UpdateWindow(hwnd);
 
 
 // 从消息队列中取出消息,交给窗口函数处理,直到 GetMessage 返回 FALSE,结束消息循环
 MSG msg;
 
 while(::GetMessage(&msg, NULL, 0, 0))
 {
 // 转化键盘消息
 ::TranslateMessage(&msg);
 // 将消息发送到相应的窗口函数
 ::DispatchMessage(&msg);
 }
 // 当 GetMessage 返回 FALSE 时程序结束
 
 return msg.wParam;
 }
 
 //******************************************************************
 //FUNCTION:
 LRESULT CALLBACK MainWndProc(HWND vHwnd, UINT vMsg, WPARAM wParam, LPARAM lParam)
 {
 HDC hdc;
 PAINTSTRUCT ps;
 HFONT hFont;//字体句柄
 
 //  上一次 Windows通知时的时间
 static int PreHour;
 static int PreMinute;
 static int PreSecond;
 //  窗口客户区的大小
 static int xClient;
 static int yClient;
 //  是否位于最顶层
 static BOOL FlagTop;
 
 switch (vMsg)
 {
 case WM_CREATE:
 //在窗口刚被创建,即接受到 WM_CREATE消息的时候,要初始化全局变量的值,并安装定时器。
 {
 HMENU hmenu;
 hmenu = ::LoadMenu((HINSTANCE)::GetWindowLongPtr(vHwnd, GWL_HINSTANCE), MAKEINTRESOURCE(ID_CLOCK));
 SetMenu(vHwnd, hmenu);
 
 //设置字体
 hdc = ::BeginPaint(vHwnd, &ps);
 setFont(hdc, &hFont, 30);
 
 // 向系统菜单中添加自定义的项
 HMENU sysMenu;
 sysMenu = ::GetSystemMenu(vHwnd, false);
 ::AppendMenu(sysMenu, MF_SEPARATOR, 0, NULL);
 ::AppendMenu(sysMenu, MF_STRING, ID_TOP, "总在最前");
 ::AppendMenu(sysMenu, MF_STRING, ID_HELP, "帮助");
 
 //  设置时间
 SYSTEMTIME time;
 ::GetLocalTime(&time);
 PreHour   = time.wHour;
 PreMinute = time.wMinute;
 PreSecond = time.wSecond;
 
 //  创建定时器
 ::SetTimer(vHwnd, ID_TIME, 1000, NULL);
 
 break;
 }
 
 case WM_SIZE:
 {
 xClient = LOWORD(lParam);
 yClient = HIWORD(lParam);
 break;
 }
 
 case WM_PAINT:
 {
 // 使无效的客户区变的有效,并取得设备环境句柄
 hdc = ::BeginPaint(vHwnd, &ps);
 
 //::GetClientRect(vHwnd, &Rect);
 setCoordinate(hdc, xClient, yClient);//  设置坐标系
 drawClockFace(hdc);//  绘制时钟外观
 
 //  绘制指针
 //  经过 1 个小时时针走30 度(360/12 ),经过 1 分钟时针走0.5度(30/60)
 drawHand(hdc, 200, 8, (PreHour*30 + PreMinute/2), RGB(0,0,0));
 //  经过 1 分钟分针走6 度(360/60 )
 drawHand(hdc, 400, 6, PreMinute*6, RGB(10,0,0));
 //  经过 1 秒钟秒针走6 度(360/60 )
 drawHand(hdc, 400, 4, PreSecond*6, RGB(10,10,0));
 
 ::EndPaint(vHwnd, &ps);
 break;
 }
 
 case WM_COMMAND://菜单响应
 {
 switch (LOWORD(wParam))
 {
 case ID_INFO:
 {
 ::SendMessage(vHwnd, WM_CLOSE, 0, 0);
 break;
 }
 case ID_HELLO:
 {
 int MsgBox = ::MessageBox(vHwnd, "hello world", "问好", MB_YESNO | MB_DEFBUTTON2);
 break;
 }
 default:
 break;
 }
 break;
 }
 
 case WM_TIMER:
 {
 if (::IsIconic(vHwnd))// IsIconic 函数用来判断窗口是否处于最小化状态
 break;
 // 取得系统时间
 SYSTEMTIME time;
 ::GetLocalTime(&time);
 
 // 建立坐标系
 hdc = ::GetDC(vHwnd);
 // 以COLOR_3DFACE为背景色就可以擦除指针了(因为窗口的背景色也是COLOR_3DFACE)
 COLORREF sysColor = ::GetSysColor(COLOR_3DFACE);
 
 // 如果分钟改变的话就擦除时针和分针
 if (PreMinute != time.wMinute)
 {
 drawHand(hdc, 200, 8, (PreHour*30 + PreMinute/2), sysColor);
 drawHand(hdc, 400, 6, PreMinute*6, sysColor);
 PreHour = time.wHour;
 PreMinute = time.wMinute;
 }
 // 如果秒改变的话就擦除秒针,然后重画所有指针
 if (PreSecond != time.wSecond)
 {
 //擦除秒针
 drawHand(hdc, 400, 4, PreSecond*6, sysColor);
 //重画所有指针
 //  经过 1 个小时时针走30 度(360/12 ),经过 1 分钟时针走0.5度(30/60)
 drawHand(hdc, 200, 8, (time.wHour*30 + time.wMinute/2), RGB(0,0,0));
 //  经过 1 分钟分针走6 度(360/60 )
 drawHand(hdc, 400, 6, time.wMinute*6, RGB(10,0,0));
 //  经过 1 秒钟秒针走6 度(360/60 )
 drawHand(hdc, 400, 4, time.wSecond*6, RGB(10,10,0));
 PreSecond = time.wSecond;
 }
 break;
 }
 
 case WM_NCHITTEST:
 {
 UINT HitTest;
 HitTest = ::DefWindowProc(vHwnd, WM_NCHITTEST, wParam, lParam);
 
 ////  如果鼠标左键按下,GetAsyncKeyState函数的返回值小于0
 if (HitTest == HTCLIENT && ::GetAsyncKeyState(MK_LBUTTON) < 0)
 {
 HitTest = HTCAPTION;
 }
 return HitTest;
 }
 
 case WM_CONTEXTMENU:
 {
 POINT pt;
 pt.x = LOWORD(lParam);
 pt.y = HIWORD(lParam);
 
 //  取得系统菜单的句柄
 HMENU SysMenu = ::GetSystemMenu(vHwnd, FALSE);//允许应用程序为复制或修改而访问窗口菜单
 //通过api函数getsystemmenu来得到它的句柄,然后通过菜单相关的api函数就能改变它了。
 
 //  弹出系统菜单
 int MenuID;
 MenuID = ::TrackPopupMenu(
 SysMenu,                                                        // 菜单句柄
 TPM_LEFTBUTTON,                // 此参数指定了一些和弹出的菜单的位置相关的选项
 pt.x,                                                                //(x, y)是弹出的菜单的基于屏幕的坐标
 pt.y,
 0,                                                                        //保留值,必须为零
 vHwnd,                                                                // 此菜单属于哪一个窗口所有(也就是WM_COMMAND消息发到这个窗口)
 NULL);
 
 if (MenuID > 0)
 {
 ::SendMessage(vHwnd, WM_SYSCOMMAND, MenuID, 0);
 }
 
 return 0;
 }
 
 case WM_SYSCOMMAND:
 {
 int ID = wParam;
 
 if (ID == ID_HELP)
 {
 ::MessageBox(vHwnd, "时钟显示", "时钟", 0);
 }
 else if (ID == ID_TOP)
 {
 HMENU sysMenu = ::GetSystemMenu(vHwnd, false);
 if (FlagTop)
 {
 // 设置 ID号为 IDM_TOPMOST 的菜单项为未选中状态
 ::CheckMenuItem(sysMenu, ID_TOP, MF_UNCHECKED);
 // 将窗口提到所有窗口的最顶层
 ::SetWindowPos(vHwnd,HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
 FlagTop = false;
 }
 else
 {
 ::CheckMenuItem(sysMenu, ID_TOP, MF_CHECKED);
 ::SetWindowPos(vHwnd,HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE);
 FlagTop = true;
 }
 }
 
 return ::DefWindowProc(vHwnd, WM_SYSCOMMAND, ID, 0);
 }
 
 
 case WM_CLOSE:
 {
 ::KillTimer(vHwnd, ID_TIME);
 int MsgBox = ::MessageBox(vHwnd, "确定要关闭窗口吗?", "询问", MB_YESNO | MB_DEFBUTTON2);
 if(MsgBox == IDYES)
 DestroyWindow(vHwnd);
 break;
 }
 case WM_DESTROY:
 {
 // 向消息队列投递一个 WM_QUIT 消息,促使 GetMessage 函数返回 0,结束消息循环
 ::PostQuitMessage(0);
 break;
 }
 default:
 return ::DefWindowProc(vHwnd, vMsg, wParam, lParam);
 }
 
 return 0;
 }
 
 //******************************************************************
 //FUNCTION:
 void drawClockFace(HDC vHdc)
 {
 const int SQUAREsize = 20;
 char ClockNum[10];
 
 static POINT ClockPt[12] =
 {
 225, 390,    // 1 点
 390, 225,    // 2 点
 450, 0,    // 3 点
 390, -225,    //... 下面的坐标是上面的点的对称点(关于 X 轴、Y 轴或原点对称)
 225, -390,
 0, -450,
 -225, -390,
 -390, -225,
 -450, 0,
 -390, 225,
 -225, 390,
 0, 450    // 12 点
 };
 // 选择一个黑色的画刷
 ::SelectObject(vHdc, ::GetStockObject(BLACK_BRUSH));
 // 画12 个圆
 for (int i =0; i<12; i++)
 {
 ::Ellipse(vHdc, ClockPt[i].x - SQUAREsize, ClockPt[i].y + SQUAREsize,
 ClockPt[i].x + SQUAREsize, ClockPt[i].y - SQUAREsize);
 wsprintf(ClockNum, "%d", i+1);
 ::TextOut(vHdc, ClockPt[i].x - 10, ClockPt[i].y - 20, ClockNum, strlen(ClockNum));
 }
 }
 
 //******************************************************************
 //FUNCTION:
 void setCoordinate(HDC vHdc, int vX, int vY)
 {
 ::SetMapMode(vHdc, MM_ISOTROPIC);
 ::SetWindowExtEx(vHdc, 1000, 1000, NULL);
 ::SetViewportExtEx(vHdc, vX, -vY, NULL);
 ::SetViewportOrgEx(vHdc, vX/2, vY/2, NULL);
 }
 
 //******************************************************************
 //FUNCTION:
 void setFont(HDC vHdc, HFONT *vFont, int vFontBig)
 {
 *vFont = ::CreateFont(
 35, 0,//高度20, 宽取0表示由系统选择最佳值
 0, 0,//文本倾斜,与字体倾斜都为0
 FW_HEAVY,//粗体
 0, 0, 0,//非斜体,无下划线,无中划线
 GB2312_CHARSET,    //字符集
 OUT_DEFAULT_PRECIS,
 CLIP_DEFAULT_PRECIS,
 DEFAULT_QUALITY,        //一系列的默认值
 DEFAULT_PITCH | FF_DONTCARE,
 "自定义字体"    //字体名称
 );//自定义字体
 
 SelectObject(vHdc, *vFont);
 DeleteObject(*vFont);
 }
 
 //******************************************************************
 //FUNCTION:
 void drawHand(HDC vHdc, int vLength, int vWidth, int vDegrees, COLORREF vColor)
 {
 double Radian = (double)vDegrees * ( (2*3.1415926)/360 );
 // 计算坐标
 POINT pt[2];
 
 pt[0].x = (int)(vLength * sin(Radian));
 pt[0].y = (int)(vLength * cos(Radian));
 pt[1].x = -pt[0].x/5;
 pt[1].y = -pt[0].y/5;
 
 // 创建画笔,并选如DC结构中
 HPEN hPen = ::CreatePen(PS_SOLID, vWidth, vColor);
 HPEN hOldPen = (HPEN)::SelectObject(vHdc, hPen);
 // 画线
 ::MoveToEx(vHdc, pt[0].x, pt[0].y, NULL);
 ::LineTo(vHdc, pt[1].x, pt[1].y);
 
 ::SelectObject(vHdc, hOldPen);
 ::DeleteObject(hPen);
 }
 
 | 
 |