c++ - 为特定用户创建计划任务
问题描述
我正在尝试制作一个可以在工作中用于设置 PC 和笔记本电脑演示的小应用程序。此应用程序将运行一系列操作。其中一项操作是为客户帐户创建计划任务。客户帐户是通过简单地创建的NET USER Customer /ADD /PASSWORDREQ:NO
(我曾尝试使用 Windows API 来实现这一点,但我遇到了很多问题。但这是另一个问题)。
为客户帐户创建的计划任务将像这样发出:SCHTASKS /CREATE /TN DemoReboot /SC DAILY /ST 12:00 /ET 18:00 /MO 2 /TR "SHUTDOWN /R /F /T 30"
。
我知道/U
和/P
参数,但不幸的是它们不起作用,因为不允许将凭据保存在机器上,如果没有提供密码,系统会提示您输入密码。
我目前执行上述命令的方法是这样的:
bool AccountManager::run(const QString &rUserName, const QString &rPass, const QString &rCommand)
{
HANDLE hToken;
LPVOID lpvEnv;
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
std::wstring wUser = rUserName.toStdWString();
WCHAR *pUser = const_cast<WCHAR*>(wUser.c_str());
std::wstring wPass = rPass.toStdWString();
WCHAR *pPass = const_cast<WCHAR*>(wPass.c_str());
std::wstring wCommand = rCommand.toStdWString();
WCHAR *pCommand = const_cast<WCHAR*>(wCommand.c_str());
/*
if (!CreateProcessWithLogonW(test, L".", pass,
0, NULL, cmd,
CREATE_UNICODE_ENVIRONMENT, NULL, NULL,
&si, &pi))
DisplayError(str);
*/
if (!LogonUser(pUser, L".", pPass, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hToken)) {
setLastError("LogonUser");
return false;
}
/*
if (!ImpersonateLoggedOnUser(&hToken))
{
setLastError("ImpersonateLoggedOnUser");
return false;
}
*/
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE)) {
setLastError("CreateEnvironmentBlock");
return false;
}
// I've tried specifying NULL for the second paramater (lpDomain)
if (!CreateProcessWithLogonW(pUser, L".", pPass,
LOGON_WITH_PROFILE, NULL, pCommand,
// I've tried using the created environmentBlock
//CREATE_UNICODE_ENVIRONMENT, lpvEnv, NULL,
CREATE_UNICODE_ENVIRONMENT, NULL, NULL,
&si, &pi)) {
setLastError("CreateProcessWithLogonW");
return false;
}
if (!DestroyEnvironmentBlock(lpvEnv)) {
setLastError("DestroyEnvironmentBlock");
return false;
}
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
qDebug() << "finished";
return true;
}
这个函数的调用方式:
void MainWindow::onCreateRebootTaskClicked()
{
// Memory Leak
QProcess *pProc = new QProcess();
pProc->setProgram("cmd.exe");
// Blank password is not allowed, so add temp one
pProc->setArguments({QString("/C net user %1 %2").arg(ui->cxNameLineEdit->text(), "cccx") });
pProc->start();
pProc->waitForFinished();
// Tried without using cmd /C
const QString command = QString("cmd /C SCHTASKS /CREATE /TN \"%1\" /SC DAILY /ST %2 /ET %3 /MO %4 /TR \"%5\"").arg(
ui->taskRebootNameLineEdit->text(),
ui->taskStartTimeEdit->text(),
ui->taskEndTimeEdit->text(),
ui->taskModifierSpinBox->text(),
QString("shutdown /r /f /t %1").arg(ui->taskRebootTimeOutSpinBox->value()));
if (!AccountManager::run(ui->cxNameLineEdit->text(), "cccx", command))
{
const auto &rError = AccountManager::getLastError();
QMessageBox::warning(this, rError.errorCodeName, rError.msg);
}
// Remove temp password
pProc->setArguments({QString("/C net user %1 %2").arg(ui->cxNameLineEdit->text(), "") });
pProc->start();
}
我在代码中留下了一些注释,说明了我尝试过的一些调整。
执行完命令后,我登录Customer账号运行SCHTASKS /DELETE /TN DemoReboot
,查看Task是否创建成功,但是没有找到任务名。
接下来,我尝试使用Run
上面列出的函数,将其指定cmd
为命令,以便获取客户帐户的命令行。随着弹出的命令行,我手动输入SCHTASKS /CREATE /TN DemoReboot /SC DAILY /ST 12:00 /ET 18:00 /MO 2 /TR "SHUTDOWN /R /F /T 30"
,然后登录客户帐户并尝试再次删除任务,这确实有效;任务在那里被删除!
我还考虑过其他方法,例如使用客户帐户的注册表,在 下添加密钥RunOnce
,但我无法从不同的本地帐户成功访问客户帐户的注册表。
希望我没有遗漏任何东西。
更新:我已经设法找出问题所在。第一个问题是对于/MO
选项,taskModifierSpinBox
文本后缀有“hr”。为了解决这个问题,我只是QString
这样拆分:ui->taskModifierSpinBox.text().split(' ').first()
我应该做的,直到现在我才想到的是使用ui->taskModiferSpinBox->value().toString();
另一个问题是转义的双引号......我真的不需要它们围绕参数 for /TN
,但/TR
我需要。我尝试过使用不起作用的单引号。我曾尝试在双引号上加倍,但这也不起作用。我也试过\\\"
,但也没有用。
我的结果是创建一个批处理文件,并将批处理文件作为命令传递给run
. 现在这不是很理想,但我还没有想出一种方法来成功传递一个有空格的参数/TR
解决方案
推荐阅读
- python - 如何从字符串中分离整数并将其转换为浮点数
- php - Laravel - 返回对 AJAX 请求的响应后继续运行功能
- c++ - 在 Windows 上使用 CMake 强制静态链接
- javascript - React 项目 firebase 部署显示欢迎屏幕
- symfony - 尝试登录用户 [Symfony 3.4] FOSUserBundke+Lexik+FosRest 时获得匿名
- c - 使用 C 中的包装函数进行递归合并排序
- javascript - Javascript查找两个数字的增加和减少百分比
- python - 可重现的权重矩阵张量流
- java - 计算每个用户输入?
- android - 颤振:java.lang.RuntimeException Logcat