最近,因为自制力有点差,看上节目容易停不下来,所以经常睡得很晚。靠意志力,显然没有办法解决早睡的问题了。所以想着写一个强制关机的软件,让关机变得措手不及。当一切从屏幕消失的时候,自然就没有兴趣去重新开电脑了。从而有效解决了晚睡的问题。
实现的目标是:极简、功能完整、逼格高、强制性
实现的思路是:
1.极简
去掉大面积的显示,仅仅留一个地方显示剩余关机时间就行了。为什么想要极简?从事多年开发后,见过纷繁复杂的软件,慢慢喜欢上了简单就好的感觉。但是简单不等于蹩脚。简单不仅是界面上的简单,同时也是开发上的简单。为了快速实现小面积显示,直接去掉客户区即可。我们将显示的内容放在了标题栏。
2.功能完整
功能的流程:启动软件开始计时、极简的显示效果、可以显示倒计时、可以关闭(强制要求可能需要屏蔽关闭)、可以最小化、默认固定关机时间、可以通过隐藏手段修改关机时间。有了这些功能,对于定时关机软件来讲,已经是完整的了。
3.逼格高
逼格,通常是大气的,简约的,精练的。所有强劲的功能都在简单的界面里可以体现。比如倒计时、屏蔽关闭、标题栏小区域显示、背景半透明。
4.强制性
软件启动,即规定了默认时间,我们可以定为我们觉得合适的时间,比如22:30。无需设置,即可使用。使用之后,无法退出。当然不排除使用非常规手段来退出,如果有这么强烈愿望的用户,也不要来用这个软件了。如果是紧急情况,也可以采用非常规方式退出哈。
下面是效果图:
下面来讲解整个的实现过程:
1.首先我们使用VS2017创建一个win32程序项目
创建完项目后,项目就可以运行了。我们可以去掉菜单,只要把WM_COMMAND消息处理去掉即可。win32的基本结构代码都有了,而且各个地方的代码的作用,VS2017自动加了注释,所以挺适合新手学习的。不熟悉VS2017的可以在C++技术网搜索“VS2017”阅读相关文章。
2.实现极简的界面
我们在初始化实例函数InitInstance中,在CreateWindowW函数中将第三个参数改为:WS_MINIMIZEBOX | WS_EX_LAYERED。这样窗口的样式就设置到位了。然后我们还要加上背景透明,所以要修改窗口类。加入如下两行代码即可:
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_MINIMIZEBOX | WS_EX_LAYERED,400, 300, 700, 0, nullptr, nullptr, hInstance, nullptr);
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd,0, 180, LWA_ALPHA);
透明值是180,你可以调成0-255,0为完全透明,255为完全不透明。400和300是窗口左上角出现在屏幕的位置,可以计算得到一个屏幕中间的值。不过要获取屏幕大小。我就懒得实现了,你自己去实现吧。700是窗口的宽度,这个宽度要容纳下所有显示的文字哦。高度则设置为0。这样一来,极简的窗口就已经做好了。
我们还要窗口在顶层显示,调用下面的函数:
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
3.计时和倒计时
我们在窗口创建的时候,即WM_CREATE消息产生时,创建一个定时器,1秒一个间隔触发定时处理。这样就可以计时了。而倒计时则获取默认的指定的时间,如22:30,然后用22:30转化为秒数,减去当前的时间的秒数,就可以得到倒计时了。
创建定时器代码为:
SetTimer(hWnd, 1, 1000,NULL);
我们在WM_TIMER消息中做计时和倒计时的显示,代码如下:
time_t t;
struct tm * lt;
time(&t);
lt = localtime(&t);
TCHAR buf[100] = { 0 };
int s = (h * 60 + m)*60 - ((lt->tm_hour * 60 + lt->tm_min)*60+ lt->tm_sec);
int hh = s / 3600;
int mm = (s-(hh*3600))/60;
int ss = s - (hh * 3600) -(mm*60);
wsprintf(buf,_T("距关机还剩:%02d时%02d分%02d秒-%04d-%02d-%02d %02d:%02d:%02d健康生活从早睡做起-C++技术网宣"), hh, mm,ss,lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
SetWindowText(hWnd, buf);
4.关机功能
关机我们最简单的实现方法就是调用控制台命令,也可以调用系统API,哪样简单哪样来吧。所以在定时显示后面马上做时间判断,符合要求了就执行关机命令,代码如下:
if (lt->tm_hour >= h && lt->tm_min >= m)
{
system("shutdown -s -f");
}
5.强制执行
我们要屏蔽关闭按钮,否则很容易关闭程序,作用就失去了。我们只要忽略WM_CLOSE消息就可以了。所以我们加一个WM_CLOSE消息的处理,然后什么代码也不写,直接break就可以了。程序启动之后就没有办法正常关闭了。
6.隐藏修改关机时间
增加命令行参数修改关机时间,我们只要解析命令行参数即可。不过要处理的话,要写一大段代码哦,下面是源码:
char* pC=NULL;
char buf[100] = { 0 };
int iLen=WideCharToMultiByte(CP_ACP,0, lpCmdLine,-1,NULL,0,NULL,NULL);
if(iLen>0)
{
pC=(char*)HeapAlloc(GetProcessHeap(),0,iLen);
if (pC)
{
WideCharToMultiByte(CP_ACP, 0, lpCmdLine, -1, pC, iLen, NULL, NULL);
sprintf(buf, "%s", pC);
HeapFree(GetProcessHeap(), 0, pC);
}
}
char* p = strtok(buf, ":");
int i = 0;
while (p != NULL)
{
if(i==0)
h = atoi(p);
else
m = atoi(p);
i++;
p = strtok(NULL, ":");
}
再加两个全局静态变量存默认时间和修改后存储时间的。放在最外面,如下:
static int h = 22;
static int m = 30;
其他没有讲的地方,都不用改。基本上就大功告成了。
下面是完整的代码:
#include "stdafx.h"
#include "WindowsProject2.h"
#include <time.h>
#include <iostream>
using namespace std;
#include <string>
#pragma warning(disable:4996)
#define MAX_LOADSTRING 100
HINSTANCE hInst;
WCHAR szTitle[MAX_LOADSTRING];
WCHAR szWindowClass[MAX_LOADSTRING];
static int h = 22;
static int m = 30;
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
char* pC=NULL;
char buf[100] = { 0 };
int iLen=WideCharToMultiByte(CP_ACP,0, lpCmdLine,-1,NULL,0,NULL,NULL);
if(iLen>0)
{
pC=(char*)HeapAlloc(GetProcessHeap(),0,iLen);
if (pC)
{
WideCharToMultiByte(CP_ACP, 0, lpCmdLine, -1, pC, iLen, NULL, NULL);
sprintf(buf, "%s", pC);
HeapFree(GetProcessHeap(), 0, pC);
}
}
char* p = strtok(buf, ":");
int i = 0;
while (p != NULL)
{
if(i==0)
h = atoi(p);
else
m = atoi(p);
i++;
p = strtok(NULL, ":");
}
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW ;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT2));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
RECT rect;
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_MINIMIZEBOX | WS_EX_LAYERED, 400,300, 625, 0, nullptr, nullptr, hInstance, nullptr);
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd,0, 180, LWA_ALPHA);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
SetTimer(hWnd, 1, 1000,NULL);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
SetWindowText(hWnd, _T("健康生活从早睡做起-爱宝贝VV"));
break;
}
case WM_TIMER:
{
time_t t;
struct tm * lt;
time(&t);
lt = localtime(&t);
TCHAR buf[100] = { 0 };
int s = (h * 60 + m)*60 - ((lt->tm_hour * 60 + lt->tm_min)*60+ lt->tm_sec);
int hh = s / 3600;
int mm = (s-(hh*3600))/60;
int ss = s - (hh * 3600) -(mm*60);
wsprintf(buf,_T("距关机还剩:%02d时%02d分%02d秒-%04d-%02d-%02d %02d:%02d:%02d健康生活从早睡做起-爱宝贝VV"), hh, mm,ss,lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
SetWindowText(hWnd, buf);
if (lt->tm_hour >= h && lt->tm_min >= m)
{
system("shutdown -s -f");
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
{
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
虽然给了完整代码,但是请认真理解代码,然后自己创建项目,用我们的代码替换上去试验,否则因为相关头文件包含不正确而无法编译哈。