Return the correct error when OpenService is called with a
[wine] / dlls / advapi32 / service.c
1 /*
2  * Win32 advapi functions
3  *
4  * Copyright 1995 Sven Verdoolaege
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <string.h>
23 #include <time.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winsvc.h"
28 #include "winerror.h"
29 #include "winreg.h"
30 #include "wine/unicode.h"
31 #include "heap.h"
32 #include "wine/debug.h"
33 #include "winternl.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(advapi);
36
37 static DWORD   start_dwNumServiceArgs;
38 static LPWSTR *start_lpServiceArgVectors;
39
40 static const WCHAR _ServiceStartDataW[]  = {'A','D','V','A','P','I','_','S',
41                                             'e','r','v','i','c','e','S','t',
42                                             'a','r','t','D','a','t','a',0};
43 static const WCHAR szServiceManagerKey[] = { 'S','y','s','t','e','m','\\',
44       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
45       'S','e','r','v','i','c','e','s','\\',0 };
46
47 /******************************************************************************
48  * SC_HANDLEs
49  */
50
51 #define MAX_SERVICE_NAME 256
52
53 typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE;
54
55 struct sc_handle;
56
57 struct sc_manager       /* SCM handle */
58 {
59     HKEY hkey_scm_db;   /* handle to services database in the registry */
60     LONG ref_count;     /* handle must remain alive until any related service */
61                         /* handle exists because DeleteService requires it */
62 };
63
64 struct sc_service       /* service handle */
65 {
66     HKEY hkey;          /* handle to service entry in the registry (under hkey_scm_db) */
67     struct sc_handle *sc_manager;  /* pointer to SCM handle */
68     WCHAR name[ MAX_SERVICE_NAME ];
69 };
70
71 struct sc_handle
72 {
73     SC_HANDLE_TYPE htype;
74     union
75     {
76         struct sc_manager manager;
77         struct sc_service service;
78     } u;
79 };
80
81 static struct sc_handle* alloc_sc_handle( SC_HANDLE_TYPE htype )
82 {
83     struct sc_handle *retval;
84
85     retval = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct sc_handle) );
86     if( retval != NULL )
87     {
88         retval->htype = htype;
89     }
90     TRACE("SC_HANDLE type=%d -> %p\n",htype,retval);
91     return retval;
92 }
93
94 static void free_sc_handle( struct sc_handle* handle )
95 {
96     if( NULL == handle )
97         return;
98
99     switch( handle->htype )
100     {
101         case SC_HTYPE_MANAGER:
102         {
103             if( InterlockedDecrement( &handle->u.manager.ref_count ) )
104                 /* there are references to this handle */
105                 return;
106
107             if( handle->u.manager.hkey_scm_db )
108                 RegCloseKey( handle->u.manager.hkey_scm_db );
109             break;
110         }
111
112         case SC_HTYPE_SERVICE:
113         {
114             struct sc_handle *h = handle->u.service.sc_manager;
115
116             if( h )
117             {
118                 /* release SCM handle */
119                 if( 0 == InterlockedDecrement( &h->u.manager.ref_count ) )
120                 {
121                     /* it's time to destroy SCM handle */
122                     if( h->u.manager.hkey_scm_db )
123                         RegCloseKey( h->u.manager.hkey_scm_db );
124                     
125                     TRACE("SC_HANDLE (SCM) %p type=%d\n",h,h->htype);
126                     
127                     HeapFree( GetProcessHeap(), 0, h );
128                 }
129             }
130             if( handle->u.service.hkey )
131                 RegCloseKey( handle->u.service.hkey );
132             break;
133         }
134     }
135
136     TRACE("SC_HANDLE %p type=%d\n",handle,handle->htype);
137
138     HeapFree( GetProcessHeap(), 0, handle );
139 }
140
141 static void init_service_handle( struct sc_handle* handle,
142                                  struct sc_handle* sc_manager,
143                                  HKEY hKey, LPCWSTR lpServiceName )
144 {
145     /* init sc_service structure */
146     handle->u.service.hkey = hKey;
147     lstrcpynW( handle->u.service.name, lpServiceName, MAX_SERVICE_NAME );
148
149     /* add reference to SCM handle */
150     InterlockedIncrement( &sc_manager->u.manager.ref_count );
151     handle->u.service.sc_manager = sc_manager;
152 }
153
154 /******************************************************************************
155  * EnumServicesStatusA [ADVAPI32.@]
156  */
157 BOOL WINAPI
158 EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType,
159                      DWORD dwServiceState, LPENUM_SERVICE_STATUSA lpServices,
160                      DWORD cbBufSize, LPDWORD pcbBytesNeeded,
161                      LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
162 {       FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager,
163                 dwServiceType, dwServiceState, lpServices, cbBufSize,
164                 pcbBytesNeeded, lpServicesReturned,  lpResumeHandle);
165         SetLastError (ERROR_ACCESS_DENIED);
166         return FALSE;
167 }
168
169 /******************************************************************************
170  * EnumServicesStatusW [ADVAPI32.@]
171  */
172 BOOL WINAPI
173 EnumServicesStatusW( SC_HANDLE hSCManager, DWORD dwServiceType,
174                      DWORD dwServiceState, LPENUM_SERVICE_STATUSW lpServices,
175                      DWORD cbBufSize, LPDWORD pcbBytesNeeded,
176                      LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
177 {       FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager,
178                 dwServiceType, dwServiceState, lpServices, cbBufSize,
179                 pcbBytesNeeded, lpServicesReturned,  lpResumeHandle);
180         SetLastError (ERROR_ACCESS_DENIED);
181         return FALSE;
182 }
183
184 /******************************************************************************
185  * StartServiceCtrlDispatcherA [ADVAPI32.@]
186  */
187 BOOL WINAPI
188 StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
189 {
190     LPSERVICE_MAIN_FUNCTIONA fpMain;
191     HANDLE wait;
192     DWORD  dwNumServiceArgs ;
193     LPWSTR *lpArgVecW;
194     LPSTR  *lpArgVecA;
195     unsigned int i;
196
197     TRACE("(%p)\n", servent);
198     wait = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
199     if (!wait)
200     {
201         ERR("Couldn't create data semaphore\n");
202         return FALSE;
203     }
204
205     dwNumServiceArgs = start_dwNumServiceArgs;
206     lpArgVecW        = start_lpServiceArgVectors;
207
208     ReleaseSemaphore(wait, 1, NULL);
209
210     /* Convert the Unicode arg vectors back to ASCII */
211     if(dwNumServiceArgs)
212         lpArgVecA = (LPSTR*) HeapAlloc( GetProcessHeap(), 0,
213                                    dwNumServiceArgs*sizeof(LPSTR) );
214     else
215         lpArgVecA = NULL;
216
217     for(i=0; i<dwNumServiceArgs; i++)
218         lpArgVecA[i]=HEAP_strdupWtoA(GetProcessHeap(), 0, lpArgVecW[i]);
219
220     /* FIXME: should we blindly start all services? */
221     while (servent->lpServiceName) {
222         TRACE("%s at %p)\n", debugstr_a(servent->lpServiceName),servent);
223         fpMain = servent->lpServiceProc;
224
225         /* try to start the service */
226         fpMain( dwNumServiceArgs, lpArgVecA);
227
228         servent++;
229     }
230
231     if(dwNumServiceArgs)
232     {
233         /* free arg strings */
234         for(i=0; i<dwNumServiceArgs; i++)
235             HeapFree(GetProcessHeap(), 0, lpArgVecA[i]);
236         HeapFree(GetProcessHeap(), 0, lpArgVecA);
237     }
238
239     return TRUE;
240 }
241
242 /******************************************************************************
243  * StartServiceCtrlDispatcherW [ADVAPI32.@]
244  *
245  * PARAMS
246  *   servent []
247  */
248 BOOL WINAPI
249 StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
250 {
251     LPSERVICE_MAIN_FUNCTIONW fpMain;
252     HANDLE wait;
253     DWORD  dwNumServiceArgs ;
254     LPWSTR *lpServiceArgVectors ;
255
256     TRACE("(%p)\n", servent);
257     wait = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, _ServiceStartDataW);
258     if(wait == 0)
259     {
260         ERR("Couldn't find wait semaphore\n");
261         ERR("perhaps you need to start services using StartService\n");
262         return FALSE;
263     }
264
265     dwNumServiceArgs    = start_dwNumServiceArgs;
266     lpServiceArgVectors = start_lpServiceArgVectors;
267
268     ReleaseSemaphore(wait, 1, NULL);
269
270     /* FIXME: should we blindly start all services? */
271     while (servent->lpServiceName) {
272         TRACE("%s at %p)\n", debugstr_w(servent->lpServiceName),servent);
273         fpMain = servent->lpServiceProc;
274
275         /* try to start the service */
276         fpMain( dwNumServiceArgs, lpServiceArgVectors);
277
278         servent++;
279     }
280
281     return TRUE;
282 }
283
284 /******************************************************************************
285  * LockServiceDatabase  [ADVAPI32.@]
286  */
287 LPVOID WINAPI LockServiceDatabase (SC_HANDLE hSCManager)
288 {
289         FIXME("%p\n",hSCManager);
290         return (SC_HANDLE)0xcacacafe;
291 }
292
293 /******************************************************************************
294  * UnlockServiceDatabase  [ADVAPI32.@]
295  */
296 BOOL WINAPI UnlockServiceDatabase (LPVOID ScLock)
297 {
298         FIXME(": %p\n",ScLock);
299         return TRUE;
300 }
301
302 /******************************************************************************
303  * RegisterServiceCtrlHandlerA [ADVAPI32.@]
304  */
305 SERVICE_STATUS_HANDLE WINAPI
306 RegisterServiceCtrlHandlerA( LPCSTR lpServiceName,
307                              LPHANDLER_FUNCTION lpfHandler )
308 {       FIXME("%s %p\n", lpServiceName, lpfHandler);
309         return 0xcacacafe;
310 }
311
312 /******************************************************************************
313  * RegisterServiceCtrlHandlerW [ADVAPI32.@]
314  *
315  * PARAMS
316  *   lpServiceName []
317  *   lpfHandler    []
318  */
319 SERVICE_STATUS_HANDLE WINAPI
320 RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName,
321                              LPHANDLER_FUNCTION lpfHandler )
322 {       FIXME("%s %p\n", debugstr_w(lpServiceName), lpfHandler);
323         return 0xcacacafe;
324 }
325
326 /******************************************************************************
327  * SetServiceStatus [ADVAPI32.@]
328  *
329  * PARAMS
330  *   hService []
331  *   lpStatus []
332  */
333 BOOL WINAPI
334 SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
335 {       FIXME("0x%lx %p\n",hService, lpStatus);
336         TRACE("\tType:%lx\n",lpStatus->dwServiceType);
337         TRACE("\tState:%lx\n",lpStatus->dwCurrentState);
338         TRACE("\tControlAccepted:%lx\n",lpStatus->dwControlsAccepted);
339         TRACE("\tExitCode:%lx\n",lpStatus->dwWin32ExitCode);
340         TRACE("\tServiceExitCode:%lx\n",lpStatus->dwServiceSpecificExitCode);
341         TRACE("\tCheckPoint:%lx\n",lpStatus->dwCheckPoint);
342         TRACE("\tWaitHint:%lx\n",lpStatus->dwWaitHint);
343         return TRUE;
344 }
345
346 /******************************************************************************
347  * OpenSCManagerA [ADVAPI32.@]
348  *
349  * Establish a connection to the service control manager and open its database.
350  *
351  * PARAMS
352  *   lpMachineName   [I] Pointer to machine name string
353  *   lpDatabaseName  [I] Pointer to database name string
354  *   dwDesiredAccess [I] Type of access
355  *
356  * RETURNS
357  *   Success: A Handle to the service control manager database
358  *   Failure: NULL
359  */
360 SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName,
361                                  DWORD dwDesiredAccess )
362 {
363     UNICODE_STRING lpMachineNameW;
364     UNICODE_STRING lpDatabaseNameW;
365     SC_HANDLE ret;
366
367     RtlCreateUnicodeStringFromAsciiz (&lpMachineNameW,lpMachineName);
368     RtlCreateUnicodeStringFromAsciiz (&lpDatabaseNameW,lpDatabaseName);
369     ret = OpenSCManagerW(lpMachineNameW.Buffer,lpDatabaseNameW.Buffer, dwDesiredAccess);
370     RtlFreeUnicodeString(&lpDatabaseNameW);
371     RtlFreeUnicodeString(&lpMachineNameW);
372     return ret;
373 }
374
375 /******************************************************************************
376  * OpenSCManagerW [ADVAPI32.@]
377  *
378  * See OpenSCManagerA.
379  */
380 SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
381                                  DWORD dwDesiredAccess )
382 {
383     struct sc_handle *retval;
384     HKEY hReg;
385     LONG r;
386
387     TRACE("(%s,%s,0x%08lx)\n", debugstr_w(lpMachineName),
388           debugstr_w(lpDatabaseName), dwDesiredAccess);
389
390     /*
391      * FIXME: what is lpDatabaseName?
392      * It should be set to "SERVICES_ACTIVE_DATABASE" according to
393      * docs, but what if it isn't?
394      */
395
396     retval = alloc_sc_handle( SC_HTYPE_MANAGER );
397     if( NULL == retval ) return NULL;
398
399     retval->u.manager.ref_count = 1;
400
401     r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg);
402     if (r!=ERROR_SUCCESS)
403         goto error;
404
405     r = RegOpenKeyExW(hReg, szServiceManagerKey,
406                       0, KEY_ALL_ACCESS, &retval->u.manager.hkey_scm_db);
407     RegCloseKey( hReg );
408     if (r!=ERROR_SUCCESS)
409         goto error;
410
411     TRACE("returning %p\n", retval);
412
413     return (SC_HANDLE) retval;
414
415 error:
416     free_sc_handle( retval );
417     return NULL;
418 }
419
420
421 /******************************************************************************
422  * AllocateLocallyUniqueId [ADVAPI32.@]
423  *
424  * PARAMS
425  *   lpluid []
426  */
427 BOOL WINAPI
428 AllocateLocallyUniqueId( PLUID lpluid )
429 {
430         lpluid->LowPart = time(NULL);
431         lpluid->HighPart = 0;
432         return TRUE;
433 }
434
435
436 /******************************************************************************
437  * ControlService [ADVAPI32.@]
438  *
439  * Send a control code to a service.
440  *
441  * PARAMS
442  *   hService        [I] Handle of the service control manager database
443  *   dwControl       [I] Control code to send (SERVICE_CONTROL_* flags from "winsvc.h")
444  *   lpServiceStatus [O] Destination for the status of the service, if available
445  *
446  * RETURNS
447  *   Success: TRUE.
448  *   Failure: FALSE.
449  */
450 BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
451                             LPSERVICE_STATUS lpServiceStatus )
452 {
453     FIXME("(%p,%ld,%p): stub\n",hService,dwControl,lpServiceStatus);
454     return TRUE;
455 }
456
457
458 /******************************************************************************
459  * CloseServiceHandle [ADVAPI32.@]
460  * 
461  * Close a handle to a service or the service control manager database.
462  *
463  * PARAMS
464  *   hSCObject [I] Handle to service or service control manager database
465  *
466  * RETURNS
467  *  Success: TRUE
468  *  Failure: FALSE
469  */
470 BOOL WINAPI
471 CloseServiceHandle( SC_HANDLE hSCObject )
472 {
473     TRACE("(%p)\n", hSCObject);
474
475     free_sc_handle( (struct sc_handle*) hSCObject );
476
477     return TRUE;
478 }
479
480
481 /******************************************************************************
482  * OpenServiceA [ADVAPI32.@]
483  *
484  * Open a handle to a service.
485  *
486  * PARAMS
487  *   hSCManager      [I] Handle of the service control manager database
488  *   lpServiceName   [I] Name of the service to open
489  *   dwDesiredAccess [I] Access required to the service
490  *
491  * RETURNS
492  *    Success: Handle to the service
493  *    Failure: NULL
494  */
495 SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
496                                DWORD dwDesiredAccess )
497 {
498     UNICODE_STRING lpServiceNameW;
499     SC_HANDLE ret;
500     RtlCreateUnicodeStringFromAsciiz (&lpServiceNameW,lpServiceName);
501     if(lpServiceName)
502         TRACE("Request for service %s\n",lpServiceName);
503     else
504         return FALSE;
505     ret = OpenServiceW( hSCManager, lpServiceNameW.Buffer, dwDesiredAccess);
506     RtlFreeUnicodeString(&lpServiceNameW);
507     return ret;
508 }
509
510
511 /******************************************************************************
512  * OpenServiceW [ADVAPI32.@]
513  *
514  * See OpenServiceA.
515  */
516 SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
517                                DWORD dwDesiredAccess)
518 {
519     struct sc_handle *hscm = hSCManager;
520     struct sc_handle *retval;
521     HKEY hKey;
522     long r;
523
524     TRACE("(%p,%p,%ld)\n",hSCManager, lpServiceName,
525           dwDesiredAccess);
526
527     retval = alloc_sc_handle( SC_HTYPE_SERVICE );
528     if( NULL == retval )
529         return NULL;
530
531     r = RegOpenKeyExW( hscm->u.manager.hkey_scm_db,
532                        lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
533     if (r!=ERROR_SUCCESS)
534     {
535         free_sc_handle( retval );
536         SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
537         return NULL;
538     }
539     
540     init_service_handle( retval, hscm, hKey, lpServiceName );
541
542     TRACE("returning %p\n",retval);
543
544     return (SC_HANDLE) retval;
545 }
546
547 /******************************************************************************
548  * CreateServiceW [ADVAPI32.@]
549  */
550 SC_HANDLE WINAPI
551 CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
552                   LPCWSTR lpDisplayName, DWORD dwDesiredAccess,
553                   DWORD dwServiceType, DWORD dwStartType,
554                   DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
555                   LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
556                   LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
557                   LPCWSTR lpPassword )
558 {
559     struct sc_handle *hscm = hSCManager;
560     struct sc_handle *retval;
561     HKEY hKey;
562     LONG r;
563     DWORD dp;
564     static const WCHAR szDisplayName[] = { 'D','i','s','p','l','a','y','N','a','m','e', 0 };
565     static const WCHAR szType[] = {'T','y','p','e',0};
566     static const WCHAR szStart[] = {'S','t','a','r','t',0};
567     static const WCHAR szError[] = {'E','r','r','o','r','C','o','n','t','r','o','l', 0};
568     static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
569     static const WCHAR szGroup[] = {'G','r','o','u','p',0};
570     static const WCHAR szDependencies[] = { 'D','e','p','e','n','d','e','n','c','i','e','s',0};
571
572     FIXME("%p %s %s\n", hSCManager, 
573           debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
574
575     retval = alloc_sc_handle( SC_HTYPE_SERVICE );
576     if( NULL == retval )
577         return NULL;
578
579     r = RegCreateKeyExW(hscm->u.manager.hkey_scm_db, lpServiceName, 0, NULL,
580                        REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
581     if (r!=ERROR_SUCCESS)
582         goto error;
583
584     init_service_handle( retval, hscm, hKey, lpServiceName );
585
586     if (dp != REG_CREATED_NEW_KEY)
587         goto error;
588
589     if(lpDisplayName)
590     {
591         r = RegSetValueExW(hKey, szDisplayName, 0, REG_SZ, (LPBYTE)lpDisplayName,
592                            (strlenW(lpDisplayName)+1)*sizeof(WCHAR) );
593         if (r!=ERROR_SUCCESS)
594             goto error;
595     }
596
597     r = RegSetValueExW(hKey, szType, 0, REG_DWORD, (LPVOID)&dwServiceType, sizeof (DWORD) );
598     if (r!=ERROR_SUCCESS)
599         goto error;
600
601     r = RegSetValueExW(hKey, szStart, 0, REG_DWORD, (LPVOID)&dwStartType, sizeof (DWORD) );
602     if (r!=ERROR_SUCCESS)
603         goto error;
604
605     r = RegSetValueExW(hKey, szError, 0, REG_DWORD,
606                            (LPVOID)&dwErrorControl, sizeof (DWORD) );
607     if (r!=ERROR_SUCCESS)
608         goto error;
609
610     if(lpBinaryPathName)
611     {
612         r = RegSetValueExW(hKey, szImagePath, 0, REG_SZ, (LPBYTE)lpBinaryPathName,
613                            (strlenW(lpBinaryPathName)+1)*sizeof(WCHAR) );
614         if (r!=ERROR_SUCCESS)
615             goto error;
616     }
617
618     if(lpLoadOrderGroup)
619     {
620         r = RegSetValueExW(hKey, szGroup, 0, REG_SZ, (LPBYTE)lpLoadOrderGroup,
621                            (strlenW(lpLoadOrderGroup)+1)*sizeof(WCHAR) );
622         if (r!=ERROR_SUCCESS)
623             goto error;
624     }
625
626     if(lpDependencies)
627     {
628         DWORD len = 0;
629
630         /* determine the length of a double null terminated multi string */
631         do {
632             len += (strlenW(&lpDependencies[len])+1);
633         } while (lpDependencies[len++]);
634
635         r = RegSetValueExW(hKey, szDependencies, 0, REG_MULTI_SZ,
636                            (LPBYTE)lpDependencies, len );
637         if (r!=ERROR_SUCCESS)
638             goto error;
639     }
640
641     if(lpPassword)
642     {
643         FIXME("Don't know how to add a Password for a service.\n");
644     }
645
646     if(lpServiceStartName)
647     {
648         FIXME("Don't know how to add a ServiceStartName for a service.\n");
649     }
650
651     return (SC_HANDLE) retval;
652     
653 error:
654     free_sc_handle( retval );
655     return NULL;
656 }
657
658
659 static inline LPWSTR SERV_dup( LPCSTR str )
660 {
661     UINT len;
662     LPWSTR wstr;
663
664     if( !str )
665         return NULL;
666     len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
667     wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
668     MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len );
669     return wstr;
670 }
671
672 static inline LPWSTR SERV_dupmulti( LPCSTR str )
673 {
674     UINT len = 0, n = 0;
675     LPWSTR wstr;
676
677     if( !str )
678         return NULL;
679     do {
680         len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 );
681         n += (strlen( &str[n] ) + 1);
682     } while (str[n]);
683     len++;
684     n++;
685     
686     wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
687     MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len );
688     return wstr;
689 }
690
691 static inline VOID SERV_free( LPWSTR wstr )
692 {
693     HeapFree( GetProcessHeap(), 0, wstr );
694 }
695
696 /******************************************************************************
697  * CreateServiceA [ADVAPI32.@]
698  */
699 SC_HANDLE WINAPI
700 CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
701                   LPCSTR lpDisplayName, DWORD dwDesiredAccess,
702                   DWORD dwServiceType, DWORD dwStartType,
703                   DWORD dwErrorControl, LPCSTR lpBinaryPathName,
704                   LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
705                   LPCSTR lpDependencies, LPCSTR lpServiceStartName,
706                   LPCSTR lpPassword )
707 {
708     LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW,
709         lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW;
710     SC_HANDLE r;
711
712     TRACE("%p %s %s\n", hSCManager,
713           debugstr_a(lpServiceName), debugstr_a(lpDisplayName));
714
715     lpServiceNameW = SERV_dup( lpServiceName );
716     lpDisplayNameW = SERV_dup( lpDisplayName );
717     lpBinaryPathNameW = SERV_dup( lpBinaryPathName );
718     lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup );
719     lpDependenciesW = SERV_dupmulti( lpDependencies );
720     lpServiceStartNameW = SERV_dup( lpServiceStartName );
721     lpPasswordW = SERV_dup( lpPassword );
722
723     r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW,
724             dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
725             lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId,
726             lpDependenciesW, lpServiceStartNameW, lpPasswordW );
727
728     SERV_free( lpServiceNameW );
729     SERV_free( lpDisplayNameW );
730     SERV_free( lpBinaryPathNameW );
731     SERV_free( lpLoadOrderGroupW );
732     SERV_free( lpDependenciesW );
733     SERV_free( lpServiceStartNameW );
734     SERV_free( lpPasswordW );
735
736     return r;
737 }
738
739
740 /******************************************************************************
741  * DeleteService [ADVAPI32.@]
742  *
743  * Delete a service from the service control manager database.
744  *
745  * PARAMS
746  *    hService [I] Handle of the service to delete
747  *
748  * RETURNS
749  *  Success: TRUE
750  *  Failure: FALSE
751  */
752 BOOL WINAPI DeleteService( SC_HANDLE hService )
753 {
754     struct sc_handle *hsvc = hService;
755     HKEY hKey = hsvc->u.service.hkey;
756     WCHAR valname[MAX_PATH+1];
757     INT index = 0;
758     LONG rc;
759     DWORD size;
760
761     size = MAX_PATH+1; 
762     /* Clean out the values */
763     rc = RegEnumValueW(hKey, index, valname,&size,0,0,0,0);
764     while (rc == ERROR_SUCCESS)
765     {
766         RegDeleteValueW(hKey,valname);
767         index++;
768         size = MAX_PATH+1; 
769         rc = RegEnumValueW(hKey, index, valname, &size,0,0,0,0);
770     }
771
772     RegCloseKey(hKey);
773     hsvc->u.service.hkey = NULL;
774
775     /* delete the key */
776     RegDeleteKeyW(hsvc->u.service.sc_manager->u.manager.hkey_scm_db,
777                   hsvc->u.service.name);
778
779     return TRUE;
780 }
781
782
783 /******************************************************************************
784  * StartServiceA [ADVAPI32.@]
785  *
786  * Start a service
787  *
788  * PARAMS
789  *   hService            [I] Handle of service
790  *   dwNumServiceArgs    [I] Number of arguments
791  *   lpServiceArgVectors [I] Address of array of argument strings
792  *
793  * NOTES
794  *  - NT implements this function using an obscure RPC call.
795  *  - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc
796  *    to get things like "%SystemRoot%\\System32\\service.exe" to load.
797  *  - This will only work for shared address space. How should the service
798  *    args be transferred when address spaces are separated?
799  *  - Can only start one service at a time.
800  *  - Has no concept of privilege.
801  *
802  * RETURNS
803  *   Success: TRUE.
804  *   Failure: FALSE
805  */
806 BOOL WINAPI
807 StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
808                  LPCSTR *lpServiceArgVectors )
809 {
810     LPWSTR *lpwstr=NULL;
811     UNICODE_STRING usBuffer;
812     unsigned int i;
813
814     TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
815
816     if(dwNumServiceArgs)
817         lpwstr = (LPWSTR*) HeapAlloc( GetProcessHeap(), 0,
818                                    dwNumServiceArgs*sizeof(LPWSTR) );
819     else
820         lpwstr = NULL;
821
822     for(i=0; i<dwNumServiceArgs; i++)
823     {
824         RtlCreateUnicodeStringFromAsciiz (&usBuffer,lpServiceArgVectors[i]);
825         lpwstr[i]=usBuffer.Buffer;
826     }
827
828     StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
829
830     if(dwNumServiceArgs)
831     {
832         for(i=0; i<dwNumServiceArgs; i++)
833             HeapFree(GetProcessHeap(), 0, lpwstr[i]);
834         HeapFree(GetProcessHeap(), 0, lpwstr);
835     }
836
837     return TRUE;
838 }
839
840
841 /******************************************************************************
842  * StartServiceW [ADVAPI32.@]
843  * 
844  * See StartServiceA.
845  */
846 BOOL WINAPI
847 StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
848                  LPCWSTR *lpServiceArgVectors )
849 {
850     static const WCHAR  _WaitServiceStartW[]  = {'A','D','V','A','P','I','_','W',
851                                                 'a','i','t','S','e','r','v','i',
852                                                 'c','e','S','t','a','r','t',0};
853     static const WCHAR  _ImagePathW[]  = {'I','m','a','g','e','P','a','t','h',0};
854                                                 
855     struct sc_handle *hsvc = hService;
856     WCHAR path[MAX_PATH],str[MAX_PATH];
857     DWORD type,size;
858     long r;
859     HANDLE data,wait;
860     PROCESS_INFORMATION procinfo;
861     STARTUPINFOW startupinfo;
862     TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,
863           lpServiceArgVectors);
864
865     size = sizeof(str);
866     r = RegQueryValueExW(hsvc->u.service.hkey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
867     if (r!=ERROR_SUCCESS)
868         return FALSE;
869     ExpandEnvironmentStringsW(str,path,sizeof(path));
870
871     TRACE("Starting service %s\n", debugstr_w(path) );
872
873     data = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
874     if (!data)
875     {
876         ERR("Couldn't create data semaphore\n");
877         return FALSE;
878     }
879     wait = CreateSemaphoreW(NULL,0,1,_WaitServiceStartW);
880     if (!wait)
881     {
882         ERR("Couldn't create wait semaphore\n");
883         return FALSE;
884     }
885
886     /*
887      * FIXME: lpServiceArgsVectors need to be stored and returned to
888      *        the service when it calls StartServiceCtrlDispatcher
889      *
890      * Chuck these in a global (yuk) so we can pass them to
891      * another process - address space separation will break this.
892      */
893
894     r = WaitForSingleObject(data,INFINITE);
895
896     if( r == WAIT_FAILED)
897         return FALSE;
898
899     FIXME("problematic because of address space separation.\n");
900     start_dwNumServiceArgs    = dwNumServiceArgs;
901     start_lpServiceArgVectors = (LPWSTR *)lpServiceArgVectors;
902
903     ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
904     startupinfo.cb = sizeof(STARTUPINFOW);
905
906     r = CreateProcessW(NULL,
907                    path,
908                    NULL,  /* process security attribs */
909                    NULL,  /* thread security attribs */
910                    FALSE, /* inherit handles */
911                    0,     /* creation flags */
912                    NULL,  /* environment */
913                    NULL,  /* current directory */
914                    &startupinfo,  /* startup info */
915                    &procinfo); /* process info */
916
917     if(r == FALSE)
918     {
919         ERR("Couldn't start process\n");
920         /* ReleaseSemaphore(data, 1, NULL);
921         return FALSE; */
922     }
923
924     /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
925     r = WaitForSingleObject(wait,30000);
926
927     ReleaseSemaphore(data, 1, NULL);
928
929     if( r == WAIT_FAILED)
930         return FALSE;
931
932     return TRUE;
933 }
934
935 /******************************************************************************
936  * QueryServiceStatus [ADVAPI32.@]
937  *
938  * PARAMS
939  *   hService        []
940  *   lpservicestatus []
941  *
942  */
943 BOOL WINAPI
944 QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus )
945 {
946     struct sc_handle *hsvc = hService;
947     LONG r;
948     DWORD type, val, size;
949
950     FIXME("(%p,%p) partial\n",hService,lpservicestatus);
951
952     /* read the service type from the registry */
953     size = sizeof(val);
954     r = RegQueryValueExA(hsvc->u.service.hkey, "Type", NULL, &type, (LPBYTE)&val, &size);
955     if(type!=REG_DWORD)
956     {
957         ERR("invalid Type\n");
958         return FALSE;
959     }
960     lpservicestatus->dwServiceType = val;
961     /* FIXME: how are these determined or read from the registry? */
962     /* SERVICE: unavailable=0, stopped=1, starting=2, running=3? */;
963     lpservicestatus->dwCurrentState            = 1;
964     lpservicestatus->dwControlsAccepted        = 0;
965     lpservicestatus->dwWin32ExitCode           = NO_ERROR;
966     lpservicestatus->dwServiceSpecificExitCode = 0;
967     lpservicestatus->dwCheckPoint              = 0;
968     lpservicestatus->dwWaitHint                = 0;
969
970     return TRUE;
971 }
972
973 /******************************************************************************
974  * QueryServiceStatusEx [ADVAPI32.@]
975  *
976  * Get information about a service.
977  *
978  * PARAMS
979  *   hService       [I] Handle to service to get information about
980  *   InfoLevel      [I] Level of information to get
981  *   lpBuffer       [O] Destination for requested information
982  *   cbBufSize      [I] Size of lpBuffer in bytes
983  *   pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small
984  *
985  * RETURNS
986  *  Success: TRUE
987  *  FAILURE: FALSE
988  */
989 BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel,
990                         LPBYTE lpBuffer, DWORD cbBufSize,
991                         LPDWORD pcbBytesNeeded)
992 {
993     FIXME("stub\n");
994     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
995     return FALSE;
996 }
997
998 /******************************************************************************
999  * QueryServiceConfigA [ADVAPI32.@]
1000  */
1001 BOOL WINAPI 
1002 QueryServiceConfigA( SC_HANDLE hService,
1003                      LPQUERY_SERVICE_CONFIGA lpServiceConfig,
1004                      DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1005 {
1006     static const CHAR szDisplayName[] = "DisplayName";
1007     static const CHAR szType[] = "Type";
1008     static const CHAR szStart[] = "Start";
1009     static const CHAR szError[] = "ErrorControl";
1010     static const CHAR szImagePath[] = "ImagePath";
1011     static const CHAR szGroup[] = "Group";
1012     static const CHAR szDependencies[] = "Dependencies";
1013     HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1014     CHAR str_buffer[ MAX_PATH ];
1015     LONG r;
1016     DWORD type, val, sz, total, n;
1017     LPBYTE p;
1018
1019     TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1020            cbBufSize, pcbBytesNeeded);
1021
1022     /* calculate the size required first */
1023     total = sizeof (QUERY_SERVICE_CONFIGA);
1024
1025     sz = sizeof(str_buffer);
1026     r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1027     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1028     {
1029         sz = ExpandEnvironmentStringsA(str_buffer,NULL,0);
1030         if( 0 == sz ) return FALSE;
1031
1032         total += sz;
1033     }
1034     else
1035     {
1036         /* FIXME: set last error */
1037         return FALSE;
1038     }
1039
1040     sz = 0;
1041     r = RegQueryValueExA( hKey, szGroup, 0, &type, NULL, &sz );
1042     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1043         total += sz;
1044
1045     sz = 0;
1046     r = RegQueryValueExA( hKey, szDependencies, 0, &type, NULL, &sz );
1047     if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1048         total += sz;
1049
1050     sz = 0;
1051     r = RegQueryValueExA( hKey, szStart, 0, &type, NULL, &sz );
1052     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1053         total += sz;
1054
1055     sz = 0;
1056     r = RegQueryValueExA( hKey, szDisplayName, 0, &type, NULL, &sz );
1057     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1058         total += sz;
1059
1060     /* if there's not enough memory, return an error */
1061     if( total > *pcbBytesNeeded )
1062     {
1063         *pcbBytesNeeded = total;
1064         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1065         return FALSE;
1066     }
1067
1068     *pcbBytesNeeded = total;
1069     ZeroMemory( lpServiceConfig, total );
1070
1071     sz = sizeof val;
1072     r = RegQueryValueExA( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1073     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1074         lpServiceConfig->dwServiceType = val;
1075
1076     sz = sizeof val;
1077     r = RegQueryValueExA( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1078     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1079         lpServiceConfig->dwStartType = val;
1080
1081     sz = sizeof val;
1082     r = RegQueryValueExA( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1083     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1084         lpServiceConfig->dwErrorControl = val;
1085
1086     /* now do the strings */
1087     p = (LPBYTE) &lpServiceConfig[1];
1088     n = total - sizeof (QUERY_SERVICE_CONFIGA);
1089
1090     sz = sizeof(str_buffer);
1091     r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1092     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1093     {
1094         sz = ExpandEnvironmentStringsA(str_buffer, p, n);
1095         if( 0 == sz || sz > n ) return FALSE;
1096
1097         lpServiceConfig->lpBinaryPathName = (LPSTR) p;
1098         p += sz;
1099         n -= sz;
1100     }
1101     else
1102     {
1103         /* FIXME: set last error */
1104         return FALSE;
1105     }
1106
1107     sz = n;
1108     r = RegQueryValueExA( hKey, szGroup, 0, &type, p, &sz );
1109     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1110     {
1111         lpServiceConfig->lpLoadOrderGroup = (LPSTR) p;
1112         p += sz;
1113         n -= sz;
1114     }
1115
1116     sz = n;
1117     r = RegQueryValueExA( hKey, szDependencies, 0, &type, p, &sz );
1118     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1119     {
1120         lpServiceConfig->lpDependencies = (LPSTR) p;
1121         p += sz;
1122         n -= sz;
1123     }
1124
1125     if( n < 0 )
1126         ERR("Buffer overflow!\n");
1127
1128     TRACE("Image path = %s\n", lpServiceConfig->lpBinaryPathName );
1129     TRACE("Group      = %s\n", lpServiceConfig->lpLoadOrderGroup );
1130
1131     return TRUE;
1132 }
1133
1134 /******************************************************************************
1135  * QueryServiceConfigW [ADVAPI32.@]
1136  */
1137 BOOL WINAPI 
1138 QueryServiceConfigW( SC_HANDLE hService,
1139                      LPQUERY_SERVICE_CONFIGW lpServiceConfig,
1140                      DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1141 {
1142     static const WCHAR szDisplayName[] = {
1143         'D','i','s','p','l','a','y','N','a','m','e', 0 };
1144     static const WCHAR szType[] = {'T','y','p','e',0};
1145     static const WCHAR szStart[] = {'S','t','a','r','t',0};
1146     static const WCHAR szError[] = {
1147         'E','r','r','o','r','C','o','n','t','r','o','l', 0};
1148     static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
1149     static const WCHAR szGroup[] = {'G','r','o','u','p',0};
1150     static const WCHAR szDependencies[] = {
1151         'D','e','p','e','n','d','e','n','c','i','e','s',0};
1152     HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1153     WCHAR str_buffer[ MAX_PATH ];
1154     LONG r;
1155     DWORD type, val, sz, total, n;
1156     LPBYTE p;
1157
1158     TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1159            cbBufSize, pcbBytesNeeded);
1160
1161     /* calculate the size required first */
1162     total = sizeof (QUERY_SERVICE_CONFIGW);
1163
1164     sz = sizeof(str_buffer);
1165     r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1166     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1167     {
1168         sz = ExpandEnvironmentStringsW(str_buffer,NULL,0);
1169         if( 0 == sz ) return FALSE;
1170
1171         total += sizeof(WCHAR) * sz;
1172     }
1173     else
1174     {
1175        /* FIXME: set last error */
1176        return FALSE;
1177     }
1178
1179     sz = 0;
1180     r = RegQueryValueExW( hKey, szGroup, 0, &type, NULL, &sz );
1181     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1182         total += sz;
1183
1184     sz = 0;
1185     r = RegQueryValueExW( hKey, szDependencies, 0, &type, NULL, &sz );
1186     if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1187         total += sz;
1188
1189     sz = 0;
1190     r = RegQueryValueExW( hKey, szStart, 0, &type, NULL, &sz );
1191     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1192         total += sz;
1193
1194     sz = 0;
1195     r = RegQueryValueExW( hKey, szDisplayName, 0, &type, NULL, &sz );
1196     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1197         total += sz;
1198
1199     /* if there's not enough memory, return an error */
1200     if( total > *pcbBytesNeeded )
1201     {
1202         *pcbBytesNeeded = total;
1203         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1204         return FALSE;
1205     }
1206
1207     *pcbBytesNeeded = total;
1208     ZeroMemory( lpServiceConfig, total );
1209
1210     sz = sizeof val;
1211     r = RegQueryValueExW( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1212     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1213         lpServiceConfig->dwServiceType = val;
1214
1215     sz = sizeof val;
1216     r = RegQueryValueExW( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1217     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1218         lpServiceConfig->dwStartType = val;
1219
1220     sz = sizeof val;
1221     r = RegQueryValueExW( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1222     if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1223         lpServiceConfig->dwErrorControl = val;
1224
1225     /* now do the strings */
1226     p = (LPBYTE) &lpServiceConfig[1];
1227     n = total - sizeof (QUERY_SERVICE_CONFIGW);
1228
1229     sz = sizeof(str_buffer);
1230     r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1231     if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1232     {
1233         sz = ExpandEnvironmentStringsW(str_buffer, (LPWSTR) p, n);
1234         sz *= sizeof(WCHAR);
1235         if( 0 == sz || sz > n ) return FALSE;
1236
1237         lpServiceConfig->lpBinaryPathName = (LPWSTR) p;
1238         p += sz;
1239         n -= sz;
1240     }
1241     else
1242     {
1243        /* FIXME: set last error */
1244        return FALSE;
1245     }
1246
1247     sz = n;
1248     r = RegQueryValueExW( hKey, szGroup, 0, &type, p, &sz );
1249     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1250     {
1251         lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p;
1252         p += sz;
1253         n -= sz;
1254     }
1255
1256     sz = n;
1257     r = RegQueryValueExW( hKey, szDependencies, 0, &type, p, &sz );
1258     if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1259     {
1260         lpServiceConfig->lpDependencies = (LPWSTR) p;
1261         p += sz;
1262         n -= sz;
1263     }
1264
1265     if( n < 0 )
1266         ERR("Buffer overflow!\n");
1267
1268     TRACE("Image path = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) );
1269     TRACE("Group      = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) );
1270
1271     return TRUE;
1272 }
1273
1274 /******************************************************************************
1275  * ChangeServiceConfigW  [ADVAPI32.@]
1276  */
1277 BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType,
1278   DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
1279   LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies,
1280   LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName)
1281 {
1282     FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1283           hService, dwServiceType, dwStartType, dwErrorControl, 
1284           debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup),
1285           lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName),
1286           debugstr_w(lpPassword), debugstr_w(lpDisplayName) );
1287     return TRUE;
1288 }
1289
1290 /******************************************************************************
1291  * ChangeServiceConfigA  [ADVAPI32.@]
1292  */
1293 BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType,
1294   DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName,
1295   LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies,
1296   LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName)
1297 {
1298     FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1299           hService, dwServiceType, dwStartType, dwErrorControl, 
1300           debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup),
1301           lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName),
1302           debugstr_a(lpPassword), debugstr_a(lpDisplayName) );
1303     return TRUE;
1304 }
1305
1306 /******************************************************************************
1307  * ChangeServiceConfig2A  [ADVAPI32.@]
1308  */
1309 BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel, 
1310     LPVOID lpInfo)
1311 {
1312     FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1313     return TRUE;
1314 }
1315
1316 /******************************************************************************
1317  * ChangeServiceConfig2W  [ADVAPI32.@]
1318  */
1319 BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel, 
1320     LPVOID lpInfo)
1321 {
1322     HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1323
1324     if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
1325     {
1326         static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
1327         LPSERVICE_DESCRIPTIONW sd = (LPSERVICE_DESCRIPTIONW)lpInfo;
1328         if (sd->lpDescription)
1329         {
1330             TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription));
1331             if (sd->lpDescription[0] == 0)
1332                 RegDeleteValueW(hKey,szDescription);
1333             else
1334                 RegSetValueExW(hKey, szDescription, 0, REG_SZ,
1335                                         (LPVOID)sd->lpDescription,
1336                                  sizeof(WCHAR)*(strlenW(sd->lpDescription)+1));
1337         }
1338     }
1339     else   
1340         FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1341     return TRUE;
1342 }