Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/schedsvc/tests/Makefile.in | 2 + dlls/schedsvc/tests/atsvc.idl | 3 + dlls/schedsvc/tests/atsvcapi.c | 196 ++++++++++++++++++++++++++++++++++++++++ include/lmat.h | 28 ++++++ 4 files changed, 229 insertions(+) create mode 100644 dlls/schedsvc/tests/atsvc.idl create mode 100644 dlls/schedsvc/tests/atsvcapi.c
diff --git a/dlls/schedsvc/tests/Makefile.in b/dlls/schedsvc/tests/Makefile.in index cd56fde214..b8e7448bb7 100644 --- a/dlls/schedsvc/tests/Makefile.in +++ b/dlls/schedsvc/tests/Makefile.in @@ -2,7 +2,9 @@ TESTDLL = schedsvc.dll IMPORTS = rpcrt4 ole32
C_SRCS = \ + atsvcapi.c \ rpcapi.c
IDL_SRCS = \ + atsvc.idl \ schrpc.idl diff --git a/dlls/schedsvc/tests/atsvc.idl b/dlls/schedsvc/tests/atsvc.idl new file mode 100644 index 0000000000..61bc163a57 --- /dev/null +++ b/dlls/schedsvc/tests/atsvc.idl @@ -0,0 +1,3 @@ +#pragma makedep client + +#include "wine/atsvc.idl" diff --git a/dlls/schedsvc/tests/atsvcapi.c b/dlls/schedsvc/tests/atsvcapi.c new file mode 100644 index 0000000000..d429232913 --- /dev/null +++ b/dlls/schedsvc/tests/atsvcapi.c @@ -0,0 +1,196 @@ +/* + * Copyright 2018 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdio.h> +#include <windows.h> +#include <ole2.h> +#include <rpcdce.h> +#include <mstask.h> +#include "atsvc.h" + +#include "wine/test.h" + +/* lmat.h defines those, but other types in that file conflict + * with generated atsvc.h typedefs. + */ +#define JOB_ADD_CURRENT_DATE 0x08 +#define JOB_NONINTERACTIVE 0x10 + +extern handle_t rpc_handle; + +static int test_failures, test_skipped; + +static LONG CALLBACK rpc_exception_filter(EXCEPTION_POINTERS *ptrs) +{ + if (test_skipped) + skip("Can't connect to ATSvc service: %#x\n", ptrs->ExceptionRecord->ExceptionCode); + + if (winetest_debug) + { + fprintf(stdout, "%04x:atsvcapi: 1 tests executed (0 marked as todo, %d %s), %d skipped.\n", + GetCurrentProcessId(), test_failures, test_failures != 1 ? "failures" : "failure", test_skipped); + fflush(stdout); + } + ExitProcess(test_failures); +} + +START_TEST(atsvcapi) +{ + static unsigned char ncalrpc[] = "ncalrpc"; + static WCHAR task1W[] = { 'T','a','s','k','1','.','e','x','e',0 }; + HRESULT hr; + unsigned char *binding_str; + WCHAR server_name[MAX_COMPUTERNAME_LENGTH + 1]; + PTOP_LEVEL_EXCEPTION_FILTER old_exception_filter; + AT_ENUM_CONTAINER container; + AT_INFO info; + DWORD ret, i, total, start_index, jobid, try, try_count; + BOOL found; + + total = MAX_COMPUTERNAME_LENGTH + 1; + SetLastError(0xdeadbeef); + ret = GetComputerNameW(server_name, &total); + ok(ret, "GetComputerName error %u\n", GetLastError()); + + hr = RpcStringBindingComposeA(NULL, ncalrpc, NULL, NULL, NULL, &binding_str); + ok(hr == RPC_S_OK, "RpcStringBindingCompose error %#x\n", hr); + hr = RpcBindingFromStringBindingA(binding_str, &rpc_handle); + ok(hr == RPC_S_OK, "RpcBindingFromStringBinding error %#x\n", hr); + hr = RpcStringFreeA(&binding_str); + ok(hr == RPC_S_OK, "RpcStringFree error %#x\n", hr); + + /* widl generated RpcTryExcept/RpcExcept can't catch raised exceptions */ + old_exception_filter = SetUnhandledExceptionFilter(rpc_exception_filter); + + /* If the first call fails that's probably because the service is not running */ + test_failures = 0; + test_skipped = 1; + + memset(&info, 0, sizeof(info)); + info.Flags = JOB_ADD_CURRENT_DATE | JOB_NONINTERACTIVE; + info.Command = task1W; + jobid = 0; + ret = NetrJobAdd(server_name, &info, &jobid); + if (ret == ERROR_ACCESS_DENIED) + { + win_skip("NetrJobAdd: Access denied, skipping the tests\n"); + goto skip_tests; + } +todo_wine + ok(ret == ERROR_SUCCESS || broken(ret == ERROR_NOT_SUPPORTED) /* Win8+ */, "NetrJobAdd error %u\n", ret); + if (ret == ERROR_NOT_SUPPORTED) + { + /* FIXME: use win_skip() when todo_wine above is removed */ + skip("NetrJobAdd is not supported on this platform\n"); + goto skip_tests; + } + ok(jobid != 0, "jobid should not be 0\n"); + + /* From now on: if the call fails that's a failure */ + test_failures = 1; + test_skipped = 0; + + try_count = 5; + + for (try = 1; try <= try_count; try++) + { + container.EntriesRead = 0; + container.Buffer = NULL; + total = start_index = 0; + ret = NetrJobEnum(server_name, &container, 1, &total, &start_index); + if (ret == ERROR_ACCESS_DENIED) + { + win_skip("NetrJobEnum: Access denied, skipping the tests\n"); + goto skip_tests_delete; + } + ok(ret == ERROR_SUCCESS, "NetrJobEnum error %u (%#x)\n", ret, ret); + ok(total != 0, "total %u\n", total); + ok(start_index == 0, "start_index %u\n", start_index); + ok(container.Buffer != NULL, "Buffer %p\n", container.Buffer); + ok(container.EntriesRead != 0, "EntriesRead %u\n", container.EntriesRead); + + found = FALSE; + + for (i = 0; i < container.EntriesRead; i++) + { + AT_INFO *info2; + + trace("%u: jobid %u, command %s\n", i, container.Buffer[i].JobId, wine_dbgstr_w(container.Buffer[i].Command)); + + if (container.Buffer[i].JobId == jobid || + !lstrcmpW(container.Buffer[i].Command, task1W)) + { + found = TRUE; + trace("found %u: jobid %u, command %s\n", i, container.Buffer[i].JobId, wine_dbgstr_w(container.Buffer[i].Command)); + } + + info2 = NULL; + ret = NetrJobGetInfo(server_name, container.Buffer[i].JobId, &info2); + ok(ret == ERROR_SUCCESS, "NetrJobGetInfo error %u\n", ret); + + ok(container.Buffer[i].JobTime == info2->JobTime, "%u != %u\n", (UINT)container.Buffer[i].JobTime, (UINT)info2->JobTime); + ok(container.Buffer[i].DaysOfMonth == info2->DaysOfMonth, "%u != %u\n", container.Buffer[i].DaysOfMonth, info2->DaysOfMonth); + ok(container.Buffer[i].DaysOfWeek == info2->DaysOfWeek, "%u != %u\n", container.Buffer[i].DaysOfWeek, info2->DaysOfWeek); + ok(container.Buffer[i].Flags == info2->Flags, "%#x != %#x\n", container.Buffer[i].Flags, info2->Flags); + ok(!lstrcmpW(container.Buffer[i].Command, info2->Command), "%s != %s\n", wine_dbgstr_w(container.Buffer[i].Command), wine_dbgstr_w(info2->Command)); + + MIDL_user_free(container.Buffer[i].Command); + MIDL_user_free(info2->Command); + MIDL_user_free(info2); + } + + if (found) + break; + } + + MIDL_user_free(container.Buffer); + + ok(found, "just added jobid %u should be found\n", jobid); + +skip_tests_delete: + ret = NetrJobDel(server_name, jobid, jobid); + ok(ret == ERROR_SUCCESS, "NetrJobDel error %u\n", ret); + +skip_tests: + SetUnhandledExceptionFilter(old_exception_filter); + + hr = RpcBindingFree(&rpc_handle); + ok(hr == RPC_S_OK, "RpcBindingFree error %#x\n", hr); +} + +DECLSPEC_HIDDEN handle_t __RPC_USER ATSVC_HANDLE_bind(ATSVC_HANDLE str) +{ + static unsigned char ncalrpc[] = "ncalrpc"; + unsigned char *binding_str; + handle_t rpc_handle; + HRESULT hr; + + hr = RpcStringBindingComposeA(NULL, ncalrpc, NULL, NULL, NULL, &binding_str); + ok(hr == RPC_S_OK, "RpcStringBindingCompose error %#x\n", hr); + + hr = RpcBindingFromStringBindingA(binding_str, &rpc_handle); + ok(hr == RPC_S_OK, "RpcBindingFromStringBinding error %#x\n", hr); + + RpcStringFreeA(&binding_str); + return rpc_handle; +} + +DECLSPEC_HIDDEN void __RPC_USER ATSVC_HANDLE_unbind(ATSVC_HANDLE ServerName, handle_t rpc_handle) +{ + RpcBindingFree(&rpc_handle); +} diff --git a/include/lmat.h b/include/lmat.h index da9691a668..0fa574f41a 100644 --- a/include/lmat.h +++ b/include/lmat.h @@ -25,6 +25,34 @@ extern "C" { #endif
+#define JOB_RUN_PERIODICALLY 0x01 +#define JOB_EXEC_ERROR 0x02 +#define JOB_RUNS_TODAY 0x04 +#define JOB_ADD_CURRENT_DATE 0x08 +#define JOB_NONINTERACTIVE 0x10 + +#define JOB_INPUT_FLAGS (JOB_RUN_PERIODICALLY | JOB_ADD_CURRENT_DATE | JOB_NONINTERACTIVE) +#define JOB_OUTPUT_FLAGS (JOB_RUN_PERIODICALLY | JOB_EXEC_ERROR | JOB_RUNS_TODAY | JOB_NONINTERACTIVE) + +typedef struct _AT_INFO +{ + DWORD_PTR JobTime; + DWORD DaysOfMonth; + UCHAR DaysOfWeek; + UCHAR Flags; + LPWSTR Command; +} AT_INFO, *PAT_INFO, *LPAT_INFO; + +typedef struct _AT_ENUM +{ + DWORD JobId; + DWORD_PTR JobTime; + DWORD DaysOfMonth; + UCHAR DaysOfWeek; + UCHAR Flags; + LPWSTR Command; +} AT_ENUM, *PAT_ENUM, *LPAT_ENUM; + NET_API_STATUS WINAPI NetScheduleJobAdd(LPCWSTR,LPBYTE,LPDWORD); NET_API_STATUS WINAPI NetScheduleJobDel(LPCWSTR,DWORD,DWORD); NET_API_STATUS WINAPI NetScheduleJobEnum(LPCWSTR,LPBYTE*,DWORD,LPDWORD,LPDWORD,LPDWORD);