0x01 快速自启动目录
快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫"启动"或者"Startup"。位于该目录下的PE文件会在开机后进行自启动。由于不同版本的Windows启动目录的路径都是不同的,所以我们用一个SHGetSpecialFolderPath来获取该路径。该API位于ShlObj.dll中。我们用隐式载入方式获取。先看MSDN:
该API有四个参数。
1. HWND窗口句柄作为保留一般为NULL。
2. pszPath是输出参数。是API返回的启动目录的路径
3. csidl是一个常量,需要指明为CSIDL_STARTUP表明我们要获取自启动目录路径
4. 这是一个布尔值,指明如果目录不存在,是否要创建该目录
具体看一下csidl的其他数值参考MSDN: https://docs.microsoft.com/zh-cn/windows/desktop/shell/csidl
给出一个使用实例:
#include <windows.h>
#include <tchar.h>
#include <cstdio>
#include <Shlobj.h>
#include <StrSafe.h>
#include <cstring>
// 该函数用于从完整路径中获取最后一个文件名,你也可以用PathStripPath函数,位于Shlwapi.dll中
LPCTSTR GetFileNameFromPath(LPCTSTR szPathName) {
LPCTSTR pFind = strrchr(szPathName, '\\');
if (NULL != pFind)
++pFind;
return(pFind);
}
// szSrcPathName指针指向源文件路径
BOOL RapidStartup(LPCTSTR szSrcPathName) {
TCHAR szPathName[MAX_PATH] = { 0 };
BOOL bRet = SHGetSpecialFolderPath(NULL, szPathName, CSIDL_STARTUP, TRUE);
if (FALSE == bRet)
return(FALSE);
LPCTSTR pFileName = GetFileNameFromPath(szSrcPathName);
if (!pFileName)
return(FALSE);
StringCchPrintf(szPathName, _countof(szPathName), "%s\\%s", szPathName, pFileName);
if (!CopyFile(szSrcPathName, szPathName, FALSE))
return(FALSE);
return(TRUE);
}
int _tmain() {
if (RapidStartup("C:\\Users\\Administrator\\Desktop\\hello.exe"))
printf("successful!\n");
else
printf("failed!\n");
system("pause");
return(0);
}
0x02 注册表自启动
注册表自启动也是一种非常常见的方式。关于注册表的详细介绍可以参见MSDN: https://docs.microsoft.com/zh-cn/windows/desktop/SysInfo/about-the-registry
这里简要介绍一下注册表。注册表是Windows操作系统中的一个重要数据库。其中包含了许多与系统相关的配置信息。Windows提供了一个编辑器方便我们编辑。可以按Win+R输入regedit来打开。或者直接去系统目录下寻找。
上面是注册表的基本布局。注册表相当复杂,这里仅仅讲关于自启动的配置的内容。设置自启动的键值项有很多,这里仅仅介绍两种通用的。其他的我也没了解感兴趣可以自己去百度。
1. HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
2. HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
可以看到上面两项唯一的区别就是根键(简称键)不同,子键是一样的。HKEY_CURRENT_USER与HKEY_LOCAL_MACHINE的区别是影响范围。前者影响只当前用户而后者是系统配置影响所有用户。只要里面添加入新的键值对就可以实现自启动。我们具体来看下:
进行如上图的写入操作后就可以实现开机自启动。可以看到我是在HKEY_CURRENT_USER根键下也就是说这个设置只会影响我目前的登陆账户。你可以利用编辑器手动写入也可以编写程序来实现。而这里就是要讲如何编写程序实现。
想要编写程序操作注册表就必须使用系统API函数,关于注册表的API位于advapi.dll动态库中。而关于注册表的API数不胜数,具体可以MSDN查询。我们这里只使用到如下API:
RegOpenKeyEx: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regopenkeyexa
RegCreateKeyEx: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regcreatekeyexa
RegSetKeyValue: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regsetkeyvaluea
RegDeleteKeyValue: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regdeletekeyvaluea
RegCloseKey: https://docs.microsoft.com/zh-cn/windows/desktop/api/winreg/nf-winreg-regclosekey
注册表的API看上去相当繁琐,实际上大部分参数都不尽相同。这里仅给出MSDN中关于API的详细介绍,考虑到篇幅就不做累述。可以结合百度与MSDN的介绍来理解。接下去贴出代码:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <tchar.h>
#include <cstdio>
#include <cstring>
#define DATA "C:\\Users\\Administrator\\Desktop\\hello.exe"
#define SUBKEY "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
#define HKEYMACHINE HKEY_LOCAL_MACHINE
#define HKEYCURRENTUSER HKEY_CURRENT_USER
BOOL RegistryStopAutoStartup(BOOL Is_HKEY_LOCAL_MACHINE, LPCTSTR pszValueName) {
HKEY hKeyFirst = NULL, hKey = NULL;
if (!pszValueName) {
_tprintf("字符串不能为空!\n");
return(FALSE);
}
if (Is_HKEY_LOCAL_MACHINE)
hKeyFirst = HKEY_LOCAL_MACHINE;
else
hKeyFirst = HKEY_CURRENT_USER;
if (ERROR_SUCCESS != RegDeleteKeyValue(hKeyFirst, SUBKEY, pszValueName)) {
_tprintf("%s错误代码: %d\n", "RegDeleteKeyValue", GetLastError());
return(FALSE);
}
return(TRUE);
}
BOOL RegistryAutoStartup(BOOL Is_HKEY_LOCAL_MACHINE, LPCTSTR pszKeyValue, LPCTSTR pszKeyValueData) {
HKEY hKey = NULL, hKeyFirst = NULL;
if (NULL == pszKeyValue || NULL == pszKeyValueData) {
_tprintf("字符串不能为空!\n");
return(FALSE);
}
if (Is_HKEY_LOCAL_MACHINE)
hKeyFirst = HKEY_LOCAL_MACHINE;
else
hKeyFirst = HKEY_CURRENT_USER;
if (ERROR_SUCCESS != RegCreateKeyEx(hKeyFirst, SUBKEY, 0, NULL, 0, REG_OPTION_NON_VOLATILE, NULL, &hKey, NULL)) {
if (0 != GetLastError())
return(FALSE);
else if (ERROR_SUCCESS != RegOpenKeyEx(hKeyFirst, SUBKEY, 0, KEY_ALL_ACCESS, &hKey)) {
_tprintf("%s错误代码: %d\n", "RegOpenKeyEx", GetLastError());
return(FALSE);
}
}
if (ERROR_SUCCESS != RegSetKeyValue(hKeyFirst, SUBKEY, pszKeyValue, REG_SZ, pszKeyValueData, _tcslen(pszKeyValueData))) {
_tprintf("%s错误代码: %d\n", "RegSetKeyValue", GetLastError());
return(FALSE);
}
RegCloseKey(hKey);
return(TRUE);
}
int _tmain() {
// 在注册表中写入键值
RegistryAutoStartup(FALSE, "hello", DATA);
// 删除注册表键值
system("pause");
RegistryStopAutoStartup(FALSE, "hello");
system("pause");
return(0);
}
注册表中还有两个值得注意的地方:
1. 操作HKEY_LOCAL_MACHINE根键内容时需要管理员权限否则会失败,而HKEY_CURRENT_USER只需要默认权限就足够了。
2. 在64位Windows下为了支持32位的程序,还存在一个WOW64层。具体参见: http://tech.163.com/06/0208/10/29ED63CH0009159F.html 这里就不再累述。我只想说的是由于32位程序的存在而为了使32位程序能够像64位程序一样使用相同方式操纵注册表而不必分开。Windows为我们设置了重定向。即假设我们为32位程序设置自启动,并且我们修改的是:
HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
实际上会被重定位到:
HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run
即SOFTWARE下WOW6432Node中。在这个项下的内容都是用于32位程序。Windows会自动为我们设置重定向,如果不想要重定向的话可以添加KEY_WOW64_64KEY标志。
0x03 系统服务自启动
系统服务实现自启动是一种更加隐蔽的方式,但是实现起来相对复杂一点。由于系统服务是出于SESSION0,自从Vista之后系统服务就与普通应用程序隔离了。也就是说处于SESSION0中的系统服务程序不能与应用程序交互以及共享UI。类似于MessageBox或者创建对话框之类的是没有办法显示的。本质上系统服务程序是控制台程序,但是执行一些系统的操作是可以的。当然想要获取图形界面也是可以的,具体参见: https://docs.microsoft.com/zh-cn/windows/desktop/Services/interactive-services
按Win+R后输入services.msc可以打开服务窗口:
可以对服务进行启动,停止,暂停,恢复这四种操作并且可以设置手动和自动,禁止等启动方式。在一项服务上右击选择属性就能看到如下对话框:
当然,这可以选择手动操作。但这里介绍如何用Win32 API来操作。我们要使用OpenSCManager, OpenService, CreateService, StartService, DeleteService, ControlService, CloseServiceHandle来操作。具体参见MSDN:
OpenSCManager: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-openscmanagera
CreateService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-createservicea
OpenService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-openservicea
StartService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-startservicea
DeleteService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-deleteservice
ControlService: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-controlservice
CloseServiceHandle: https://docs.microsoft.com/zh-cn/windows/desktop/api/winsvc/nf-winsvc-closeservicehandle
下面贴出实例代码:
#include <windows.h>
#include <tchar.h>
#include <Shlwapi.h>
#include <StrSafe.h>
// Shlwapi.dll需要在链接过程中使用导入库
#pragma comment(lib, "Shlwapi.lib")
BOOL ServiceCreateOrStart(BOOL IsCreate, LPCTSTR pszServicePath) {
SC_HANDLE shSCM = NULL;
TCHAR szServiceName[MAX_PATH] = { 0 };
SC_HANDLE shCS = NULL;
if (!pszServicePath)
return(FALSE);
_tcscpy_s(szServiceName, _countof(szServiceName), pszServicePath);
PathStripPath(szServiceName);
shSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (!shSCM) {
_tprintf("OpenSCManager %d", GetLastError());
return(FALSE);
}
if (IsCreate)
shCS = CreateService(shSCM, szServiceName, szServiceName, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL, pszServicePath, NULL, NULL, NULL, NULL, NULL);
else {
shCS = OpenService(shSCM, szServiceName, SERVICE_ALL_ACCESS);
if (NULL == shCS) {
_tprintf("OpenService %d", GetLastError());
CloseServiceHandle(shSCM);
return(FALSE);
}
if (!StartService(shCS, 0, NULL)) {
_tprintf("StartService %d", GetLastError());
CloseServiceHandle(shSCM);
CloseServiceHandle(shCS);
return(FALSE);
}
}
CloseServiceHandle(shSCM);
CloseServiceHandle(shCS);
return(TRUE);
}
BOOL DelOrStopService(BOOL IsDel, LPCTSTR pszServiceName) {
SC_HANDLE shSCM = NULL, shSC = NULL;
SERVICE_STATUS ss = {0};
TCHAR szServiceName[MAX_PATH] = {0};
BOOL bRet = FALSE;
if (!pszServiceName)
return(FALSE);
_tcscpy_s(szServiceName, _countof(szServiceName), pszServiceName);
PathStripPath(szServiceName);
if (NULL == (shSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS))) {
_tprintf("OpenSCManager: %d\n", GetLastError());
return(FALSE);
}
if (NULL == (shSC = OpenService(shSCM, szServiceName, SERVICE_ALL_ACCESS))) {
_tprintf("OpenService: %d\n", GetLastError());
CloseServiceHandle(shSCM);
return(FALSE);
}
if (IsDel)
bRet = DeleteService(shSC);
else
bRet = ControlService(shSC, SERVICE_CONTROL_STOP, &ss);
CloseServiceHandle(shSCM);
CloseServiceHandle(shSC);
return(bRet);
}
int _tmain() {
ServiceCreateOrStart(TRUE, "C:\\Users\\Administrator\\Desktop\\SERVICEEXE.exe");
DelOrStopService(TRUE, "C:\\Users\\Administrator\\Desktop\\SERVICEEXE.exe");
system("pause");
return(0);
}
值得注意的是操纵系统服务需要管理员权限。上面这段代码实现了系统服务的创建,启动,关闭,删除等功能。
系统服务程序的编写也和普通程序不一样。其流程类似于下面的图:
接下去我们贴出实例代码:
#include <windows.h>
#include <tchar.h>
//LPSERVICE_MAIN_FUNCTIONA LpserviceMainFunctiona;
TCHAR szServiceName[MAX_PATH] = "ServiceTest.exe";
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = 0;
void MyFunc() {
HANDLE hFile = NULL;
TCHAR szText[] = "If you see this, it means service is created successfully : D";
DWORD dwSize = 0;
hFile = CreateFile("C:\\Users\\Administrator\\Desktop\\ttt.txt", GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, szText, _countof(szText), &dwSize, NULL);
CloseHandle(hFile);
}
BOOL SetServiceStatus(DWORD dwServiceCode) {
SERVICE_STATUS ss = {0};
ss.dwCurrentState = dwServiceCode;
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN |
SERVICE_ACCEPT_PAUSE_CONTINUE;
ss.dwWin32ExitCode = 0;
ss.dwWaitHint = 3000;
return(SetServiceStatus(g_ServiceStatusHandle, &ss));
}
void __stdcall LphandlerFunction(DWORD dwControl) {
switch (dwControl) {
case SERVICE_CONTROL_STOP:
SetServiceStatus(SERVICE_STOP_PENDING);
SetServiceStatus(SERVICE_STOPPED);
break;
case SERVICE_CONTROL_PAUSE:
SetServiceStatus(SERVICE_PAUSE_PENDING);
SetServiceStatus(SERVICE_PAUSED);
break;
case SERVICE_CONTROL_CONTINUE:
SetServiceStatus(SERVICE_CONTINUE_PENDING);
SetServiceStatus(SERVICE_RUNNING);
break;
default:
break;
}
}
void __stdcall LpserviceMainFunctiona(
DWORD dwNumServicesArgs,
LPSTR* lpServiceArgVectors
) {
g_ServiceStatusHandle = RegisterServiceCtrlHandler(szServiceName, LphandlerFunction);
if (0 == g_ServiceStatusHandle)
return;
SetServiceStatus(SERVICE_START_PENDING);
SetServiceStatus(SERVICE_RUNNING);
while (TRUE) {
Sleep(5000);
MyFunc();
}
}
int _tmain() {
SERVICE_TABLE_ENTRY stDispatchTable[] = {
{szServiceName, LpserviceMainFunctiona},
{NULL, NULL}
};
StartServiceCtrlDispatcher(stDispatchTable);
return(0);
}
这就是服务程序。注意要把想实现的功能用死循环包裹起来不然重启会导致功能无法运行。
0x04 计划任务实现自启动
实现原理
我是使用Windows Shell编程实现创建任务计划,所以会涉及COM组件相关知识。
现在,为方便大家的理解,我把整个程序的逻辑概括为 3 各部分,分别是:初始化操作、创建任务计划以及删除任务计划。现在,我们一一对每一个部分进行解析。
初始化操作的目的就是获取 ITaskService 对象以及 ITaskFolder 对象,我们创建任务计划,主要是对这两个指针进行操作。具体流程是:
-
首先初始化COM接口环境,因为我们接下来会使用到COM组件
-
然后,创建 ITaskService 对象,并连接到任务服务上
-
接着,从 ITaskService 对象中获取 ITaskFolder 对象
这样,初始化操作就完成了,接下来就是直接使用 ITaskService 对象以及 ITaskFolder 对象进行操作了。
现在,解析下任务计划的具体创建过程:
-
首先,从 ITaskService 对象中创建一个任务定义对象 ITaskDefinition,用来来创建任务
-
接着,就是开始对任务定义对象 ITaskDefinition进行设置:
-
设置注册信息,包括设置作者的信息
-
设置主体信息,包括登陆类型、运行权限
-
设置设置信息,包括设置在使用电池运行时是否停止、在使用电池是是否允许运行、是否允许手动运行、是否设置多个实例
-
设置操作信息,包括启动程序,并设置运行程序的路径和参数
-
设置触发器信息,包括用户登录时触发
-
-
最后,使用 ITaskFolder 对象根据任务定义对象 ITaskDefinition的设置,注册任务计划
这样,任务计划创建的操作就完成了,只要满足设置的触发条件,那么就会启动指定程序。
ITaskFolder 对象存储着已经注册成功的任务计划的信息,我们只需要将任务计划的名称传入其中,调用DeleteTask接口函数,就可以删除指定的任务计划了。
注意:创建任务计划,要求程序必须要有管机员权限才行。所以,测试的时候,不要忘记以管理员身份运行程序。
创建任务计划的初始化
CMyTaskSchedule::CMyTaskSchedule(void)
{
m_lpITS = NULL;
m_lpRootFolder = NULL;
// 初始化COM
HRESULT hr = ::CoInitialize(NULL);
if(FAILED(hr))
{
ShowError("CoInitialize", hr);
}
// 创建一个任务服务(Task Service)实例
hr = ::CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(LPVOID *)(&m_lpITS));
if(FAILED(hr))
{
ShowError("CoCreateInstance", hr);
}
// 连接到任务服务(Task Service)
hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if(FAILED(hr))
{
ShowError("ITaskService::Connect", hr);
}
// 获取Root Task Folder的指针,这个指针指向的是新注册的任务
hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
if(FAILED(hr))
{
ShowError("ITaskService::GetFolder", hr);
}
}
创建任务计划
BOOL CMyTaskSchedule::NewTask(char *lpszTaskName, char *lpszProgramPath, char *lpszParameters, char *lpszAuthor)
{
if(NULL == m_lpRootFolder)
{
return FALSE;
}
// 如果存在相同的计划任务,则删除
Delete(lpszTaskName);
// 创建任务定义对象来创建任务
ITaskDefinition *pTaskDefinition = NULL;
HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
if(FAILED(hr))
{
ShowError("ITaskService::NewTask", hr);
return FALSE;
}
/* 设置注册信息 */
IRegistrationInfo *pRegInfo = NULL;
CComVariant variantAuthor(NULL);
variantAuthor = lpszAuthor;
hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
if(FAILED(hr))
{
ShowError("pTaskDefinition::get_RegistrationInfo", hr);
return FALSE;
}
// 设置作者信息
hr = pRegInfo->put_Author(variantAuthor.bstrVal);
pRegInfo->Release();
/* 设置登录类型和运行权限 */
IPrincipal *pPrincipal = NULL;
hr = pTaskDefinition->get_Principal(&pPrincipal);
if(FAILED(hr))
{
ShowError("pTaskDefinition::get_Principal", hr);
return FALSE;
}
// 设置登录类型
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
// 设置运行权限
// 最高权限
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
pPrincipal->Release();
/* 设置其他信息 */
ITaskSettings *pSettting = NULL;
hr = pTaskDefinition->get_Settings(&pSettting);
if(FAILED(hr))
{
ShowError("pTaskDefinition::get_Settings", hr);
return FALSE;
}
// 设置其他信息
hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE);
hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
hr = pSettting->put_AllowDemandStart(VARIANT_TRUE);
hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE);
hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL);
pSettting->Release();
/* 创建执行动作 */
IActionCollection *pActionCollect = NULL;
hr = pTaskDefinition->get_Actions(&pActionCollect);
if(FAILED(hr))
{
ShowError("pTaskDefinition::get_Actions", hr);
return FALSE;
}
IAction *pAction = NULL;
// 创建执行操作
hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
pActionCollect->Release();
/* 设置执行程序路径和参数 */
CComVariant variantProgramPath(NULL);
CComVariant variantParameters(NULL);
IExecAction *pExecAction = NULL;
hr = pAction->QueryInterface(IID_IExecAction, (PVOID *)(&pExecAction));
if(FAILED(hr))
{
pAction->Release();
ShowError("IAction::QueryInterface", hr);
return FALSE;
}
pAction->Release();
// 设置程序路径和参数
variantProgramPath = lpszProgramPath;
variantParameters = lpszParameters;
pExecAction->put_Path(variantProgramPath.bstrVal);
pExecAction->put_Arguments(variantParameters.bstrVal);
pExecAction->Release();
/* 创建触发器,实现用户登陆自启动 */
ITriggerCollection *pTriggers = NULL;
hr = pTaskDefinition->get_Triggers(&pTriggers);
if (FAILED(hr))
{
ShowError("pTaskDefinition::get_Triggers", hr);
return FALSE;
}
// 创建触发器
ITrigger *pTrigger = NULL;
hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
if (FAILED(hr))
{
ShowError("ITriggerCollection::Create", hr);
return FALSE;
}
/* 注册任务计划 */
IRegisteredTask *pRegisteredTask = NULL;
CComVariant variantTaskName(NULL);
variantTaskName = lpszTaskName;
hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal,
pTaskDefinition,
TASK_CREATE_OR_UPDATE,
_variant_t(),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(""),
&pRegisteredTask);
if(FAILED(hr))
{
pTaskDefinition->Release();
ShowError("ITaskFolder::RegisterTaskDefinition", hr);
return FALSE;
}
pTaskDefinition->Release();
pRegisteredTask->Release();
return TRUE;
}
删除任务计划
BOOL CMyTaskSchedule::Delete(char *lpszTaskName)
{
if(NULL == m_lpRootFolder)
{
return FALSE;
}
CComVariant variantTaskName(NULL);
variantTaskName = lpszTaskName;
HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0);
if(FAILED(hr))
{
return FALSE;
}
return TRUE;
}
程序测试
在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
int _tmain(int argc, _TCHAR* argv[])
{
CMyTaskSchedule task;
BOOL bRet = FALSE;
// 创建 任务计划
bRet = task.NewTask("520", "C:\\Users\\DemonGan\\Desktop\\520.exe", "", "");
if (FALSE == bRet)
{
printf("Create Task Schedule Error!\n");
}
// 暂停
printf("Create Task Schedule OK!\n");
system("pause");
// 卸载 任务计划
bRet = task.Delete("520");
if (FALSE == bRet)
{
printf("Delete Task Schedule Error!\n");
}
printf("Delete Task Schedule OK!\n");
system("pause");
return 0;
}
测试结果:
“以管理员身份运行程序”方式打开程序,提示任务计划创建成功。
打开任务计划列表进行查看,发现“520”任务计划成功创建。
然后,删除创建的任务计划,程序提示删除成功。
查看任务计划列表,发现“520”任务计划已经成功删除。