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), &registeredTask);
        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.

这会使用更新后的值更新任务定义的注册,这正是我想要做的,而且成功了!!!!