service.c 70.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Win32 advapi functions
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 * Copyright 1995 Sven Verdoolaege
5
 * Copyright 2005 Mike McCormack
6
 * Copyright 2007 Rolf Kalbermatter
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23 24 25
#include "config.h"
#include "wine/port.h"

26
#include <stdarg.h>
27
#include <string.h>
28
#include <time.h>
29
#include <assert.h>
30

31 32
#define NONAMELESSUNION

33 34
#include "ntstatus.h"
#define WIN32_NO_STATUS
35
#include "windef.h"
36
#include "winbase.h"
37
#include "winsvc.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
38
#include "winerror.h"
39
#include "winreg.h"
40
#include "wine/unicode.h"
41
#include "wine/debug.h"
42
#include "winternl.h"
43 44
#include "lmcons.h"
#include "lmserver.h"
45

46 47
#include "svcctl.h"

48 49
#include "advapi32_misc.h"

50 51
#include "wine/exception.h"

52
WINE_DEFAULT_DEBUG_CHANNEL(service);
53

54
void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
55
{
56
    return heap_alloc(len);
57 58 59 60
}

void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
{
61
    heap_free(ptr);
62 63
}

64 65
typedef struct service_data_t
{
66
    LPHANDLER_FUNCTION_EX handler;
67
    LPVOID context;
68
    HANDLE thread;
69
    SC_HANDLE handle;
70
    SC_HANDLE full_access_handle;
71
    BOOL unicode : 1;
72 73 74 75 76 77 78 79
    union {
        LPSERVICE_MAIN_FUNCTIONA a;
        LPSERVICE_MAIN_FUNCTIONW w;
    } proc;
    LPWSTR args;
    WCHAR name[1];
} service_data;

80 81 82 83 84 85
typedef struct dispatcher_data_t
{
    SC_HANDLE manager;
    HANDLE pipe;
} dispatcher_data;

86 87 88 89 90 91
static CRITICAL_SECTION service_cs;
static CRITICAL_SECTION_DEBUG service_cs_debug =
{
    0, 0, &service_cs,
    { &service_cs_debug.ProcessLocksList, 
      &service_cs_debug.ProcessLocksList },
92
      0, 0, { (DWORD_PTR)(__FILE__ ": service_cs") }
93
};
94
static CRITICAL_SECTION service_cs = { &service_cs_debug, -1, 0, 0, 0, 0 };
95

96 97
static service_data **services;
static unsigned int nb_services;
98
static HANDLE service_event;
99
static HANDLE stop_event;
100

101
extern HANDLE CDECL __wine_make_process_system(void);
102

103
/******************************************************************************
104 105 106
 * String management functions (same behaviour as strdup)
 * NOTE: the caller of those functions is responsible for calling HeapFree
 * in order to release the memory allocated by those functions.
107
 */
108
LPWSTR SERV_dup( LPCSTR str )
109 110 111 112 113 114 115
{
    UINT len;
    LPWSTR wstr;

    if( !str )
        return NULL;
    len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
116
    wstr = heap_alloc( len*sizeof (WCHAR) );
117 118 119 120
    MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len );
    return wstr;
}

121
static inline LPWSTR SERV_dupmulti(LPCSTR str)
122 123 124 125 126 127 128 129 130 131 132 133 134
{
    UINT len = 0, n = 0;
    LPWSTR wstr;

    if( !str )
        return NULL;
    do {
        len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 );
        n += (strlen( &str[n] ) + 1);
    } while (str[n]);
    len++;
    n++;

135
    wstr = heap_alloc( len*sizeof (WCHAR) );
136 137 138 139
    MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len );
    return wstr;
}

140 141 142 143 144 145 146 147 148 149 150 151
static inline DWORD multisz_cb(LPCWSTR wmultisz)
{
    const WCHAR *wptr = wmultisz;

    if (wmultisz == NULL)
        return 0;

    while (*wptr)
        wptr += lstrlenW(wptr)+1;
    return (wptr - wmultisz + 1)*sizeof(WCHAR);
}

152
/******************************************************************************
153
 * RPC connection with services.exe
154
 */
155
static handle_t rpc_wstr_bind(RPC_WSTR str)
156 157 158 159 160 161 162
{
    WCHAR transport[] = SVCCTL_TRANSPORT;
    WCHAR endpoint[] = SVCCTL_ENDPOINT;
    RPC_WSTR binding_str;
    RPC_STATUS status;
    handle_t rpc_handle;

163
    status = RpcStringBindingComposeW(NULL, transport, str, endpoint, NULL, &binding_str);
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    if (status != RPC_S_OK)
    {
        ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status);
        return NULL;
    }

    status = RpcBindingFromStringBindingW(binding_str, &rpc_handle);
    RpcStringFreeW(&binding_str);

    if (status != RPC_S_OK)
    {
        ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status);
        return NULL;
    }

    return rpc_handle;
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
static handle_t rpc_cstr_bind(RPC_CSTR str)
{
    RPC_CSTR transport = (RPC_CSTR)SVCCTL_TRANSPORTA;
    RPC_CSTR endpoint = (RPC_CSTR)SVCCTL_ENDPOINTA;
    RPC_CSTR binding_str;
    RPC_STATUS status;
    handle_t rpc_handle;

    status = RpcStringBindingComposeA(NULL, transport, str, endpoint, NULL, &binding_str);
    if (status != RPC_S_OK)
    {
        ERR("RpcStringBindingComposeW failed (%d)\n", (DWORD)status);
        return NULL;
    }

    status = RpcBindingFromStringBindingA(binding_str, &rpc_handle);
    RpcStringFreeA(&binding_str);

    if (status != RPC_S_OK)
    {
        ERR("Couldn't connect to services.exe: error code %u\n", (DWORD)status);
        return NULL;
    }

    return rpc_handle;
}

DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEA_bind(MACHINE_HANDLEA MachineName)
{
    return rpc_cstr_bind((RPC_CSTR)MachineName);
}

DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEA_unbind(MACHINE_HANDLEA MachineName, handle_t h)
{
    RpcBindingFree(&h);
}

DECLSPEC_HIDDEN handle_t __RPC_USER MACHINE_HANDLEW_bind(MACHINE_HANDLEW MachineName)
{
    return rpc_wstr_bind((RPC_WSTR)MachineName);
}

224
DECLSPEC_HIDDEN void __RPC_USER MACHINE_HANDLEW_unbind(MACHINE_HANDLEW MachineName, handle_t h)
225 226 227 228
{
    RpcBindingFree(&h);
}

229 230 231 232 233 234 235 236 237 238
DECLSPEC_HIDDEN handle_t __RPC_USER SVCCTL_HANDLEW_bind(SVCCTL_HANDLEW MachineName)
{
    return rpc_wstr_bind((RPC_WSTR)MachineName);
}

DECLSPEC_HIDDEN void __RPC_USER SVCCTL_HANDLEW_unbind(SVCCTL_HANDLEW MachineName, handle_t h)
{
    RpcBindingFree(&h);
}

239 240 241 242 243 244 245 246 247 248
static LONG WINAPI rpc_filter(EXCEPTION_POINTERS *eptr)
{
    return I_RpcExceptionFilter(eptr->ExceptionRecord->ExceptionCode);
}

static DWORD map_exception_code(DWORD exception_code)
{
    switch (exception_code)
    {
    case RPC_X_NULL_REF_POINTER:
249
        return ERROR_INVALID_ADDRESS;
250 251 252
    case RPC_X_ENUM_VALUE_OUT_OF_RANGE:
    case RPC_X_BYTE_COUNT_TOO_SMALL:
        return ERROR_INVALID_PARAMETER;
253 254 255
    case RPC_S_INVALID_BINDING:
    case RPC_X_SS_IN_NULL_CONTEXT:
        return ERROR_INVALID_HANDLE;
256 257 258 259 260
    default:
        return exception_code;
    }
}

261
/******************************************************************************
262
 * Service IPC functions
263
 */
264
static LPWSTR service_get_pipe_name(void)
265
{
266 267 268 269 270 271
    static const WCHAR format[] = { '\\','\\','.','\\','p','i','p','e','\\',
        'n','e','t','\\','N','t','C','o','n','t','r','o','l','P','i','p','e','%','u',0};
    static const WCHAR service_current_key_str[] = { 'S','Y','S','T','E','M','\\',
        'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
        'C','o','n','t','r','o','l','\\',
        'S','e','r','v','i','c','e','C','u','r','r','e','n','t',0};
272 273
    LPWSTR name;
    DWORD len;
274 275 276 277
    HKEY service_current_key;
    DWORD service_current;
    LONG ret;
    DWORD type;
278

279 280 281 282 283 284 285 286 287 288 289
    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_current_key_str, 0,
        KEY_QUERY_VALUE, &service_current_key);
    if (ret != ERROR_SUCCESS)
        return NULL;
    len = sizeof(service_current);
    ret = RegQueryValueExW(service_current_key, NULL, NULL, &type,
        (BYTE *)&service_current, &len);
    RegCloseKey(service_current_key);
    if (ret != ERROR_SUCCESS || type != REG_DWORD)
        return NULL;
    len = sizeof(format)/sizeof(WCHAR) + 10 /* strlenW("4294967295") */;
290
    name = heap_alloc(len * sizeof(WCHAR));
291 292 293
    if (!name)
        return NULL;
    snprintfW(name, len, format, service_current);
294 295 296
    return name;
}

297
static HANDLE service_open_pipe(void)
298
{
299
    LPWSTR szPipe = service_get_pipe_name();
300 301 302 303 304 305 306 307 308
    HANDLE handle = INVALID_HANDLE_VALUE;

    do {
        handle = CreateFileW(szPipe, GENERIC_READ|GENERIC_WRITE,
                         0, NULL, OPEN_ALWAYS, 0, NULL);
        if (handle != INVALID_HANDLE_VALUE)
            break;
        if (GetLastError() != ERROR_PIPE_BUSY)
            break;
309
    } while (WaitNamedPipeW(szPipe, NMPWAIT_USE_DEFAULT_WAIT));
310
    heap_free(szPipe);
311 312 313 314

    return handle;
}

315 316 317 318 319 320 321 322 323 324 325
static service_data *find_service_by_name( const WCHAR *name )
{
    unsigned int i;

    if (nb_services == 1)  /* only one service (FIXME: should depend on OWN_PROCESS etc.) */
        return services[0];
    for (i = 0; i < nb_services; i++)
        if (!strcmpiW( name, services[i]->name )) return services[i];
    return NULL;
}

326
/******************************************************************************
327
 * service_thread
328
 *
329
 * Call into the main service routine provided by StartServiceCtrlDispatcher.
330
 */
331
static DWORD WINAPI service_thread(LPVOID arg)
332
{
333 334 335
    service_data *info = arg;
    LPWSTR str = info->args;
    DWORD argc = 0, len = 0;
336

337 338 339 340 341 342 343
    TRACE("%p\n", arg);

    while (str[len])
    {
        len += strlenW(&str[len]) + 1;
        argc++;
    }
344
    len++;
345

346 347 348
    if (info->unicode)
    {
        LPWSTR *argv, p;
349

350
        argv = heap_alloc((argc+1)*sizeof(LPWSTR));
351 352 353 354 355
        for (argc=0, p=str; *p; p += strlenW(p) + 1)
            argv[argc++] = p;
        argv[argc] = NULL;

        info->proc.w(argc, argv);
356
        heap_free(argv);
357
    }
358
    else
359 360 361 362 363
    {
        LPSTR strA, *argv, p;
        DWORD lenA;
        
        lenA = WideCharToMultiByte(CP_ACP,0, str, len, NULL, 0, NULL, NULL);
364
        strA = heap_alloc(lenA);
365 366
        WideCharToMultiByte(CP_ACP,0, str, len, strA, lenA, NULL, NULL);

367
        argv = heap_alloc((argc+1)*sizeof(LPSTR));
368 369 370 371 372
        for (argc=0, p=strA; *p; p += strlen(p) + 1)
            argv[argc++] = p;
        argv[argc] = NULL;

        info->proc.a(argc, argv);
373 374
        heap_free(argv);
        heap_free(strA);
375 376
    }
    return 0;
377 378
}

379
/******************************************************************************
380
 * service_handle_start
381
 */
382
static DWORD service_handle_start(service_data *service, const WCHAR *data, DWORD count)
383
{
384
    TRACE("%s argsize %u\n", debugstr_w(service->name), count);
385 386 387

    if (service->thread)
    {
388
        WARN("service is not stopped\n");
389
        return ERROR_SERVICE_ALREADY_RUNNING;
390 391
    }

392 393
    heap_free(service->args);
    service->args = heap_alloc(count * sizeof(WCHAR));
394
    memcpy( service->args, data, count * sizeof(WCHAR) );
395 396
    service->thread = CreateThread( NULL, 0, service_thread,
                                    service, 0, NULL );
397
    SetEvent( service_event );  /* notify the main loop */
398
    return 0;
399 400
}

401
/******************************************************************************
402
 * service_handle_control
403
 */
404
static DWORD service_handle_control(const service_data *service, DWORD dwControl)
405
{
406 407 408
    DWORD ret = ERROR_INVALID_SERVICE_CONTROL;

    TRACE("%s control %u\n", debugstr_w(service->name), dwControl);
409

410 411
    if (service->handler)
        ret = service->handler(dwControl, 0, NULL, service->context);
412
    return ret;
413 414 415 416 417 418 419
}

/******************************************************************************
 * service_control_dispatcher
 */
static DWORD WINAPI service_control_dispatcher(LPVOID arg)
{
420
    dispatcher_data *disp = arg;
421

422
    /* dispatcher loop */
423
    while (1)
424
    {
425 426 427
        service_data *service;
        service_start_info info;
        WCHAR *data = NULL;
428
        BOOL r;
429
        DWORD data_size = 0, count, result;
430

431
        r = ReadFile( disp->pipe, &info, FIELD_OFFSET(service_start_info,data), &count, NULL );
432
        if (!r)
433
        {
434 435 436 437
            if (GetLastError() != ERROR_BROKEN_PIPE)
                ERR( "pipe read failed error %u\n", GetLastError() );
            break;
        }
438
        if (count != FIELD_OFFSET(service_start_info,data))
439 440
        {
            ERR( "partial pipe read %u\n", count );
441 442
            break;
        }
443 444 445
        if (count < info.total_size)
        {
            data_size = info.total_size - FIELD_OFFSET(service_start_info,data);
446
            data = heap_alloc( data_size );
447
            r = ReadFile( disp->pipe, data, data_size, &count, NULL );
448 449 450 451
            if (!r)
            {
                if (GetLastError() != ERROR_BROKEN_PIPE)
                    ERR( "pipe read failed error %u\n", GetLastError() );
452
                heap_free( data );
453 454 455 456 457
                break;
            }
            if (count != data_size)
            {
                ERR( "partial pipe read %u/%u\n", count, data_size );
458
                heap_free( data );
459 460 461 462 463 464 465 466 467 468 469 470 471 472
                break;
            }
        }

        /* find the service */

        if (!(service = find_service_by_name( data )))
        {
            FIXME( "got request %u for unknown service %s\n", info.cmd, debugstr_w(data));
            result = ERROR_INVALID_PARAMETER;
            goto done;
        }

        TRACE( "got request %u for service %s\n", info.cmd, debugstr_w(data) );
473

474
        /* handle the request */
475
        switch (info.cmd)
476
        {
477
        case WINESERV_STARTINFO:
478 479
            if (!service->handle)
            {
480 481 482
                if (!(service->handle = OpenServiceW( disp->manager, data, SERVICE_SET_STATUS )) ||
                    !(service->full_access_handle = OpenServiceW( disp->manager, data,
                            GENERIC_READ|GENERIC_WRITE )))
483 484
                    FIXME( "failed to open service %s\n", debugstr_w(data) );
            }
485
            result = service_handle_start(service, data, data_size / sizeof(WCHAR));
486 487
            break;
        case WINESERV_SENDCONTROL:
488
            result = service_handle_control(service, info.control);
489 490
            break;
        default:
491 492 493
            ERR("received invalid command %u\n", info.cmd);
            result = ERROR_INVALID_PARAMETER;
            break;
494
        }
495 496

    done:
497
        WriteFile( disp->pipe, &result, sizeof(result), &count, NULL );
498
        heap_free( data );
499 500
    }

501 502
    CloseHandle( disp->pipe );
    CloseServiceHandle( disp->manager );
503
    heap_free( disp );
504
    return 1;
505 506
}

507
/******************************************************************************
508
 * service_run_main_thread
Alexandre Julliard's avatar
Alexandre Julliard committed
509
 */
510
static BOOL service_run_main_thread(void)
511
{
512 513 514
    DWORD i, n, ret;
    HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
    UINT wait_services[MAXIMUM_WAIT_OBJECTS];
515
    dispatcher_data *disp = heap_alloc( sizeof(*disp) );
516 517 518 519 520

    disp->manager = OpenSCManagerW( NULL, NULL, SC_MANAGER_CONNECT );
    if (!disp->manager)
    {
        ERR("failed to open service manager error %u\n", GetLastError());
521
        heap_free( disp );
522 523 524 525 526 527 528 529
        return FALSE;
    }

    disp->pipe = service_open_pipe();
    if (disp->pipe == INVALID_HANDLE_VALUE)
    {
        WARN("failed to create control pipe error %u\n", GetLastError());
        CloseServiceHandle( disp->manager );
530
        heap_free( disp );
531 532 533
        SetLastError( ERROR_FAILED_SERVICE_CONTROLLER_CONNECT );
        return FALSE;
    }
534

535
    service_event = CreateEventW( NULL, FALSE, FALSE, NULL );
536
    stop_event = CreateEventW( NULL, FALSE, FALSE, NULL );
537

538
    /* FIXME: service_control_dispatcher should be merged into the main thread */
539
    wait_handles[0] = __wine_make_process_system();
540
    wait_handles[1] = CreateThread( NULL, 0, service_control_dispatcher, disp, 0, NULL );
541
    wait_handles[2] = service_event;
542
    wait_handles[3] = stop_event;
543

544
    TRACE("Starting %d services running as process %d\n",
545
          nb_services, GetCurrentProcessId());
546 547

    /* wait for all the threads to pack up and exit */
548
    for (;;)
549
    {
550
        EnterCriticalSection( &service_cs );
551
        for (i = 0, n = 4; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
552 553 554 555 556 557 558 559
        {
            if (!services[i]->thread) continue;
            wait_services[n] = i;
            wait_handles[n++] = services[i]->thread;
        }
        LeaveCriticalSection( &service_cs );

        ret = WaitForMultipleObjects( n, wait_handles, FALSE, INFINITE );
560 561
        if (!ret)  /* system process event */
        {
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
            SERVICE_STATUS st;
            SERVICE_PRESHUTDOWN_INFO spi;
            DWORD timeout = 5000;
            BOOL res;

            EnterCriticalSection( &service_cs );
            n = 0;
            for (i = 0; i < nb_services && n < MAXIMUM_WAIT_OBJECTS; i++)
            {
                if (!services[i]->thread) continue;

                res = QueryServiceStatus(services[i]->full_access_handle, &st);
                ret = ERROR_SUCCESS;
                if (res && (st.dwControlsAccepted & SERVICE_ACCEPT_PRESHUTDOWN))
                {
                    res = QueryServiceConfig2W( services[i]->full_access_handle, SERVICE_CONFIG_PRESHUTDOWN_INFO,
                            (LPBYTE)&spi, sizeof(spi), &i );
                    if (res)
                    {
                        FIXME("service should be able to delay shutdown\n");
                        timeout += spi.dwPreshutdownTimeout;
                        ret = service_handle_control( services[i], SERVICE_CONTROL_PRESHUTDOWN );
                        wait_handles[n++] = services[i]->thread;
                    }
                }
                else if (res && (st.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN))
                {
                    ret = service_handle_control( services[i], SERVICE_CONTROL_SHUTDOWN );
                    wait_handles[n++] = services[i]->thread;
                }
            }
            LeaveCriticalSection( &service_cs );

            TRACE("last user process exited, shutting down (timeout: %d)\n", timeout);
            WaitForMultipleObjects( n, wait_handles, TRUE, timeout );
597 598
            ExitProcess(0);
        }
599
        else if (ret == 1)
600 601 602 603 604 605
        {
            TRACE( "control dispatcher exited, shutting down\n" );
            /* FIXME: we should maybe send a shutdown control to running services */
            ExitProcess(0);
        }
        else if (ret == 2)
606
        {
607
            continue;  /* rebuild the list */
608
        }
609 610 611 612
        else if (ret == 3)
        {
            return TRUE;
        }
613 614 615 616 617 618
        else if (ret < n)
        {
            services[wait_services[ret]]->thread = 0;
            CloseHandle( wait_handles[ret] );
        }
        else return FALSE;
619
    }
620 621 622 623 624
}

/******************************************************************************
 * StartServiceCtrlDispatcherA [ADVAPI32.@]
 *
625
 * See StartServiceCtrlDispatcherW.
626
 */
627
BOOL WINAPI StartServiceCtrlDispatcherA( const SERVICE_TABLE_ENTRYA *servent )
628 629
{
    service_data *info;
630
    unsigned int i;
631 632 633

    TRACE("%p\n", servent);

634
    if (nb_services)
635
    {
636 637 638 639
        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
        return FALSE;
    }
    while (servent[nb_services].lpServiceName) nb_services++;
640 641 642 643 644 645
    if (!nb_services)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

646
    services = heap_alloc( nb_services * sizeof(*services) );
647

648 649 650 651
    for (i = 0; i < nb_services; i++)
    {
        DWORD len = MultiByteToWideChar(CP_ACP, 0, servent[i].lpServiceName, -1, NULL, 0);
        DWORD sz = FIELD_OFFSET( service_data, name[len] );
652
        info = heap_alloc_zero( sz );
653 654
        MultiByteToWideChar(CP_ACP, 0, servent[i].lpServiceName, -1, info->name, len);
        info->proc.a = servent[i].lpServiceProc;
655
        info->unicode = FALSE;
656
        services[i] = info;
657
    }
658

659
    return service_run_main_thread();
660
}
661

662 663 664
/******************************************************************************
 * StartServiceCtrlDispatcherW [ADVAPI32.@]
 *
665 666 667
 *  Connects a process containing one or more services to the service control
 * manager.
 *
668
 * PARAMS
669
 *   servent [I]  A list of the service names and service procedures
670 671 672 673
 *
 * RETURNS
 *  Success: TRUE.
 *  Failure: FALSE.
674
 */
675
BOOL WINAPI StartServiceCtrlDispatcherW( const SERVICE_TABLE_ENTRYW *servent )
676
{
677
    service_data *info;
678
    unsigned int i;
679 680 681

    TRACE("%p\n", servent);

682
    if (nb_services)
683
    {
684 685 686 687
        SetLastError( ERROR_SERVICE_ALREADY_RUNNING );
        return FALSE;
    }
    while (servent[nb_services].lpServiceName) nb_services++;
688 689 690 691 692 693
    if (!nb_services)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

694
    services = heap_alloc( nb_services * sizeof(*services) );
695

696 697 698 699
    for (i = 0; i < nb_services; i++)
    {
        DWORD len = strlenW(servent[i].lpServiceName) + 1;
        DWORD sz = FIELD_OFFSET( service_data, name[len] );
700
        info = heap_alloc_zero( sz );
701 702
        strcpyW(info->name, servent[i].lpServiceName);
        info->proc.w = servent[i].lpServiceProc;
703
        info->unicode = TRUE;
704
        services[i] = info;
705 706
    }

707
    return service_run_main_thread();
Alexandre Julliard's avatar
Alexandre Julliard committed
708 709
}

710 711 712
/******************************************************************************
 * LockServiceDatabase  [ADVAPI32.@]
 */
713
SC_LOCK WINAPI LockServiceDatabase (SC_HANDLE hSCManager)
714
{
715
    SC_RPC_LOCK hLock = NULL;
716
    DWORD err;
717 718 719

    TRACE("%p\n",hSCManager);

720 721
    __TRY
    {
722
        err = svcctl_LockServiceDatabase(hSCManager, &hLock);
723 724 725 726 727 728
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
729 730 731 732 733 734
    if (err != ERROR_SUCCESS)
    {
        SetLastError(err);
        return NULL;
    }
    return hLock;
735 736 737 738 739
}

/******************************************************************************
 * UnlockServiceDatabase  [ADVAPI32.@]
 */
740
BOOL WINAPI UnlockServiceDatabase (SC_LOCK ScLock)
741
{
742 743 744
    DWORD err;
    SC_RPC_LOCK hRpcLock = ScLock;

745 746
    TRACE("%p\n",ScLock);

747 748 749 750 751 752 753 754 755
    __TRY
    {
        err = svcctl_UnlockServiceDatabase(&hRpcLock);
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
756 757 758 759 760 761
    if (err != ERROR_SUCCESS)
    {
        SetLastError(err);
        return FALSE;
    }
    return TRUE;
762 763
}

764
/******************************************************************************
765
 * SetServiceStatus [ADVAPI32.@]
766 767 768 769
 *
 * PARAMS
 *   hService []
 *   lpStatus []
770
 */
771
BOOL WINAPI
772
SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
773
{
774
    DWORD err;
775

776
    TRACE("%p %x %x %x %x %x %x %x\n", hService,
777 778 779 780 781
          lpStatus->dwServiceType, lpStatus->dwCurrentState,
          lpStatus->dwControlsAccepted, lpStatus->dwWin32ExitCode,
          lpStatus->dwServiceSpecificExitCode, lpStatus->dwCheckPoint,
          lpStatus->dwWaitHint);

782 783
    __TRY
    {
784
        err = svcctl_SetServiceStatus( hService, lpStatus );
785 786 787 788 789 790
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
791 792 793 794 795 796
    if (err != ERROR_SUCCESS)
    {
        SetLastError(err);
        return FALSE;
    }

797 798
    if (lpStatus->dwCurrentState == SERVICE_STOPPED) {
        SetEvent(stop_event);
799
        CloseServiceHandle((SC_HANDLE)hService);
800
    }
801 802

    return TRUE;
803
}
Alexandre Julliard's avatar
Alexandre Julliard committed
804

805

Alexandre Julliard's avatar
Alexandre Julliard committed
806
/******************************************************************************
807
 * OpenSCManagerA [ADVAPI32.@]
Jon Griffiths's avatar
Jon Griffiths committed
808 809 810 811 812 813 814 815 816 817 818
 *
 * Establish a connection to the service control manager and open its database.
 *
 * PARAMS
 *   lpMachineName   [I] Pointer to machine name string
 *   lpDatabaseName  [I] Pointer to database name string
 *   dwDesiredAccess [I] Type of access
 *
 * RETURNS
 *   Success: A Handle to the service control manager database
 *   Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
819
 */
Jon Griffiths's avatar
Jon Griffiths committed
820 821
SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName,
                                 DWORD dwDesiredAccess )
822
{
823
    LPWSTR machineW, databaseW;
824 825
    SC_HANDLE ret;

826 827 828 829 830
    machineW = SERV_dup(lpMachineName);
    databaseW = SERV_dup(lpDatabaseName);
    ret = OpenSCManagerW(machineW, databaseW, dwDesiredAccess);
    heap_free(databaseW);
    heap_free(machineW);
Alexandre Julliard's avatar
Alexandre Julliard committed
831
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
832 833
}

Alexandre Julliard's avatar
Alexandre Julliard committed
834
/******************************************************************************
835
 * OpenSCManagerW [ADVAPI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
836
 *
Jon Griffiths's avatar
Jon Griffiths committed
837
 * See OpenSCManagerA.
Alexandre Julliard's avatar
Alexandre Julliard committed
838
 */
839 840
DWORD SERV_OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
                           DWORD dwDesiredAccess, SC_HANDLE *handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
841
{
842
    DWORD r;
843

844
    TRACE("(%s,%s,0x%08x)\n", debugstr_w(lpMachineName),
Alexandre Julliard's avatar
Alexandre Julliard committed
845
          debugstr_w(lpDatabaseName), dwDesiredAccess);
846

847 848
    __TRY
    {
849
        r = svcctl_OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess, (SC_RPC_HANDLE *)handle);
850 851 852 853 854 855
    }
    __EXCEPT(rpc_filter)
    {
        r = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
856 857

    if (r!=ERROR_SUCCESS)
858 859 860 861 862
        *handle = 0;

    TRACE("returning %p\n", *handle);
    return r;
}
863

864 865 866 867 868 869 870 871 872
SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
                                 DWORD dwDesiredAccess )
{
    SC_HANDLE handle = 0;
    DWORD r;

    r = SERV_OpenSCManagerW(lpMachineName, lpDatabaseName, dwDesiredAccess, &handle);
    if (r!=ERROR_SUCCESS)
        SetLastError(r);
873
    return handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
874 875
}

Alexandre Julliard's avatar
Alexandre Julliard committed
876
/******************************************************************************
877
 * ControlService [ADVAPI32.@]
Jon Griffiths's avatar
Jon Griffiths committed
878 879
 *
 * Send a control code to a service.
Alexandre Julliard's avatar
Alexandre Julliard committed
880
 *
881
 * PARAMS
Jon Griffiths's avatar
Jon Griffiths committed
882 883 884
 *   hService        [I] Handle of the service control manager database
 *   dwControl       [I] Control code to send (SERVICE_CONTROL_* flags from "winsvc.h")
 *   lpServiceStatus [O] Destination for the status of the service, if available
885
 *
Jon Griffiths's avatar
Jon Griffiths committed
886 887 888
 * RETURNS
 *   Success: TRUE.
 *   Failure: FALSE.
889 890 891 892
 *
 * BUGS
 *   Unlike M$' implementation, control requests are not serialized and may be
 *   processed asynchronously.
Alexandre Julliard's avatar
Alexandre Julliard committed
893
 */
Jon Griffiths's avatar
Jon Griffiths committed
894 895
BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
                            LPSERVICE_STATUS lpServiceStatus )
Alexandre Julliard's avatar
Alexandre Julliard committed
896
{
897
    DWORD err;
898

899
    TRACE("%p %d %p\n", hService, dwControl, lpServiceStatus);
900

901 902
    __TRY
    {
903
        err = svcctl_ControlService(hService, dwControl, lpServiceStatus);
904 905 906 907 908 909
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
910
    if (err != ERROR_SUCCESS)
911
    {
912 913
        SetLastError(err);
        return FALSE;
914 915
    }

916
    return TRUE;
917
}
Alexandre Julliard's avatar
Alexandre Julliard committed
918 919

/******************************************************************************
920
 * CloseServiceHandle [ADVAPI32.@]
Jon Griffiths's avatar
Jon Griffiths committed
921 922
 * 
 * Close a handle to a service or the service control manager database.
Alexandre Julliard's avatar
Alexandre Julliard committed
923 924
 *
 * PARAMS
925
 *   hSCObject [I] Handle to service or service control manager database
Alexandre Julliard's avatar
Alexandre Julliard committed
926
 *
Jon Griffiths's avatar
Jon Griffiths committed
927 928 929
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
930
 */
931
BOOL WINAPI
932
CloseServiceHandle( SC_HANDLE hSCObject )
Alexandre Julliard's avatar
Alexandre Julliard committed
933
{
934 935
    DWORD err;

936
    TRACE("%p\n", hSCObject);
937

938 939
    __TRY
    {
940
        err = svcctl_CloseServiceHandle((SC_RPC_HANDLE *)&hSCObject);
941 942 943 944 945 946
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
947

948 949 950 951 952
    if (err != ERROR_SUCCESS)
    {
        SetLastError(err);
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
953 954 955 956 957
    return TRUE;
}


/******************************************************************************
958
 * OpenServiceA [ADVAPI32.@]
Jon Griffiths's avatar
Jon Griffiths committed
959 960 961 962 963 964 965 966 967 968 969
 *
 * Open a handle to a service.
 *
 * PARAMS
 *   hSCManager      [I] Handle of the service control manager database
 *   lpServiceName   [I] Name of the service to open
 *   dwDesiredAccess [I] Access required to the service
 *
 * RETURNS
 *    Success: Handle to the service
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
970
 */
Jon Griffiths's avatar
Jon Griffiths committed
971 972
SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
                               DWORD dwDesiredAccess )
Alexandre Julliard's avatar
Alexandre Julliard committed
973
{
974
    LPWSTR lpServiceNameW;
975
    SC_HANDLE ret;
976

977
    TRACE("%p %s 0x%08x\n", hSCManager, debugstr_a(lpServiceName), dwDesiredAccess);
978 979 980

    lpServiceNameW = SERV_dup(lpServiceName);
    ret = OpenServiceW( hSCManager, lpServiceNameW, dwDesiredAccess);
981
    heap_free(lpServiceNameW);
Alexandre Julliard's avatar
Alexandre Julliard committed
982 983 984 985 986
    return ret;
}


/******************************************************************************
987
 * OpenServiceW [ADVAPI32.@]
988
 *
Jon Griffiths's avatar
Jon Griffiths committed
989
 * See OpenServiceA.
Alexandre Julliard's avatar
Alexandre Julliard committed
990
 */
991 992
DWORD SERV_OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
                         DWORD dwDesiredAccess, SC_HANDLE *handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
993
{
994
    DWORD err;
995

996
    TRACE("%p %s 0x%08x\n", hSCManager, debugstr_w(lpServiceName), dwDesiredAccess);
997

998
    if (!hSCManager)
999
        return ERROR_INVALID_HANDLE;
1000

1001 1002
    __TRY
    {
1003
        err = svcctl_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess, (SC_RPC_HANDLE *)handle);
1004 1005 1006 1007 1008 1009
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
1010 1011

    if (err != ERROR_SUCCESS)
1012
        *handle = 0;
1013

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    TRACE("returning %p\n", *handle);
    return err;
}

SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
                               DWORD dwDesiredAccess)
{
    SC_HANDLE handle = 0;
    DWORD err;

    err = SERV_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess, &handle);
    if (err != ERROR_SUCCESS)
        SetLastError(err);
1027
    return handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
1028 1029
}

1030
/******************************************************************************
1031
 * CreateServiceW [ADVAPI32.@]
1032 1033
 */
SC_HANDLE WINAPI
1034
CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
1035 1036
                  LPCWSTR lpDisplayName, DWORD dwDesiredAccess,
                  DWORD dwServiceType, DWORD dwStartType,
1037
                  DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
1038 1039
                  LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
                  LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
1040
                  LPCWSTR lpPassword )
Alexandre Julliard's avatar
Alexandre Julliard committed
1041
{
1042
    SC_HANDLE handle = 0;
1043
    DWORD err;
1044
    SIZE_T passwdlen;
1045 1046

    TRACE("%p %s %s\n", hSCManager, 
1047 1048
          debugstr_w(lpServiceName), debugstr_w(lpDisplayName));

1049
    if (!hSCManager)
1050 1051
    {
        SetLastError( ERROR_INVALID_HANDLE );
1052
        return 0;
1053 1054
    }

1055 1056 1057 1058
    if (lpPassword)
        passwdlen = (strlenW(lpPassword) + 1) * sizeof(WCHAR);
    else
        passwdlen = 0;
1059

1060 1061
    __TRY
    {
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
        BOOL is_wow64;

        IsWow64Process(GetCurrentProcess(), &is_wow64);

        if (is_wow64)
            err = svcctl_CreateServiceWOW64W(hSCManager, lpServiceName,
                    lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
                    lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (const BYTE*)lpDependencies,
                    multisz_cb(lpDependencies), lpServiceStartName, (const BYTE*)lpPassword, passwdlen,
                    (SC_RPC_HANDLE *)&handle);
        else
            err = svcctl_CreateServiceW(hSCManager, lpServiceName,
                    lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
                    lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, (const BYTE*)lpDependencies,
                    multisz_cb(lpDependencies), lpServiceStartName, (const BYTE*)lpPassword, passwdlen,
                    (SC_RPC_HANDLE *)&handle);
1078 1079 1080 1081 1082 1083
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
1084

1085
    if (err != ERROR_SUCCESS)
1086
    {
1087
        SetLastError(err);
1088
        handle = 0;
1089
    }
1090
    return handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
1091 1092 1093
}


1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
/******************************************************************************
 * CreateServiceA [ADVAPI32.@]
 */
SC_HANDLE WINAPI
CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
                  LPCSTR lpDisplayName, DWORD dwDesiredAccess,
                  DWORD dwServiceType, DWORD dwStartType,
                  DWORD dwErrorControl, LPCSTR lpBinaryPathName,
                  LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
                  LPCSTR lpDependencies, LPCSTR lpServiceStartName,
                  LPCSTR lpPassword )
{
    LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW,
        lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW;
    SC_HANDLE r;

    TRACE("%p %s %s\n", hSCManager,
          debugstr_a(lpServiceName), debugstr_a(lpDisplayName));

    lpServiceNameW = SERV_dup( lpServiceName );
    lpDisplayNameW = SERV_dup( lpDisplayName );
    lpBinaryPathNameW = SERV_dup( lpBinaryPathName );
    lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup );
    lpDependenciesW = SERV_dupmulti( lpDependencies );
    lpServiceStartNameW = SERV_dup( lpServiceStartName );
    lpPasswordW = SERV_dup( lpPassword );

    r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW,
            dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
            lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId,
            lpDependenciesW, lpServiceStartNameW, lpPasswordW );

1126 1127 1128 1129 1130 1131 1132
    heap_free( lpServiceNameW );
    heap_free( lpDisplayNameW );
    heap_free( lpBinaryPathNameW );
    heap_free( lpLoadOrderGroupW );
    heap_free( lpDependenciesW );
    heap_free( lpServiceStartNameW );
    heap_free( lpPasswordW );
1133 1134 1135 1136 1137

    return r;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1138
/******************************************************************************
1139
 * DeleteService [ADVAPI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
1140
 *
Jon Griffiths's avatar
Jon Griffiths committed
1141
 * Delete a service from the service control manager database.
Alexandre Julliard's avatar
Alexandre Julliard committed
1142
 *
Jon Griffiths's avatar
Jon Griffiths committed
1143 1144
 * PARAMS
 *    hService [I] Handle of the service to delete
Alexandre Julliard's avatar
Alexandre Julliard committed
1145
 *
Jon Griffiths's avatar
Jon Griffiths committed
1146 1147 1148
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
1149
 */
Jon Griffiths's avatar
Jon Griffiths committed
1150
BOOL WINAPI DeleteService( SC_HANDLE hService )
Alexandre Julliard's avatar
Alexandre Julliard committed
1151
{
1152
    DWORD err;
1153

1154 1155
    TRACE("%p\n", hService);

1156 1157
    __TRY
    {
1158
        err = svcctl_DeleteService(hService);
1159 1160 1161 1162 1163 1164
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
1165
    if (err != 0)
1166
    {
1167
        SetLastError(err);
1168 1169 1170
        return FALSE;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1171 1172 1173 1174 1175
    return TRUE;
}


/******************************************************************************
1176
 * StartServiceA [ADVAPI32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
1177
 *
Jon Griffiths's avatar
Jon Griffiths committed
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
 * Start a service
 *
 * PARAMS
 *   hService            [I] Handle of service
 *   dwNumServiceArgs    [I] Number of arguments
 *   lpServiceArgVectors [I] Address of array of argument strings
 *
 * NOTES
 *  - NT implements this function using an obscure RPC call.
 *  - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc
 *    to get things like "%SystemRoot%\\System32\\service.exe" to load.
 *  - This will only work for shared address space. How should the service
 *    args be transferred when address spaces are separated?
 *  - Can only start one service at a time.
 *  - Has no concept of privilege.
 *
 * RETURNS
 *   Success: TRUE.
 *   Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
1197
 */
1198 1199
BOOL WINAPI StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
                           LPCSTR *lpServiceArgVectors )
Alexandre Julliard's avatar
Alexandre Julliard committed
1200
{
1201
    LPWSTR *lpwstr=NULL;
1202
    unsigned int i;
1203
    BOOL r;
1204

1205
    TRACE("(%p,%d,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
1206

1207
    if (dwNumServiceArgs)
1208
        lpwstr = heap_alloc( dwNumServiceArgs*sizeof(LPWSTR) );
1209 1210

    for(i=0; i<dwNumServiceArgs; i++)
1211
        lpwstr[i]=SERV_dup(lpServiceArgVectors[i]);
1212

1213
    r = StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
1214

1215
    if (dwNumServiceArgs)
1216 1217
    {
        for(i=0; i<dwNumServiceArgs; i++)
1218 1219
            heap_free(lpwstr[i]);
        heap_free(lpwstr);
1220 1221
    }

1222
    return r;
Alexandre Julliard's avatar
Alexandre Julliard committed
1223 1224
}

1225

1226 1227 1228 1229 1230 1231 1232 1233
/******************************************************************************
 * StartServiceW [ADVAPI32.@]
 * 
 * See StartServiceA.
 */
BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs,
                          LPCWSTR *lpServiceArgVectors)
{
1234
    DWORD err;
1235

1236
    TRACE("%p %d %p\n", hService, dwNumServiceArgs, lpServiceArgVectors);
1237

1238 1239
    __TRY
    {
1240
        err = svcctl_StartServiceW(hService, dwNumServiceArgs, lpServiceArgVectors);
1241 1242 1243 1244 1245 1246
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
1247
    if (err != ERROR_SUCCESS)
1248
    {
1249 1250
        SetLastError(err);
        return FALSE;
1251 1252
    }

1253
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1254 1255
}

1256
/******************************************************************************
1257
 * QueryServiceStatus [ADVAPI32.@]
1258 1259
 *
 * PARAMS
1260 1261
 *   hService        [I] Handle to service to get information about
 *   lpservicestatus [O] buffer to receive the status information for the service
1262
 *
1263
 */
1264 1265
BOOL WINAPI QueryServiceStatus(SC_HANDLE hService,
                               LPSERVICE_STATUS lpservicestatus)
1266
{
1267 1268
    SERVICE_STATUS_PROCESS SvcStatusData;
    BOOL ret;
1269
    DWORD dummy;
1270 1271

    TRACE("%p %p\n", hService, lpservicestatus);
1272

1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
    if (!hService)
    {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }
    if (!lpservicestatus)
    {
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

1284
    ret = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&SvcStatusData,
1285
                                sizeof(SERVICE_STATUS_PROCESS), &dummy);
1286
    if (ret) memcpy(lpservicestatus, &SvcStatusData, sizeof(SERVICE_STATUS)) ;
1287
    return ret;
1288
}
1289

1290

1291 1292 1293
/******************************************************************************
 * QueryServiceStatusEx [ADVAPI32.@]
 *
Jon Griffiths's avatar
Jon Griffiths committed
1294 1295
 * Get information about a service.
 *
1296
 * PARAMS
Jon Griffiths's avatar
Jon Griffiths committed
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
 *   hService       [I] Handle to service to get information about
 *   InfoLevel      [I] Level of information to get
 *   lpBuffer       [O] Destination for requested information
 *   cbBufSize      [I] Size of lpBuffer in bytes
 *   pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small
 *
 * RETURNS
 *  Success: TRUE
 *  FAILURE: FALSE
 */
1307 1308 1309 1310
BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel,
                        LPBYTE lpBuffer, DWORD cbBufSize,
                        LPDWORD pcbBytesNeeded)
{
1311
    DWORD err;
1312 1313 1314

    TRACE("%p %d %p %d %p\n", hService, InfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);

1315
    if (InfoLevel != SC_STATUS_PROCESS_INFO)
1316
    {
1317
        err = ERROR_INVALID_LEVEL;
1318
    }
1319
    else if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
1320
    {
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
        *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
        err = ERROR_INSUFFICIENT_BUFFER;
    }
    else
    {
        __TRY
        {
            err = svcctl_QueryServiceStatusEx(hService, InfoLevel, lpBuffer, cbBufSize, pcbBytesNeeded);
        }
        __EXCEPT(rpc_filter)
        {
            err = map_exception_code(GetExceptionCode());
        }
        __ENDTRY
1335
    }
1336
    if (err != ERROR_SUCCESS)
1337
    {
1338 1339
        SetLastError(err);
        return FALSE;
1340 1341
    }
    return TRUE;
1342
}
1343 1344 1345 1346

/******************************************************************************
 * QueryServiceConfigA [ADVAPI32.@]
 */
1347 1348
BOOL WINAPI QueryServiceConfigA( SC_HANDLE hService, LPQUERY_SERVICE_CONFIGA config,
                                 DWORD size, LPDWORD needed )
1349
{
1350 1351 1352 1353
    DWORD n;
    LPSTR p, buffer;
    BOOL ret;
    QUERY_SERVICE_CONFIGW *configW;
1354

1355
    TRACE("%p %p %d %p\n", hService, config, size, needed);
1356

1357
    if (!(buffer = heap_alloc( 2 * size )))
1358
    {
1359
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1360 1361
        return FALSE;
    }
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
    configW = (QUERY_SERVICE_CONFIGW *)buffer;
    ret = QueryServiceConfigW( hService, configW, 2 * size, needed );
    if (!ret) goto done;

    config->dwServiceType      = configW->dwServiceType;
    config->dwStartType        = configW->dwStartType;
    config->dwErrorControl     = configW->dwErrorControl;
    config->lpBinaryPathName   = NULL;
    config->lpLoadOrderGroup   = NULL;
    config->dwTagId            = configW->dwTagId;
    config->lpDependencies     = NULL;
    config->lpServiceStartName = NULL;
    config->lpDisplayName      = NULL;

    p = (LPSTR)(config + 1);
    n = size - sizeof(*config);
    ret = FALSE;

#define MAP_STR(str) \
    do { \
        if (configW->str) \
        { \
            DWORD sz = WideCharToMultiByte( CP_ACP, 0, configW->str, -1, p, n, NULL, NULL ); \
            if (!sz) goto done; \
            config->str = p; \
            p += sz; \
            n -= sz; \
        } \
    } while (0)

    MAP_STR( lpBinaryPathName );
    MAP_STR( lpLoadOrderGroup );
    MAP_STR( lpDependencies );
    MAP_STR( lpServiceStartName );
    MAP_STR( lpDisplayName );
#undef MAP_STR

1399
    *needed = p - (LPSTR)config;
1400 1401 1402
    ret = TRUE;

done:
1403
    heap_free( buffer );
1404
    return ret;
1405 1406
}

1407 1408 1409 1410 1411
static DWORD move_string_to_buffer(BYTE **buf, LPWSTR *string_ptr)
{
    DWORD cb;

    if (!*string_ptr)
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421
    {
        cb = sizeof(WCHAR);
        memset(*buf, 0, cb);
    }
    else
    {
        cb = (strlenW(*string_ptr) + 1)*sizeof(WCHAR);
        memcpy(*buf, *string_ptr, cb);
        MIDL_user_free(*string_ptr);
    }
1422 1423 1424 1425 1426 1427 1428

    *string_ptr = (LPWSTR)*buf;
    *buf += cb;

    return cb;
}

1429
static DWORD size_string(LPCWSTR string)
1430 1431 1432 1433
{
    return (string ? (strlenW(string) + 1)*sizeof(WCHAR) : sizeof(WCHAR));
}

1434 1435 1436 1437 1438 1439 1440 1441
/******************************************************************************
 * QueryServiceConfigW [ADVAPI32.@]
 */
BOOL WINAPI 
QueryServiceConfigW( SC_HANDLE hService,
                     LPQUERY_SERVICE_CONFIGW lpServiceConfig,
                     DWORD cbBufSize, LPDWORD pcbBytesNeeded)
{
1442 1443 1444 1445
    QUERY_SERVICE_CONFIGW config;
    DWORD total;
    DWORD err;
    BYTE *bufpos;
1446

1447
    TRACE("%p %p %d %p\n", hService, lpServiceConfig,
1448 1449
           cbBufSize, pcbBytesNeeded);

1450
    memset(&config, 0, sizeof(config));
1451

1452 1453
    __TRY
    {
1454
        err = svcctl_QueryServiceConfigW(hService, &config, cbBufSize, pcbBytesNeeded);
1455 1456 1457 1458 1459 1460 1461 1462
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY

    if (err != ERROR_SUCCESS)
1463
    {
1464 1465 1466
        TRACE("services.exe: error %u\n", err);
        SetLastError(err);
        return FALSE;
1467
    }
1468

1469 1470 1471 1472 1473 1474 1475
    /* calculate the size required first */
    total = sizeof (QUERY_SERVICE_CONFIGW);
    total += size_string(config.lpBinaryPathName);
    total += size_string(config.lpLoadOrderGroup);
    total += size_string(config.lpDependencies);
    total += size_string(config.lpServiceStartName);
    total += size_string(config.lpDisplayName);
1476

1477 1478
    *pcbBytesNeeded = total;

1479
    /* if there's not enough memory, return an error */
1480
    if( total > cbBufSize )
1481 1482
    {
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
1483 1484 1485 1486 1487
        MIDL_user_free(config.lpBinaryPathName);
        MIDL_user_free(config.lpLoadOrderGroup);
        MIDL_user_free(config.lpDependencies);
        MIDL_user_free(config.lpServiceStartName);
        MIDL_user_free(config.lpDisplayName);
1488 1489 1490
        return FALSE;
    }

1491 1492 1493 1494 1495 1496 1497
    *lpServiceConfig = config;
    bufpos = ((BYTE *)lpServiceConfig) + sizeof(QUERY_SERVICE_CONFIGW);
    move_string_to_buffer(&bufpos, &lpServiceConfig->lpBinaryPathName);
    move_string_to_buffer(&bufpos, &lpServiceConfig->lpLoadOrderGroup);
    move_string_to_buffer(&bufpos, &lpServiceConfig->lpDependencies);
    move_string_to_buffer(&bufpos, &lpServiceConfig->lpServiceStartName);
    move_string_to_buffer(&bufpos, &lpServiceConfig->lpDisplayName);
1498

1499 1500 1501 1502 1503
    TRACE("Image path           = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) );
    TRACE("Group                = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) );
    TRACE("Dependencies         = %s\n", debugstr_w(lpServiceConfig->lpDependencies) );
    TRACE("Service account name = %s\n", debugstr_w(lpServiceConfig->lpServiceStartName) );
    TRACE("Display name         = %s\n", debugstr_w(lpServiceConfig->lpDisplayName) );
1504 1505 1506 1507

    return TRUE;
}

1508 1509 1510 1511
/******************************************************************************
 * QueryServiceConfig2A [ADVAPI32.@]
 *
 * Note
Austin English's avatar
Austin English committed
1512
 *   observed under win2k:
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522
 *   The functions QueryServiceConfig2A and QueryServiceConfig2W return the same
 *   required buffer size (in byte) at least for dwLevel SERVICE_CONFIG_DESCRIPTION
 */
BOOL WINAPI QueryServiceConfig2A(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer,
                                 DWORD size, LPDWORD needed)
{
    BOOL ret;
    LPBYTE bufferW = NULL;

    if(buffer && size)
1523
        bufferW = heap_alloc(size);
1524 1525 1526 1527 1528 1529

    ret = QueryServiceConfig2W(hService, dwLevel, bufferW, size, needed);
    if(!ret) goto cleanup;

    switch(dwLevel) {
        case SERVICE_CONFIG_DESCRIPTION:
1530 1531
            if (buffer && bufferW) {
                LPSERVICE_DESCRIPTIONA configA = (LPSERVICE_DESCRIPTIONA) buffer;
1532
                LPSERVICE_DESCRIPTIONW configW = (LPSERVICE_DESCRIPTIONW) bufferW;
1533
                if (configW->lpDescription && (size > sizeof(SERVICE_DESCRIPTIONA))) {
1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545
                    DWORD sz;
                    configA->lpDescription = (LPSTR)(configA + 1);
                    sz = WideCharToMultiByte( CP_ACP, 0, configW->lpDescription, -1,
                             configA->lpDescription, size - sizeof(SERVICE_DESCRIPTIONA), NULL, NULL );
                    if (!sz) {
                        FIXME("WideCharToMultiByte failed for configW->lpDescription\n");
                        ret = FALSE;
                        configA->lpDescription = NULL;
                    }
                }
                else configA->lpDescription = NULL;
            }
1546
            break;
1547 1548 1549 1550
        case SERVICE_CONFIG_PRESHUTDOWN_INFO:
            if (buffer && bufferW && *needed<=size)
                memcpy(buffer, bufferW, *needed);
            break;
1551 1552 1553
        default:
            FIXME("conversation W->A not implemented for level %d\n", dwLevel);
            ret = FALSE;
1554
            break;
1555 1556 1557
    }

cleanup:
1558
    heap_free( bufferW);
1559 1560 1561 1562 1563
    return ret;
}

/******************************************************************************
 * QueryServiceConfig2W [ADVAPI32.@]
1564 1565
 *
 * See QueryServiceConfig2A.
1566 1567 1568 1569
 */
BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer,
                                 DWORD size, LPDWORD needed)
{
1570
    DWORD err;
1571

1572
    if(dwLevel!=SERVICE_CONFIG_DESCRIPTION && dwLevel!=SERVICE_CONFIG_PRESHUTDOWN_INFO) {
1573
        FIXME("Level %d not implemented\n", dwLevel);
1574 1575 1576
        SetLastError(ERROR_INVALID_LEVEL);
        return FALSE;
    }
1577 1578

    if(!buffer && size) {
1579 1580 1581 1582 1583 1584
        SetLastError(ERROR_INVALID_ADDRESS);
        return FALSE;
    }

    TRACE("%p 0x%d %p 0x%d %p\n", hService, dwLevel, buffer, size, needed);

1585 1586
    __TRY
    {
1587
        err = svcctl_QueryServiceConfig2W(hService, dwLevel, buffer, size, needed);
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY

    if (err != ERROR_SUCCESS)
    {
        SetLastError( err );
        return FALSE;
    }

    switch (dwLevel)
    {
    case SERVICE_CONFIG_DESCRIPTION:
        if (buffer)
        {
            SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
            if (descr->lpDescription)  /* make it an absolute pointer */
                descr->lpDescription = (WCHAR *)(buffer + (ULONG_PTR)descr->lpDescription);
            break;
1610 1611 1612
        }
    }

1613
    return TRUE;
1614 1615
}

1616 1617 1618 1619
/******************************************************************************
 * EnumServicesStatusA [ADVAPI32.@]
 */
BOOL WINAPI
1620 1621 1622
EnumServicesStatusA( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_STATUSA
                     services, DWORD size, LPDWORD needed, LPDWORD returned,
                     LPDWORD resume_handle )
1623
{
1624 1625 1626 1627 1628 1629 1630 1631 1632
    BOOL ret;
    unsigned int i;
    ENUM_SERVICE_STATUSW *servicesW = NULL;
    DWORD sz, n;
    char *p;

    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
          returned, resume_handle);

1633
    sz = max( 2 * size, sizeof(*servicesW) );
1634
    if (!(servicesW = heap_alloc( sz )))
1635 1636 1637 1638 1639
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }

1640
    ret = EnumServicesStatusW( hmngr, type, state, servicesW, sz, needed, returned, resume_handle );
1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
    if (!ret) goto done;

    p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUSA);
    n = size - (p - (char *)services);
    ret = FALSE;
    for (i = 0; i < *returned; i++)
    {
        sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpServiceName, -1, p, n, NULL, NULL );
        if (!sz) goto done;
        services[i].lpServiceName = p;
        p += sz;
        n -= sz;
        if (servicesW[i].lpDisplayName)
        {
            sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpDisplayName, -1, p, n, NULL, NULL );
            if (!sz) goto done;
            services[i].lpDisplayName = p;
            p += sz;
            n -= sz;
        }
1661
        else services[i].lpDisplayName = NULL;
1662 1663 1664 1665 1666 1667
        services[i].ServiceStatus = servicesW[i].ServiceStatus;
    }

    ret = TRUE;

done:
1668
    heap_free( servicesW );
1669
    return ret;
1670 1671 1672 1673 1674 1675
}

/******************************************************************************
 * EnumServicesStatusW [ADVAPI32.@]
 */
BOOL WINAPI
1676 1677 1678
EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_STATUSW
                     services, DWORD size, LPDWORD needed, LPDWORD returned,
                     LPDWORD resume_handle )
1679
{
1680
    DWORD err, i;
1681
    ENUM_SERVICE_STATUSW dummy_status;
1682 1683 1684 1685 1686 1687 1688 1689 1690 1691

    TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
          returned, resume_handle);

    if (!hmngr)
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }

1692 1693 1694 1695 1696 1697 1698
    /* make sure we pass a valid pointer */
    if (!services || size < sizeof(*services))
    {
        services = &dummy_status;
        size = sizeof(dummy_status);
    }

1699 1700
    __TRY
    {
1701
        err = svcctl_EnumServicesStatusW( hmngr, type, state, (BYTE *)services, size, needed, returned, resume_handle );
1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code( GetExceptionCode() );
    }
    __ENDTRY

    if (err != ERROR_SUCCESS)
    {
        SetLastError( err );
        return FALSE;
    }

    for (i = 0; i < *returned; i++)
    {
        /* convert buffer offsets into pointers */
        services[i].lpServiceName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpServiceName);
        if (services[i].lpDisplayName)
            services[i].lpDisplayName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpDisplayName);
    }

    return TRUE;
1724 1725
}

1726 1727 1728 1729
/******************************************************************************
 * EnumServicesStatusExA [ADVAPI32.@]
 */
BOOL WINAPI
1730 1731 1732
EnumServicesStatusExA( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state,
                       LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
                       LPDWORD resume_handle, LPCSTR group )
1733
{
1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
    BOOL ret;
    unsigned int i;
    ENUM_SERVICE_STATUS_PROCESSA *services = (ENUM_SERVICE_STATUS_PROCESSA *)buffer;
    ENUM_SERVICE_STATUS_PROCESSW *servicesW = NULL;
    WCHAR *groupW = NULL;
    DWORD sz, n;
    char *p;

    TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer,
          size, needed, returned, resume_handle, debugstr_a(group));

1745
    sz = max( 2 * size, sizeof(*servicesW) );
1746
    if (!(servicesW = heap_alloc( sz )))
1747 1748 1749 1750 1751 1752 1753
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }
    if (group)
    {
        int len = MultiByteToWideChar( CP_ACP, 0, group, -1, NULL, 0 );
1754
        if (!(groupW = heap_alloc( len * sizeof(WCHAR) )))
1755 1756
        {
            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1757
            heap_free( servicesW );
1758 1759 1760 1761 1762
            return FALSE;
        }
        MultiByteToWideChar( CP_ACP, 0, group, -1, groupW, len * sizeof(WCHAR) );
    }

1763
    ret = EnumServicesStatusExW( hmngr, level, type, state, (BYTE *)servicesW, sz,
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784
                                 needed, returned, resume_handle, groupW );
    if (!ret) goto done;

    p = (char *)services + *returned * sizeof(ENUM_SERVICE_STATUS_PROCESSA);
    n = size - (p - (char *)services);
    ret = FALSE;
    for (i = 0; i < *returned; i++)
    {
        sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpServiceName, -1, p, n, NULL, NULL );
        if (!sz) goto done;
        services[i].lpServiceName = p;
        p += sz;
        n -= sz;
        if (servicesW[i].lpDisplayName)
        {
            sz = WideCharToMultiByte( CP_ACP, 0, servicesW[i].lpDisplayName, -1, p, n, NULL, NULL );
            if (!sz) goto done;
            services[i].lpDisplayName = p;
            p += sz;
            n -= sz;
        }
1785
        else services[i].lpDisplayName = NULL;
1786 1787 1788 1789 1790 1791
        services[i].ServiceStatusProcess = servicesW[i].ServiceStatusProcess;
    }

    ret = TRUE;

done:
1792 1793
    heap_free( servicesW );
    heap_free( groupW );
1794
    return ret;
1795 1796 1797 1798 1799 1800
}

/******************************************************************************
 * EnumServicesStatusExW [ADVAPI32.@]
 */
BOOL WINAPI
1801 1802 1803
EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD state,
                       LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
                       LPDWORD resume_handle, LPCWSTR group )
1804
{
1805
    DWORD err, i;
1806
    ENUM_SERVICE_STATUS_PROCESSW dummy_status;
1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822
    ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;

    TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer,
          size, needed, returned, resume_handle, debugstr_w(group));

    if (level != SC_ENUM_PROCESS_INFO)
    {
        SetLastError( ERROR_INVALID_LEVEL );
        return FALSE;
    }
    if (!hmngr)
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }

1823 1824 1825 1826 1827 1828 1829
    /* make sure we pass a valid buffer pointer */
    if (!services || size < sizeof(*services))
    {
        buffer = (BYTE *)&dummy_status;
        size = sizeof(dummy_status);
    }

1830 1831
    __TRY
    {
1832 1833
        err = svcctl_EnumServicesStatusExW( hmngr, SC_ENUM_PROCESS_INFO, type, state, buffer, size, needed,
                                            returned, resume_handle, group );
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code( GetExceptionCode() );
    }
    __ENDTRY

    if (err != ERROR_SUCCESS)
    {
        SetLastError( err );
        return FALSE;
    }

    for (i = 0; i < *returned; i++)
    {
        /* convert buffer offsets into pointers */
        services[i].lpServiceName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpServiceName);
        if (services[i].lpDisplayName)
            services[i].lpDisplayName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpDisplayName);
    }

    return TRUE;
1856 1857
}

1858 1859 1860
/******************************************************************************
 * GetServiceKeyNameA [ADVAPI32.@]
 */
1861 1862 1863
BOOL WINAPI GetServiceKeyNameA( SC_HANDLE hSCManager, LPCSTR lpDisplayName,
                                LPSTR lpServiceName, LPDWORD lpcchBuffer )
{
1864 1865 1866 1867 1868 1869 1870 1871 1872
    LPWSTR lpDisplayNameW, lpServiceNameW;
    DWORD sizeW;
    BOOL ret = FALSE;

    TRACE("%p %s %p %p\n", hSCManager,
          debugstr_a(lpDisplayName), lpServiceName, lpcchBuffer);

    lpDisplayNameW = SERV_dup(lpDisplayName);
    if (lpServiceName)
1873
        lpServiceNameW = heap_alloc(*lpcchBuffer * sizeof(WCHAR));
1874 1875 1876 1877 1878 1879
    else
        lpServiceNameW = NULL;

    sizeW = *lpcchBuffer;
    if (!GetServiceKeyNameW(hSCManager, lpDisplayNameW, lpServiceNameW, &sizeW))
    {
1880
        if (lpServiceName && *lpcchBuffer)
1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898
            lpServiceName[0] = 0;
        *lpcchBuffer = sizeW*2;  /* we can only provide an upper estimation of string length */
        goto cleanup;
    }

    if (!WideCharToMultiByte(CP_ACP, 0, lpServiceNameW, (sizeW + 1), lpServiceName,
                        *lpcchBuffer, NULL, NULL ))
    {
        if (*lpcchBuffer && lpServiceName)
            lpServiceName[0] = 0;
        *lpcchBuffer = WideCharToMultiByte(CP_ACP, 0, lpServiceNameW, -1, NULL, 0, NULL, NULL);
        goto cleanup;
    }

    /* lpcchBuffer not updated - same as in GetServiceDisplayNameA */
    ret = TRUE;

cleanup:
1899 1900
    heap_free(lpServiceNameW);
    heap_free(lpDisplayNameW);
1901
    return ret;
1902 1903
}

1904 1905 1906
/******************************************************************************
 * GetServiceKeyNameW [ADVAPI32.@]
 */
1907 1908 1909
BOOL WINAPI GetServiceKeyNameW( SC_HANDLE hSCManager, LPCWSTR lpDisplayName,
                                LPWSTR lpServiceName, LPDWORD lpcchBuffer )
{
1910
    DWORD err;
1911
    WCHAR buffer[2];
1912
    DWORD size;
1913 1914

    TRACE("%p %s %p %p\n", hSCManager,
1915
          debugstr_w(lpDisplayName), lpServiceName, lpcchBuffer);
1916

1917
    if (!hSCManager)
1918
    {
1919
        SetLastError( ERROR_INVALID_HANDLE );
1920
        return FALSE;
1921 1922
    }

1923 1924 1925 1926 1927 1928 1929 1930 1931 1932
    /* provide a buffer if the caller didn't */
    if (!lpServiceName || *lpcchBuffer < 2)
    {
        lpServiceName = buffer;
        /* A size of 1 would be enough, but tests show that Windows returns 2,
         * probably because of a WCHAR/bytes mismatch in their code.
         */
        *lpcchBuffer = 2;
    }

1933 1934 1935 1936
    /* RPC call takes size excluding nul-terminator, whereas *lpcchBuffer
     * includes the nul-terminator on input. */
    size = *lpcchBuffer - 1;

1937 1938
    __TRY
    {
1939
        err = svcctl_GetServiceKeyNameW(hSCManager, lpDisplayName, lpServiceName,
1940
                                        &size);
1941 1942 1943 1944 1945 1946
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
1947

1948 1949 1950 1951
    /* The value of *lpcchBuffer excludes nul-terminator on output. */
    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
        *lpcchBuffer = size;

1952 1953 1954
    if (err)
        SetLastError(err);
    return err == ERROR_SUCCESS;
1955 1956
}

1957 1958 1959
/******************************************************************************
 * QueryServiceLockStatusA [ADVAPI32.@]
 */
1960 1961 1962 1963
BOOL WINAPI QueryServiceLockStatusA( SC_HANDLE hSCManager,
                                     LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
                                     DWORD cbBufSize, LPDWORD pcbBytesNeeded)
{
1964
    FIXME("%p %p %08x %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded);
1965 1966 1967 1968

    return FALSE;
}

1969 1970 1971
/******************************************************************************
 * QueryServiceLockStatusW [ADVAPI32.@]
 */
1972 1973 1974 1975
BOOL WINAPI QueryServiceLockStatusW( SC_HANDLE hSCManager,
                                     LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
                                     DWORD cbBufSize, LPDWORD pcbBytesNeeded)
{
1976
    FIXME("%p %p %08x %p\n", hSCManager, lpLockStatus, cbBufSize, pcbBytesNeeded);
1977 1978 1979

    return FALSE;
}
1980 1981 1982 1983 1984 1985 1986

/******************************************************************************
 * GetServiceDisplayNameA  [ADVAPI32.@]
 */
BOOL WINAPI GetServiceDisplayNameA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
  LPSTR lpDisplayName, LPDWORD lpcchBuffer)
{
1987 1988 1989
    LPWSTR lpServiceNameW, lpDisplayNameW;
    DWORD sizeW;
    BOOL ret = FALSE;
1990 1991

    TRACE("%p %s %p %p\n", hSCManager,
1992
          debugstr_a(lpServiceName), lpDisplayName, lpcchBuffer);
1993

1994
    lpServiceNameW = SERV_dup(lpServiceName);
1995
    if (lpDisplayName)
1996
        lpDisplayNameW = heap_alloc(*lpcchBuffer * sizeof(WCHAR));
1997 1998
    else
        lpDisplayNameW = NULL;
1999

2000 2001
    sizeW = *lpcchBuffer;
    if (!GetServiceDisplayNameW(hSCManager, lpServiceNameW, lpDisplayNameW, &sizeW))
2002
    {
2003
        if (lpDisplayName && *lpcchBuffer)
2004
            lpDisplayName[0] = 0;
2005 2006
        *lpcchBuffer = sizeW*2;  /* we can only provide an upper estimation of string length */
        goto cleanup;
2007
    }
2008 2009 2010

    if (!WideCharToMultiByte(CP_ACP, 0, lpDisplayNameW, (sizeW + 1), lpDisplayName,
                        *lpcchBuffer, NULL, NULL ))
2011
    {
2012 2013
        if (*lpcchBuffer && lpDisplayName)
            lpDisplayName[0] = 0;
2014 2015
        *lpcchBuffer = WideCharToMultiByte(CP_ACP, 0, lpDisplayNameW, -1, NULL, 0, NULL, NULL);
        goto cleanup;
2016 2017
    }

2018 2019 2020
    /* probably due to a bug GetServiceDisplayNameA doesn't modify lpcchBuffer on success.
     * (but if the function succeeded it means that is a good upper estimation of the size) */
    ret = TRUE;
2021

2022
cleanup:
2023 2024
    heap_free(lpDisplayNameW);
    heap_free(lpServiceNameW);
2025
    return ret;
2026 2027 2028 2029 2030 2031 2032 2033
}

/******************************************************************************
 * GetServiceDisplayNameW  [ADVAPI32.@]
 */
BOOL WINAPI GetServiceDisplayNameW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
  LPWSTR lpDisplayName, LPDWORD lpcchBuffer)
{
2034
    DWORD err;
2035
    DWORD size;
2036
    WCHAR buffer[2];
2037 2038

    TRACE("%p %s %p %p\n", hSCManager,
2039
          debugstr_w(lpServiceName), lpDisplayName, lpcchBuffer);
2040

2041
    if (!hSCManager)
2042
    {
2043
        SetLastError( ERROR_INVALID_HANDLE );
2044
        return FALSE;
2045 2046
    }

2047 2048 2049 2050 2051 2052 2053 2054 2055 2056
    /* provide a buffer if the caller didn't */
    if (!lpDisplayName || *lpcchBuffer < 2)
    {
        lpDisplayName = buffer;
        /* A size of 1 would be enough, but tests show that Windows returns 2,
         * probably because of a WCHAR/bytes mismatch in their code.
         */
        *lpcchBuffer = 2;
    }

2057 2058 2059 2060
    /* RPC call takes size excluding nul-terminator, whereas *lpcchBuffer
     * includes the nul-terminator on input. */
    size = *lpcchBuffer - 1;

2061 2062
    __TRY
    {
2063
        err = svcctl_GetServiceDisplayNameW(hSCManager, lpServiceName, lpDisplayName,
2064
                                            &size);
2065 2066 2067 2068 2069 2070
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
2071

2072 2073 2074 2075
    /* The value of *lpcchBuffer excludes nul-terminator on output. */
    if (err == ERROR_SUCCESS || err == ERROR_INSUFFICIENT_BUFFER)
        *lpcchBuffer = size;

2076 2077 2078
    if (err)
        SetLastError(err);
    return err == ERROR_SUCCESS;
2079 2080
}

2081 2082 2083 2084 2085 2086 2087 2088
/******************************************************************************
 * ChangeServiceConfigW  [ADVAPI32.@]
 */
BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType,
  DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
  LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies,
  LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName)
{
2089 2090
    DWORD cb_pwd;
    DWORD err;
2091

2092
    TRACE("%p %d %d %d %s %s %p %p %s %s %s\n",
2093 2094 2095 2096
          hService, dwServiceType, dwStartType, dwErrorControl, 
          debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup),
          lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName),
          debugstr_w(lpPassword), debugstr_w(lpDisplayName) );
2097

2098
    cb_pwd = lpPassword ? (strlenW(lpPassword) + 1)*sizeof(WCHAR) : 0;
2099

2100 2101
    __TRY
    {
2102
        err = svcctl_ChangeServiceConfigW(hService, dwServiceType,
2103 2104 2105 2106 2107 2108 2109 2110 2111
                dwStartType, dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId,
                (const BYTE *)lpDependencies, multisz_cb(lpDependencies), lpServiceStartName,
                (const BYTE *)lpPassword, cb_pwd, lpDisplayName);
    }
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY
2112

2113 2114
    if (err != ERROR_SUCCESS)
        SetLastError(err);
2115

2116
    return err == ERROR_SUCCESS;
2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
}

/******************************************************************************
 * ChangeServiceConfigA  [ADVAPI32.@]
 */
BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType,
  DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName,
  LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies,
  LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName)
{
2127 2128 2129 2130
    LPWSTR wBinaryPathName, wLoadOrderGroup, wDependencies;
    LPWSTR wServiceStartName, wPassword, wDisplayName;
    BOOL r;

2131
    TRACE("%p %d %d %d %s %s %p %p %s %s %s\n",
2132 2133 2134 2135
          hService, dwServiceType, dwStartType, dwErrorControl, 
          debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup),
          lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName),
          debugstr_a(lpPassword), debugstr_a(lpDisplayName) );
2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148

    wBinaryPathName = SERV_dup( lpBinaryPathName );
    wLoadOrderGroup = SERV_dup( lpLoadOrderGroup );
    wDependencies = SERV_dupmulti( lpDependencies );
    wServiceStartName = SERV_dup( lpServiceStartName );
    wPassword = SERV_dup( lpPassword );
    wDisplayName = SERV_dup( lpDisplayName );

    r = ChangeServiceConfigW( hService, dwServiceType,
            dwStartType, dwErrorControl, wBinaryPathName,
            wLoadOrderGroup, lpdwTagId, wDependencies,
            wServiceStartName, wPassword, wDisplayName);

2149 2150 2151 2152 2153 2154
    heap_free( wBinaryPathName );
    heap_free( wLoadOrderGroup );
    heap_free( wDependencies );
    heap_free( wServiceStartName );
    heap_free( wPassword );
    heap_free( wDisplayName );
2155 2156

    return r;
2157
}
2158 2159 2160 2161 2162 2163 2164

/******************************************************************************
 * ChangeServiceConfig2A  [ADVAPI32.@]
 */
BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel, 
    LPVOID lpInfo)
{
2165 2166
    BOOL r = FALSE;

2167
    TRACE("%p %d %p\n",hService, dwInfoLevel, lpInfo);
2168 2169 2170

    if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
    {
2171
        LPSERVICE_DESCRIPTIONA sd = lpInfo;
2172 2173 2174 2175 2176 2177
        SERVICE_DESCRIPTIONW sdw;

        sdw.lpDescription = SERV_dup( sd->lpDescription );

        r = ChangeServiceConfig2W( hService, dwInfoLevel, &sdw );

2178
        heap_free( sdw.lpDescription );
2179 2180 2181
    }
    else if (dwInfoLevel == SERVICE_CONFIG_FAILURE_ACTIONS)
    {
2182
        LPSERVICE_FAILURE_ACTIONSA fa = lpInfo;
2183 2184 2185 2186 2187 2188 2189 2190 2191 2192
        SERVICE_FAILURE_ACTIONSW faw;

        faw.dwResetPeriod = fa->dwResetPeriod;
        faw.lpRebootMsg = SERV_dup( fa->lpRebootMsg );
        faw.lpCommand = SERV_dup( fa->lpCommand );
        faw.cActions = fa->cActions;
        faw.lpsaActions = fa->lpsaActions;

        r = ChangeServiceConfig2W( hService, dwInfoLevel, &faw );

2193 2194
        heap_free( faw.lpRebootMsg );
        heap_free( faw.lpCommand );
2195
    }
2196 2197 2198 2199
    else if (dwInfoLevel == SERVICE_CONFIG_PRESHUTDOWN_INFO)
    {
        r = ChangeServiceConfig2W( hService, dwInfoLevel, lpInfo);
    }
2200 2201 2202 2203
    else
        SetLastError( ERROR_INVALID_PARAMETER );

    return r;
2204 2205 2206 2207 2208 2209 2210 2211
}

/******************************************************************************
 * ChangeServiceConfig2W  [ADVAPI32.@]
 */
BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, 
    LPVOID lpInfo)
{
2212
    DWORD err;
2213

2214
    __TRY
2215
    {
2216 2217 2218
        SC_RPC_CONFIG_INFOW info;

        info.dwInfoLevel = dwInfoLevel;
2219
        info.u.descr = lpInfo;
2220
        err = svcctl_ChangeServiceConfig2W( hService, info );
2221
    }
2222 2223 2224 2225 2226 2227 2228 2229 2230 2231
    __EXCEPT(rpc_filter)
    {
        err = map_exception_code(GetExceptionCode());
    }
    __ENDTRY

    if (err != ERROR_SUCCESS)
        SetLastError(err);

    return err == ERROR_SUCCESS;
2232
}
2233

2234
NTSTATUS SERV_QueryServiceObjectSecurity(SC_HANDLE hService,
2235 2236 2237 2238
       SECURITY_INFORMATION dwSecurityInformation,
       PSECURITY_DESCRIPTOR lpSecurityDescriptor,
       DWORD cbBufSize, LPDWORD pcbBytesNeeded)
{
2239
    SECURITY_DESCRIPTOR descriptor;
2240
    NTSTATUS status;
2241 2242
    DWORD size;
    ACL acl;
2243

2244
    FIXME("%p %d %p %u %p - semi-stub\n", hService, dwSecurityInformation,
2245
          lpSecurityDescriptor, cbBufSize, pcbBytesNeeded);
2246

2247 2248
    if (dwSecurityInformation != DACL_SECURITY_INFORMATION)
        FIXME("information %d not supported\n", dwSecurityInformation);
2249

2250 2251 2252 2253 2254 2255
    InitializeSecurityDescriptor(&descriptor, SECURITY_DESCRIPTOR_REVISION);

    InitializeAcl(&acl, sizeof(ACL), ACL_REVISION);
    SetSecurityDescriptorDacl(&descriptor, TRUE, &acl, TRUE);

    size = cbBufSize;
2256
    status = RtlMakeSelfRelativeSD(&descriptor, lpSecurityDescriptor, &size);
2257
    *pcbBytesNeeded = size;
2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276
    return status;
}

/******************************************************************************
 * QueryServiceObjectSecurity [ADVAPI32.@]
 */
BOOL WINAPI QueryServiceObjectSecurity(SC_HANDLE hService,
       SECURITY_INFORMATION dwSecurityInformation,
       PSECURITY_DESCRIPTOR lpSecurityDescriptor,
       DWORD cbBufSize, LPDWORD pcbBytesNeeded)
{
    NTSTATUS status = SERV_QueryServiceObjectSecurity(hService, dwSecurityInformation, lpSecurityDescriptor,
                                                      cbBufSize, pcbBytesNeeded);
    if (status != STATUS_SUCCESS)
    {
        SetLastError(RtlNtStatusToDosError(status));
        return FALSE;
    }
    return TRUE;
2277 2278 2279 2280
}

/******************************************************************************
 * SetServiceObjectSecurity [ADVAPI32.@]
2281 2282 2283
 *
 * NOTES
 *  - SetSecurityInfo should be updated to call this function once it's implemented.
2284 2285 2286 2287 2288
 */
BOOL WINAPI SetServiceObjectSecurity(SC_HANDLE hService,
       SECURITY_INFORMATION dwSecurityInformation,
       PSECURITY_DESCRIPTOR lpSecurityDescriptor)
{
2289
    FIXME("%p %d %p\n", hService, dwSecurityInformation, lpSecurityDescriptor);
2290
    return TRUE;
2291
}
2292

2293 2294 2295
/******************************************************************************
 * SetServiceBits [ADVAPI32.@]
 */
2296 2297 2298 2299 2300
BOOL WINAPI SetServiceBits( SERVICE_STATUS_HANDLE hServiceStatus,
        DWORD dwServiceBits,
        BOOL bSetBitsOn,
        BOOL bUpdateImmediately)
{
2301
    FIXME("%p %08x %x %x\n", hServiceStatus, dwServiceBits,
2302 2303 2304
          bSetBitsOn, bUpdateImmediately);
    return TRUE;
}
2305

2306 2307
/* thunk for calling the RegisterServiceCtrlHandler handler function */
static DWORD WINAPI ctrl_handler_thunk( DWORD control, DWORD type, void *data, void *context )
2308
{
2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320
    LPHANDLER_FUNCTION func = context;

    func( control );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * RegisterServiceCtrlHandlerA [ADVAPI32.@]
 */
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerA( LPCSTR name, LPHANDLER_FUNCTION handler )
{
    return RegisterServiceCtrlHandlerExA( name, ctrl_handler_thunk, handler );
2321 2322
}

2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340
/******************************************************************************
 * RegisterServiceCtrlHandlerW [ADVAPI32.@]
 */
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerW( LPCWSTR name, LPHANDLER_FUNCTION handler )
{
    return RegisterServiceCtrlHandlerExW( name, ctrl_handler_thunk, handler );
}

/******************************************************************************
 * RegisterServiceCtrlHandlerExA [ADVAPI32.@]
 */
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExA( LPCSTR name, LPHANDLER_FUNCTION_EX handler, LPVOID context )
{
    LPWSTR nameW;
    SERVICE_STATUS_HANDLE ret;

    nameW = SERV_dup(name);
    ret = RegisterServiceCtrlHandlerExW( nameW, handler, context );
2341
    heap_free( nameW );
2342 2343 2344 2345 2346 2347
    return ret;
}

/******************************************************************************
 * RegisterServiceCtrlHandlerExW [ADVAPI32.@]
 */
2348 2349 2350
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerExW( LPCWSTR lpServiceName,
        LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext )
{
2351 2352
    service_data *service;
    SC_HANDLE hService = 0;
2353
    BOOL found = FALSE;
2354 2355 2356 2357

    TRACE("%s %p %p\n", debugstr_w(lpServiceName), lpHandlerProc, lpContext);

    EnterCriticalSection( &service_cs );
2358
    if ((service = find_service_by_name( lpServiceName )))
2359
    {
2360 2361 2362 2363
        service->handler = lpHandlerProc;
        service->context = lpContext;
        hService = service->handle;
        found = TRUE;
2364 2365 2366
    }
    LeaveCriticalSection( &service_cs );

2367
    if (!found) SetLastError(ERROR_SERVICE_DOES_NOT_EXIST);
2368 2369

    return (SERVICE_STATUS_HANDLE)hService;
2370
}
2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398

/******************************************************************************
 * EnumDependentServicesA [ADVAPI32.@]
 */
BOOL WINAPI EnumDependentServicesA( SC_HANDLE hService, DWORD dwServiceState,
                                    LPENUM_SERVICE_STATUSA lpServices, DWORD cbBufSize,
        LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned )
{
    FIXME("%p 0x%08x %p 0x%08x %p %p - stub\n", hService, dwServiceState,
          lpServices, cbBufSize, pcbBytesNeeded, lpServicesReturned);

    *lpServicesReturned = 0;
    return TRUE;
}

/******************************************************************************
 * EnumDependentServicesW [ADVAPI32.@]
 */
BOOL WINAPI EnumDependentServicesW( SC_HANDLE hService, DWORD dwServiceState,
                                    LPENUM_SERVICE_STATUSW lpServices, DWORD cbBufSize,
                                    LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned )
{
    FIXME("%p 0x%08x %p 0x%08x %p %p - stub\n", hService, dwServiceState,
          lpServices, cbBufSize, pcbBytesNeeded, lpServicesReturned);

    *lpServicesReturned = 0;
    return TRUE;
}
2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432

/******************************************************************************
 * NotifyServiceStatusChangeW [ADVAPI32.@]
 */
DWORD WINAPI NotifyServiceStatusChangeW(SC_HANDLE hService, DWORD dwNotifyMask,
        SERVICE_NOTIFYW *pNotifyBuffer)
{
    DWORD dummy;
    BOOL ret;
    SERVICE_STATUS_PROCESS st;

    FIXME("%p 0x%x %p - semi-stub\n", hService, dwNotifyMask, pNotifyBuffer);

    ret = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (void*)&st, sizeof(st), &dummy);
    if (ret)
    {
        /* dwNotifyMask is a set of bitflags in same order as SERVICE_ statuses */
        if (dwNotifyMask & (1 << (st.dwCurrentState - SERVICE_STOPPED)))
        {
            pNotifyBuffer->dwNotificationStatus = ERROR_SUCCESS;
            memcpy(&pNotifyBuffer->ServiceStatus, &st, sizeof(pNotifyBuffer->ServiceStatus));
            pNotifyBuffer->dwNotificationTriggered = 1 << (st.dwCurrentState - SERVICE_STOPPED);
            pNotifyBuffer->pszServiceNames = NULL;
            TRACE("Queueing notification: 0x%x\n", pNotifyBuffer->dwNotificationTriggered);
            QueueUserAPC((PAPCFUNC)pNotifyBuffer->pfnNotifyCallback,
                    GetCurrentThread(), (ULONG_PTR)pNotifyBuffer);
        }
    }

    /* TODO: If the service is not currently in a matching state, we should
     * tell `services` to monitor it. */

    return ERROR_SUCCESS;
}