From: Paul Gofman pgofman@codeweavers.com
--- dlls/taskschd/task.c | 107 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-)
diff --git a/dlls/taskschd/task.c b/dlls/taskschd/task.c index 8c9fc9f2582..28c0ff89945 100644 --- a/dlls/taskschd/task.c +++ b/dlls/taskschd/task.c @@ -2457,6 +2457,19 @@ static inline HRESULT write_text_value(IStream *stream, const WCHAR *name, const return write_stringW(stream, L">\n"); }
+static HRESULT write_bool_value(IStream *stream, const WCHAR *name, VARIANT_BOOL value) +{ + return write_text_value(stream, name, value ? L"true" : L"false"); +} + +static HRESULT write_int_value(IStream *stream, const WCHAR *name, int val) +{ + WCHAR s[32]; + + swprintf(s, ARRAY_SIZE(s), L"%d", val); + return write_text_value(stream, name, s); +} + static HRESULT write_task_attributes(IStream *stream, ITaskDefinition *taskdef) { HRESULT hr; @@ -2684,12 +2697,104 @@ static HRESULT write_principal(IStream *stream, IPrincipal *principal) return write_element_end(stream, L"Principals"); }
+const WCHAR *string_from_instances_policy(TASK_INSTANCES_POLICY policy) +{ + switch (policy) + { + case TASK_INSTANCES_PARALLEL: return L"Parallel"; + case TASK_INSTANCES_QUEUE: return L"Queue"; + case TASK_INSTANCES_IGNORE_NEW: return L"IgnoreNew"; + case TASK_INSTANCES_STOP_EXISTING : return L"StopExisting"; + } + return L"<error>"; +} + static HRESULT write_settings(IStream *stream, ITaskSettings *settings) { + INetworkSettings *network_settings; + TASK_INSTANCES_POLICY policy; + IIdleSettings *idle_settings; + VARIANT_BOOL bval; + HRESULT hr; + INT ival; + BSTR s; + if (!settings) return write_empty_element(stream, L"Settings");
- FIXME("stub\n"); + if (FAILED(hr = write_element(stream, L"Settings"))) + return hr; + + push_indent(); + +#define WRITE_BOOL_OPTION(name) \ + { \ + if (FAILED(hr = ITaskSettings_get_##name(settings, &bval))) \ + return hr; \ + if (FAILED(hr = write_bool_value(stream, L ## #name, bval))) \ + return hr; \ + } + + + if (FAILED(hr = ITaskSettings_get_AllowDemandStart(settings, &bval))) + return hr; + if (FAILED(hr = write_bool_value(stream, L"AllowStartOnDemand", bval))) + return hr; + + if (SUCCEEDED(hr = TaskSettings_get_RestartInterval(settings, &s)) && s) + { + FIXME("RestartInterval not handled.\n"); + SysFreeString(s); + } + if (FAILED(hr = ITaskSettings_get_MultipleInstances(settings, &policy))) + return hr; + if (FAILED(hr = write_text_value(stream, L"MultipleInstancesPolicy", string_from_instances_policy(policy)))) + return hr; + + WRITE_BOOL_OPTION(DisallowStartIfOnBatteries); + WRITE_BOOL_OPTION(StopIfGoingOnBatteries); + WRITE_BOOL_OPTION(AllowHardTerminate); + WRITE_BOOL_OPTION(StartWhenAvailable); + WRITE_BOOL_OPTION(RunOnlyIfNetworkAvailable); + WRITE_BOOL_OPTION(WakeToRun); + WRITE_BOOL_OPTION(Enabled); + WRITE_BOOL_OPTION(Hidden); + + if (SUCCEEDED(hr = TaskSettings_get_DeleteExpiredTaskAfter(settings, &s)) && s) + { + hr = write_text_value(stream, L"DeleteExpiredTaskAfter", s); + SysFreeString(s); + if (FAILED(hr)) + return hr; + } + if (SUCCEEDED(hr = TaskSettings_get_IdleSettings(settings, &idle_settings))) + { + FIXME("IdleSettings not handled.\n"); + IIdleSettings_Release(idle_settings); + } + if (SUCCEEDED(hr = TaskSettings_get_NetworkSettings(settings, &network_settings))) + { + FIXME("NetworkSettings not handled.\n"); + INetworkSettings_Release(network_settings); + } + if (SUCCEEDED(hr = TaskSettings_get_ExecutionTimeLimit(settings, &s)) && s) + { + hr = write_text_value(stream, L"ExecutionTimeLimit", s); + SysFreeString(s); + if (FAILED(hr)) + return hr; + } + if (FAILED(hr = ITaskSettings_get_Priority(settings, &ival))) + return hr; + if (FAILED(hr = write_int_value(stream, L"Priority", ival))) + return hr; + + WRITE_BOOL_OPTION(RunOnlyIfIdle); +#undef WRITE_BOOL_OPTION + + pop_indent(); + write_element_end(stream, L"Settings"); + return S_OK; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/schedsvc/Makefile.in | 2 +- dlls/schedsvc/schedsvc.c | 226 +++++++++++++++++++++++++++++++- dlls/taskschd/tests/scheduler.c | 4 - 3 files changed, 225 insertions(+), 7 deletions(-)
diff --git a/dlls/schedsvc/Makefile.in b/dlls/schedsvc/Makefile.in index 021cae468a6..ccd483edae2 100644 --- a/dlls/schedsvc/Makefile.in +++ b/dlls/schedsvc/Makefile.in @@ -1,5 +1,5 @@ MODULE = schedsvc.dll -IMPORTS = rpcrt4 advapi32 ole32 +IMPORTS = rpcrt4 advapi32 ole32 xmllite
C_SRCS = \ atsvc.c \ diff --git a/dlls/schedsvc/schedsvc.c b/dlls/schedsvc/schedsvc.c index ee200d94a6c..ba6da110fe5 100644 --- a/dlls/schedsvc/schedsvc.c +++ b/dlls/schedsvc/schedsvc.c @@ -20,7 +20,12 @@
#include <stdarg.h>
+#define COBJMACROS + #include "windef.h" +#include "initguid.h" +#include "objbase.h" +#include "xmllite.h" #include "schrpc.h" #include "taskschd.h" #include "wine/debug.h" @@ -31,6 +36,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(schedsvc);
static const char bom_utf8[] = { 0xef,0xbb,0xbf };
+struct task_info +{ + BOOL enabled; +}; + HRESULT __cdecl SchRpcHighestVersion(DWORD *version) { TRACE("%p\n", version); @@ -310,6 +320,212 @@ static HRESULT read_xml(const WCHAR *name, WCHAR **xml) return hr; }
+static HRESULT read_text_value(IXmlReader *reader, WCHAR **value) +{ + HRESULT hr; + XmlNodeType type; + + while (IXmlReader_Read(reader, &type) == S_OK) + { + switch (type) + { + case XmlNodeType_Text: + if (FAILED(hr = IXmlReader_GetValue(reader, (const WCHAR **)value, NULL))) + return hr; + TRACE("%s\n", debugstr_w(*value)); + return S_OK; + + case XmlNodeType_Whitespace: + case XmlNodeType_Comment: + break; + + default: + FIXME("unexpected node type %d\n", type); + return E_FAIL; + } + } + + return E_FAIL; +} + +static HRESULT read_variantbool_value(IXmlReader *reader, VARIANT_BOOL *vbool) +{ + WCHAR *value; + HRESULT hr; + + *vbool = VARIANT_FALSE; + + if (FAILED(hr = read_text_value(reader, &value))) + return hr; + + if (!wcscmp(value, L"true")) + { + *vbool = VARIANT_TRUE; + } + else if (wcscmp(value, L"false")) + { + WARN("unexpected bool value %s\n", debugstr_w(value)); + return SCHED_E_INVALIDVALUE; + } + + return S_OK; +} + +static HRESULT read_task_settings(IXmlReader *reader, struct task_info *info) +{ + VARIANT_BOOL bool_val; + const WCHAR *name; + XmlNodeType type; + HRESULT hr; + + if (IXmlReader_IsEmptyElement(reader)) + { + TRACE("Settings is empty.\n"); + return S_OK; + } + + while (IXmlReader_Read(reader, &type) == S_OK) + { + switch (type) + { + case XmlNodeType_EndElement: + hr = IXmlReader_GetLocalName(reader, &name, NULL); + if (hr != S_OK) return hr; + + TRACE("/%s\n", debugstr_w(name)); + + if (!wcscmp(name, L"Settings")) + return S_OK; + + break; + + case XmlNodeType_Element: + hr = IXmlReader_GetLocalName(reader, &name, NULL); + if (hr != S_OK) return hr; + + TRACE("Element: %s\n", debugstr_w(name)); + + if (!wcscmp(name, L"Enabled")) + { + if (FAILED(hr = read_variantbool_value(reader, &bool_val))) + return hr; + info->enabled = !!bool_val; + } + break; + default: + break; + } + } + + WARN("Settings was not terminated\n"); + return SCHED_E_MALFORMEDXML; +} + +static HRESULT read_task_info(IXmlReader *reader, struct task_info *info) +{ + const WCHAR *name; + XmlNodeType type; + HRESULT hr; + + if (IXmlReader_IsEmptyElement(reader)) + { + TRACE("Task is empty\n"); + return S_OK; + } + while (IXmlReader_Read(reader, &type) == S_OK) + { + switch (type) + { + case XmlNodeType_EndElement: + if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) + return hr; + + if (!wcscmp(name, L"Task")) + return S_OK; + break; + + case XmlNodeType_Element: + if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) + return hr; + + TRACE("Element: %s\n", debugstr_w(name)); + + if (!wcscmp(name, L"Settings")) + { + if (FAILED(hr = read_task_settings(reader, info))) + return hr; + } + break; + + default: + break; + } + } + + WARN("Task was not terminated\n"); + return SCHED_E_MALFORMEDXML; + +} + +static HRESULT read_task_info_from_xml(const WCHAR *xml, struct task_info *info) +{ + IXmlReader *reader; + const WCHAR *name; + XmlNodeType type; + IStream *stream; + HGLOBAL hmem; + HRESULT hr; + void *buf; + + memset(info, 0, sizeof(*info)); + + if (!(hmem = GlobalAlloc(0, wcslen(xml) * sizeof(WCHAR)))) + return E_OUTOFMEMORY; + + buf = GlobalLock(hmem); + memcpy(buf, xml, lstrlenW(xml) * sizeof(WCHAR)); + GlobalUnlock(hmem); + + if (FAILED(hr = CreateStreamOnHGlobal(hmem, TRUE, &stream))) + { + GlobalFree(hmem); + return hr; + } + + if (FAILED(hr = CreateXmlReader(&IID_IXmlReader, (void **)&reader, NULL))) + { + IStream_Release(stream); + return hr; + } + + if (FAILED(hr = IXmlReader_SetInput(reader, (IUnknown *)stream))) + goto done; + + while (IXmlReader_Read(reader, &type) == S_OK) + { + if (type != XmlNodeType_Element) continue; + if (FAILED(hr = IXmlReader_GetLocalName(reader, &name, NULL))) + goto done; + + TRACE("Element: %s\n", debugstr_w(name)); + if (wcscmp(name, L"Task")) + continue; + + hr = read_task_info(reader, info); + break; + } + +done: + IXmlReader_Release(reader); + IStream_Release(stream); + if (FAILED(hr)) + { + WARN("Failed parsing xml, hr %#lx.\n", hr); + return SCHED_E_MALFORMEDXML; + } + return S_OK; +} + HRESULT __cdecl SchRpcRetrieveTask(const WCHAR *path, const WCHAR *languages, ULONG *n_languages, WCHAR **xml) { WCHAR *full_name; @@ -685,6 +901,7 @@ HRESULT __cdecl SchRpcGetLastRunInfo(const WCHAR *path, SYSTEMTIME *last_runtime HRESULT __cdecl SchRpcGetTaskInfo(const WCHAR *path, DWORD flags, DWORD *enabled, DWORD *task_state) { WCHAR *full_name, *xml; + struct task_info info; HRESULT hr;
FIXME("%s,%#lx,%p,%p: stub\n", debugstr_w(path), flags, enabled, task_state); @@ -695,10 +912,15 @@ HRESULT __cdecl SchRpcGetTaskInfo(const WCHAR *path, DWORD flags, DWORD *enabled hr = read_xml(full_name, &xml); heap_free(full_name); if (hr != S_OK) return hr; + hr = read_task_info_from_xml(xml, &info); heap_free(xml); + if (FAILED(hr)) return hr;
- *enabled = 0; - *task_state = (flags & SCH_FLAG_STATE) ? TASK_STATE_DISABLED : TASK_STATE_UNKNOWN; + *enabled = info.enabled; + if (flags & SCH_FLAG_STATE) + *task_state = *enabled ? TASK_STATE_READY : TASK_STATE_DISABLED; + else + *task_state = TASK_STATE_UNKNOWN; return S_OK; }
diff --git a/dlls/taskschd/tests/scheduler.c b/dlls/taskschd/tests/scheduler.c index 4a97d831795..e2f985650e0 100644 --- a/dlls/taskschd/tests/scheduler.c +++ b/dlls/taskschd/tests/scheduler.c @@ -860,11 +860,9 @@ static void test_GetTask(void) SysFreeString(bstr); hr = IRegisteredTask_get_State(task2, &state); ok(hr == S_OK, "get_State error %#lx\n", hr); - todo_wine ok(state == TASK_STATE_READY, "expected TASK_STATE_READY, got %d\n", state); hr = IRegisteredTask_get_Enabled(task2, &vbool); ok(hr == S_OK, "get_Enabled error %#lx\n", hr); - todo_wine ok(vbool == VARIANT_TRUE, "expected VARIANT_TRUE, got %d\n", vbool);
IRegisteredTask_Release(task2); @@ -919,11 +917,9 @@ static void test_GetTask(void) SysFreeString(bstr); hr = IRegisteredTask_get_State(task2, &state); ok(hr == S_OK, "get_State error %#lx\n", hr); - todo_wine ok(state == TASK_STATE_READY, "expected TASK_STATE_READY, got %d\n", state); hr = IRegisteredTask_get_Enabled(task2, &vbool); ok(hr == S_OK, "get_Enabled error %#lx\n", hr); - todo_wine ok(vbool == VARIANT_TRUE, "expected VARIANT_TRUE, got %d\n", vbool);
hr = IRegisteredTask_get_State(task2, NULL);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/taskschd/regtask.c | 2 +- dlls/taskschd/tests/scheduler.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/dlls/taskschd/regtask.c b/dlls/taskschd/regtask.c index 4c0c9130c5e..69bec19027c 100644 --- a/dlls/taskschd/regtask.c +++ b/dlls/taskschd/regtask.c @@ -205,7 +205,7 @@ static HRESULT WINAPI regtask_GetInstances(IRegisteredTask *iface, LONG flags, I static HRESULT WINAPI regtask_get_LastRunTime(IRegisteredTask *iface, DATE *date) { FIXME("%p,%p: stub\n", iface, date); - return E_NOTIMPL; + return SCHED_S_TASK_HAS_NOT_RUN; }
static HRESULT WINAPI regtask_get_LastTaskResult(IRegisteredTask *iface, LONG *result) diff --git a/dlls/taskschd/tests/scheduler.c b/dlls/taskschd/tests/scheduler.c index e2f985650e0..e6efd3aa1af 100644 --- a/dlls/taskschd/tests/scheduler.c +++ b/dlls/taskschd/tests/scheduler.c @@ -744,6 +744,7 @@ static void test_GetTask(void) ITaskService *service; ITaskFolder *root, *folder; IRegisteredTask *task1, *task2; + DATE date; IID iid; int i;
@@ -840,6 +841,9 @@ static void test_GetTask(void) ok(hr == S_OK, "get_Enabled error %#lx\n", hr); ok(vbool == VARIANT_FALSE, "expected VARIANT_FALSE, got %d\n", vbool);
+ hr = IRegisteredTask_get_LastRunTime(task1, &date); + ok(hr == SCHED_S_TASK_HAS_NOT_RUN, "got %#lx\n", hr); + IRegisteredTask_Release(task1);
hr = ITaskFolder_RegisterTask(folder, Task1, xmlW, TASK_CREATE, v_null, v_null, TASK_LOGON_NONE, v_null, &task2); @@ -865,6 +869,9 @@ static void test_GetTask(void) ok(hr == S_OK, "get_Enabled error %#lx\n", hr); ok(vbool == VARIANT_TRUE, "expected VARIANT_TRUE, got %d\n", vbool);
+ hr = IRegisteredTask_get_LastRunTime(task2, &date); + ok(hr == SCHED_S_TASK_HAS_NOT_RUN, "got %#lx\n", hr); + IRegisteredTask_Release(task2);
hr = ITaskFolder_GetTask(root, NULL, &task1); @@ -891,6 +898,9 @@ static void test_GetTask(void) ok(hr == S_OK, "get_Enabled error %#lx\n", hr); ok(vbool == VARIANT_FALSE, "expected VARIANT_FALSE, got %d\n", vbool);
+ hr = IRegisteredTask_get_LastRunTime(task1, &date); + ok(hr == SCHED_S_TASK_HAS_NOT_RUN, "got %#lx\n", hr); + hr = IRegisteredTask_put_Enabled(task1, VARIANT_TRUE); ok(hr == S_OK, "put_Enabled error %#lx\n", hr); hr = IRegisteredTask_get_State(task1, &state); @@ -902,6 +912,9 @@ static void test_GetTask(void) todo_wine ok(vbool == VARIANT_TRUE, "expected VARIANT_TRUE, got %d\n", vbool);
+ hr = IRegisteredTask_get_LastRunTime(task1, &date); + ok(hr == SCHED_S_TASK_HAS_NOT_RUN, "got %#lx\n", hr); + IRegisteredTask_Release(task1);
hr = ITaskFolder_GetTask(folder, Task2, &task2); @@ -922,6 +935,9 @@ static void test_GetTask(void) ok(hr == S_OK, "get_Enabled error %#lx\n", hr); ok(vbool == VARIANT_TRUE, "expected VARIANT_TRUE, got %d\n", vbool);
+ hr = IRegisteredTask_get_LastRunTime(task2, &date); + ok(hr == SCHED_S_TASK_HAS_NOT_RUN, "got %#lx\n", hr); + hr = IRegisteredTask_get_State(task2, NULL); ok(hr == E_POINTER, "expected E_POINTER, got %#lx\n", hr); hr = IRegisteredTask_get_Enabled(task2, NULL);