dahall / TaskScheduler

Provides a .NET wrapper for the Windows Task Scheduler. It aggregates the multiple versions, provides an editor and allows for localization.
MIT License
1.21k stars 191 forks source link

[Question] How to use ITaskHandlerStatus.UpdateStatus and verify it worked? #837

Closed Gwindalmir closed 4 years ago

Gwindalmir commented 4 years ago

I've been trying to figure out how the UpdateStatus method is supposed to work, or even how to see the messages it logs.

I can hardly find any documentation, even from Microsoft. The signature is:

HRESULT UpdateStatus(
  short percentComplete,
  BSTR  statusMessage
);

My questions are:

What is the format of percentComplete?

I've seen conflicting information on what goes there. This comment https://github.com/dahall/TaskScheduler/issues/627#issuecomment-381365869 uses a division and cast to short... which is always going to be 0 or 1. That's not really a percentage, that's just complete or not complete. Then, I found some documentation buried here that describes it as a value from 0-100.

The return code is always 0 when I call it. I even thought the marshaling might be wrong, so I changed the P/Invoke signature to:

[return: MarshalAs(UnmanagedType.Error)]
int UpdateStatus([In] short percentComplete, [In, MarshalAs(UnmanagedType.BStr)] string statusMessage);

Notice the return attribute (since the original method has an HRESULT return).

However still nothing happens that I can see. My message is never logged in the Task Scheduler UI, and it never shows anything in the history that indicates it was ever called.

TaskCompleted method works fine though. As soon as I call that, my error code is shown in the Task UI, but no message.

Which leads to my second question: How can I actually see the messages that were logged with UpdateStatus?

Ideally, I'd like to display a custom message in the "Last Run Result", next to that error code. However I'd be happy with just being able to see any message I posted somewhere where users can see it.

Here's a snapshot of the task history: image

Currently, I call the method this way for each successful action in a task:

var percent = (short)(((float)(taskData.Actions.IndexOf(a) + 1) / taskData.Actions.Count) * 100);
this.StatusHandler.UpdateStatus(percent, $"Action: {a.Operation} completed.");

And at the end of all the actions if all were successful:

this.StatusHandler.UpdateStatus((short)100, "Task completed successfully.");
this.StatusHandler.TaskCompleted(0);

For tasks that error out:

this.StatusHandler.UpdateStatus(100, ex.Message);
this.StatusHandler.TaskCompleted(ex.ErrorCode != 0 ? ex.ErrorCode : -1);

The ErrorCode above shows in the UI, but the Message does not.

dahall commented 4 years ago

I guess the first thing I should clarify is the ITaskHandler and ITaskHandlerStatus are defined by Microsoft and are implemented in the OS. I have a simple execution thread for mimicking the OS in my library, but COM objects that implement that interface are processed in the OS.

That said, I'm not sure how you are "running" the thread. Are you registering the COM Handler action in the task and then running the task or letting the task trigger? If so, then we need to look at what may be happening in the OS. I'm happy to try out some of my custom COM handlers to see. Let me know how you're running and this and I'll duplicate.

Gwindalmir commented 4 years ago

I'm manually running the task in the Windows Task Scheduler UI. The code that calls UpdateStatus is inside my ITaskHandler implementation. In the screenshot above, I right click the task, and click Run.

I understand this is Microsoft's API, but I figured since you wrote the wrapper, you would have an idea of how it's supposed to work. I apologize if that wasn't appropriate. 😃

dahall commented 4 years ago

Perfect. I'm curious too. Did you implement your COM Handler using my template at https://github.com/dahall/ITaskHandlerTemplate or did you implement it in C/C++? I'll be testing using my template but can switch to C++ if that doesn't help us get to an answer.

Gwindalmir commented 4 years ago

Everything is in C#, using your template. One exception is the Transaction attribute, I have that set to NotSupported instead. I have code called by the handler that fails if executing within a transaction.

dahall commented 4 years ago

After some testing, best I can tell, the UpdateStatus message does not get logged into the Event Log in any way. It doesn't even update the UI when the task is running in any way. The UI only shows "Running..." and does not show status. So, that explains the lack of information on the percentComplete parameter. It is never used, so why bother defining how it works! The only logging done by the Task Scheduler is into the Event Log, which you see in the history pane. There is no interface or method in any of the exposed interfaces from Microsoft that even refer to the status. In conclusion, I think it is a case of an un-implemented method.

Gwindalmir commented 4 years ago

Interesting. Good job, Microsoft! /s

I suppose I'll have to write to the event log myself. It's messy too, so I was hoping to avoid that. 😄

EDIT: You can close this issue if you like, or use it to check in doc changes.

dahall commented 4 years ago

If you come up with a way to extend TaskEvent or TaskEventLog in my library to write (today it only reads), please let me know and I'll add it. I think that would be a good addition.

Gwindalmir commented 4 years ago

Hmm, I'm not using those at all currently in my product. I might take a look at those in the future after all then.

dahall commented 4 years ago

There's also a CorrelatedTaskEvent class in the Editor project worth looking at too then.