当前位置:C++技术网 > 资讯 > Win32写一个极简定时关机软件的全部过程讲解

Win32写一个极简定时关机软件的全部过程讲解

更新时间:2018-12-18 15:44:44浏览次数:1+次

    最近,因为自制力有点差,看上节目容易停不下来,所以经常睡得很晚。靠意志力,显然没有办法解决早睡的问题了。所以想着写一个强制关机的软件,让关机变得措手不及。当一切从屏幕消失的时候,自然就没有兴趣去重新开电脑了。从而有效解决了晚睡的问题。
实现的目标是:极简、功能完整、逼格高、强制性
实现的思路是:
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;
}

    虽然给了完整代码,但是请认真理解代码,然后自己创建项目,用我们的代码替换上去试验,否则因为相关头文件包含不正确而无法编译哈。