TaskScheduler ExecAction put_WorkingDirectory 无法更新其值
TaskScheduler ExecAction put_WorkingDirectory fails to update its value
我正在处理一个需要安装一系列 Windows Task Scheduler 任务的项目,为此我创建了一个 Wix 项目和一个处理所有细节的自定义操作。该自定义操作是使用 C++ 创建的,以避免依赖于 .NET Framework。
起初我从 Wix 自定义操作开始执行 SCHTASKS.EXE 但是,在我设法创建正确的命令行以正确安装任务后,我意识到我无法设置操作的工作目录('Start in' 在 SCHTASKS.EXE 命令行的任务计划程序 UI) 中,因为它根本没有选项...
然后我决定在 C++ (#import <taskschd.dll> raw_interfaces_only
) 中使用 COM 来访问任务计划程序,并在使用 SCHTASKS.EXE 安装任务后使用 API 调整工作文件夹除了那个细节之外还不错。
在正确安装任务后,我设法完成了任务并读取了它的值,但是当我使用当前值执行 put_WorkingDirectory
方法时 实际上并没有失败,但该值没有保存到任务.
有人知道我为什么无法到达那里吗?这是我用来访问 ExecAction 并成功设置值的代码的一部分。请记住,这是在 Wix 自定义操作中,因此一些方法调用是 Wix 的。
这段代码确实有效,日志显示了我打算设置的正确路径,但任务没有改变。我做错了什么?
HRESULT UpdateWorkingDirectory(TaskScheduler::ITaskFolderPtr rootFolder, BSTR taskName, BSTR installFolder)
{
HRESULT hr = S_OK;
TaskScheduler::IRegisteredTaskCollectionPtr taskCollection;
LONG numTasks = 0;
TaskScheduler::IRegisteredTaskPtr thisTask;
TaskScheduler::ITaskDefinitionPtr definition;
TaskScheduler::IActionCollectionPtr actions;
TaskScheduler::IActionPtr action;
TaskScheduler::IExecActionPtr execAction;
long actionCount;
hr = rootFolder->GetTasks(NULL, &taskCollection);
ExitOnFailure(hr, "Cannot get task collection pointer");
hr = taskCollection->get_Count(&numTasks);
ExitOnFailure(hr, "Cannot get task collection item count");
for (LONG taskIdx = 0; taskIdx < numTasks; taskIdx++) {
TaskScheduler::IRegisteredTaskPtr registeredTask;
bstr_t taskIdxName;
hr = taskCollection->get_Item(variant_t(taskIdx + 1), ®isteredTask);
ExitOnFailure(hr, "Cannot get task item %d", taskIdx + 1);
hr = registeredTask->get_Name(&taskIdxName.GetBSTR());
ExitOnFailure(hr, "Cannot get task name");
WcaLog(LOGMSG_STANDARD, " registered task name = %s", (LPCSTR)taskIdxName);
if (strcmp(bstr_t(taskName), taskIdxName) == 0) {
thisTask = registeredTask;
break;
}
}
if (thisTask == NULL) {
hr = E_FAIL;
ExitOnFailure(hr, "task {%S} not found", taskName);
}
hr = thisTask->get_Definition(&definition);
ExitOnFailure(hr, "error getting task definition for {%S}", taskName);
hr = definition->get_Actions(&actions);
ExitOnFailure(hr, "error getting actions for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " got actions from %S", taskName);
hr = actions->get_Count(&actionCount);
ExitOnFailure(hr, "error getting action count for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " got count = %d from {%S}", actionCount, taskName);
if (actionCount > 0) {
bstr_t actionId;
bstr_t arguments;
bstr_t path;
hr = actions->get_Item(1, &action);
ExitOnFailure(hr, "error getting action[1] for {%S}", taskName);
hr = action->QueryInterface(&execAction);
ExitOnFailure(hr, "error getting ExecAction for {%S}", taskName);
hr = execAction->get_Id(&actionId.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action id for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action Id is %s", (LPCSTR)actionId);
hr = execAction->get_Arguments(&arguments.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action arguments for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action arguments are %s", (LPCSTR)arguments);
hr = execAction->get_Path(&path.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action path for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action path is %s", (LPCSTR)path);
hr = execAction->put_WorkingDirectory(installFolder);
ExitOnFailure(hr, "error putting working directory for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " successful put working directory to %S", installFolder);
}
LExit:
return hr;
}
注意:我发现的所有 C++ 示例都使用简单的接口指针而不是智能指针,我更喜欢智能指针以避免自己处理发布
在我看来,这是一个很好的例子:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa446854(v=vs.85).aspx
更具体地说,它表明您在更改工作目录等内容之前调用了 ITask::Activate,并且您调用了 IPersistFile 来保存更改。我在发布的代码示例中看不到任何这些。我已经有一段时间没有看过这些接口了,但这可能就是问题所在。
我找到了答案
我不得不更改我的方法签名以适应用户名和密码
HRESULT UpdateWorkingDirectory(TaskScheduler::ITaskFolderPtr rootFolder, BSTR taskName, BSTR installFolder, variant_t username, variant_t password)
并添加这五行
thisTask = NULL;
hr = rootFolder->RegisterTaskDefinition(taskName, definition, TaskScheduler::_TASK_CREATION::TASK_UPDATE, username, password, TaskScheduler::_TASK_LOGON_TYPE::TASK_LOGON_PASSWORD, variant_t(), &thisTask);
ExitOnFailure(hr, "error updating task regisration for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " successful update of task regisration to %S", taskName);
执行后put_WorkingDirectory.
这会使用更新后的值更新任务定义的注册,这正是我想要做的,而且成功了!!!!
我正在处理一个需要安装一系列 Windows Task Scheduler 任务的项目,为此我创建了一个 Wix 项目和一个处理所有细节的自定义操作。该自定义操作是使用 C++ 创建的,以避免依赖于 .NET Framework。
起初我从 Wix 自定义操作开始执行 SCHTASKS.EXE 但是,在我设法创建正确的命令行以正确安装任务后,我意识到我无法设置操作的工作目录('Start in' 在 SCHTASKS.EXE 命令行的任务计划程序 UI) 中,因为它根本没有选项...
然后我决定在 C++ (#import <taskschd.dll> raw_interfaces_only
) 中使用 COM 来访问任务计划程序,并在使用 SCHTASKS.EXE 安装任务后使用 API 调整工作文件夹除了那个细节之外还不错。
在正确安装任务后,我设法完成了任务并读取了它的值,但是当我使用当前值执行 put_WorkingDirectory
方法时 实际上并没有失败,但该值没有保存到任务.
有人知道我为什么无法到达那里吗?这是我用来访问 ExecAction 并成功设置值的代码的一部分。请记住,这是在 Wix 自定义操作中,因此一些方法调用是 Wix 的。
这段代码确实有效,日志显示了我打算设置的正确路径,但任务没有改变。我做错了什么?
HRESULT UpdateWorkingDirectory(TaskScheduler::ITaskFolderPtr rootFolder, BSTR taskName, BSTR installFolder)
{
HRESULT hr = S_OK;
TaskScheduler::IRegisteredTaskCollectionPtr taskCollection;
LONG numTasks = 0;
TaskScheduler::IRegisteredTaskPtr thisTask;
TaskScheduler::ITaskDefinitionPtr definition;
TaskScheduler::IActionCollectionPtr actions;
TaskScheduler::IActionPtr action;
TaskScheduler::IExecActionPtr execAction;
long actionCount;
hr = rootFolder->GetTasks(NULL, &taskCollection);
ExitOnFailure(hr, "Cannot get task collection pointer");
hr = taskCollection->get_Count(&numTasks);
ExitOnFailure(hr, "Cannot get task collection item count");
for (LONG taskIdx = 0; taskIdx < numTasks; taskIdx++) {
TaskScheduler::IRegisteredTaskPtr registeredTask;
bstr_t taskIdxName;
hr = taskCollection->get_Item(variant_t(taskIdx + 1), ®isteredTask);
ExitOnFailure(hr, "Cannot get task item %d", taskIdx + 1);
hr = registeredTask->get_Name(&taskIdxName.GetBSTR());
ExitOnFailure(hr, "Cannot get task name");
WcaLog(LOGMSG_STANDARD, " registered task name = %s", (LPCSTR)taskIdxName);
if (strcmp(bstr_t(taskName), taskIdxName) == 0) {
thisTask = registeredTask;
break;
}
}
if (thisTask == NULL) {
hr = E_FAIL;
ExitOnFailure(hr, "task {%S} not found", taskName);
}
hr = thisTask->get_Definition(&definition);
ExitOnFailure(hr, "error getting task definition for {%S}", taskName);
hr = definition->get_Actions(&actions);
ExitOnFailure(hr, "error getting actions for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " got actions from %S", taskName);
hr = actions->get_Count(&actionCount);
ExitOnFailure(hr, "error getting action count for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " got count = %d from {%S}", actionCount, taskName);
if (actionCount > 0) {
bstr_t actionId;
bstr_t arguments;
bstr_t path;
hr = actions->get_Item(1, &action);
ExitOnFailure(hr, "error getting action[1] for {%S}", taskName);
hr = action->QueryInterface(&execAction);
ExitOnFailure(hr, "error getting ExecAction for {%S}", taskName);
hr = execAction->get_Id(&actionId.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action id for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action Id is %s", (LPCSTR)actionId);
hr = execAction->get_Arguments(&arguments.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action arguments for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action arguments are %s", (LPCSTR)arguments);
hr = execAction->get_Path(&path.GetBSTR());
ExitOnFailure(hr, "error getting Exec Action path for first exec action of {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " first Exec Action path is %s", (LPCSTR)path);
hr = execAction->put_WorkingDirectory(installFolder);
ExitOnFailure(hr, "error putting working directory for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " successful put working directory to %S", installFolder);
}
LExit:
return hr;
}
注意:我发现的所有 C++ 示例都使用简单的接口指针而不是智能指针,我更喜欢智能指针以避免自己处理发布
在我看来,这是一个很好的例子:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa446854(v=vs.85).aspx
更具体地说,它表明您在更改工作目录等内容之前调用了 ITask::Activate,并且您调用了 IPersistFile 来保存更改。我在发布的代码示例中看不到任何这些。我已经有一段时间没有看过这些接口了,但这可能就是问题所在。
我找到了答案
我不得不更改我的方法签名以适应用户名和密码
HRESULT UpdateWorkingDirectory(TaskScheduler::ITaskFolderPtr rootFolder, BSTR taskName, BSTR installFolder, variant_t username, variant_t password)
并添加这五行
thisTask = NULL;
hr = rootFolder->RegisterTaskDefinition(taskName, definition, TaskScheduler::_TASK_CREATION::TASK_UPDATE, username, password, TaskScheduler::_TASK_LOGON_TYPE::TASK_LOGON_PASSWORD, variant_t(), &thisTask);
ExitOnFailure(hr, "error updating task regisration for {%S}", taskName);
WcaLog(LOGMSG_STANDARD, " successful update of task regisration to %S", taskName);
执行后put_WorkingDirectory.
这会使用更新后的值更新任务定义的注册,这正是我想要做的,而且成功了!!!!