Add a basic GDI LOGFONT test, fix a couple of failures.
[wine/testsucceed.git] / dlls / advapi32 / service.c
blobaec42ed48607408f3cb7ff771ace56bb2f7687e4
1 /*
2 * Win32 advapi functions
4 * Copyright 1995 Sven Verdoolaege
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.
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.
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
21 #include <stdarg.h>
22 #include <string.h>
23 #include <time.h>
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"
35 WINE_DEFAULT_DEBUG_CHANNEL(advapi);
37 static DWORD start_dwNumServiceArgs;
38 static LPWSTR *start_lpServiceArgVectors;
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 };
47 /******************************************************************************
48 * SC_HANDLEs
51 #define MAX_SERVICE_NAME 256
53 typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE;
55 struct sc_handle;
57 struct sc_manager /* SCM handle */
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 */
64 struct sc_service /* service handle */
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 ];
71 struct sc_handle
73 SC_HANDLE_TYPE htype;
74 union
76 struct sc_manager manager;
77 struct sc_service service;
78 } u;
81 static struct sc_handle* alloc_sc_handle( SC_HANDLE_TYPE htype )
83 struct sc_handle *retval;
85 retval = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct sc_handle) );
86 if( retval != NULL )
88 retval->htype = htype;
90 TRACE("SC_HANDLE type=%d -> %p\n",htype,retval);
91 return retval;
94 static void free_sc_handle( struct sc_handle* handle )
96 if( NULL == handle )
97 return;
99 switch( handle->htype )
101 case SC_HTYPE_MANAGER:
103 if( InterlockedDecrement( &handle->u.manager.ref_count ) )
104 /* there are references to this handle */
105 return;
107 if( handle->u.manager.hkey_scm_db )
108 RegCloseKey( handle->u.manager.hkey_scm_db );
109 break;
112 case SC_HTYPE_SERVICE:
114 struct sc_handle *h = handle->u.service.sc_manager;
116 if( h )
118 /* release SCM handle */
119 if( 0 == InterlockedDecrement( &h->u.manager.ref_count ) )
121 /* it's time to destroy SCM handle */
122 if( h->u.manager.hkey_scm_db )
123 RegCloseKey( h->u.manager.hkey_scm_db );
125 TRACE("SC_HANDLE (SCM) %p type=%d\n",h,h->htype);
127 HeapFree( GetProcessHeap(), 0, h );
130 if( handle->u.service.hkey )
131 RegCloseKey( handle->u.service.hkey );
132 break;
136 TRACE("SC_HANDLE %p type=%d\n",handle,handle->htype);
138 HeapFree( GetProcessHeap(), 0, handle );
141 static void init_service_handle( struct sc_handle* handle,
142 struct sc_handle* sc_manager,
143 HKEY hKey, LPCWSTR lpServiceName )
145 /* init sc_service structure */
146 handle->u.service.hkey = hKey;
147 lstrcpynW( handle->u.service.name, lpServiceName, MAX_SERVICE_NAME );
149 /* add reference to SCM handle */
150 InterlockedIncrement( &sc_manager->u.manager.ref_count );
151 handle->u.service.sc_manager = sc_manager;
154 /******************************************************************************
155 * EnumServicesStatusA [ADVAPI32.@]
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;
169 /******************************************************************************
170 * EnumServicesStatusW [ADVAPI32.@]
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;
184 /******************************************************************************
185 * StartServiceCtrlDispatcherA [ADVAPI32.@]
187 BOOL WINAPI
188 StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
190 LPSERVICE_MAIN_FUNCTIONA fpMain;
191 HANDLE wait;
192 DWORD dwNumServiceArgs ;
193 LPWSTR *lpArgVecW;
194 LPSTR *lpArgVecA;
195 int i;
197 TRACE("(%p)\n", servent);
198 wait = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
199 if (!wait)
201 ERR("Couldn't create data semaphore\n");
202 return FALSE;
205 dwNumServiceArgs = start_dwNumServiceArgs;
206 lpArgVecW = start_lpServiceArgVectors;
208 ReleaseSemaphore(wait, 1, NULL);
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;
217 for(i=0; i<dwNumServiceArgs; i++)
218 lpArgVecA[i]=HEAP_strdupWtoA(GetProcessHeap(), 0, lpArgVecW[i]);
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;
225 /* try to start the service */
226 fpMain( dwNumServiceArgs, lpArgVecA);
228 servent++;
231 if(dwNumServiceArgs)
233 /* free arg strings */
234 for(i=0; i<dwNumServiceArgs; i++)
235 HeapFree(GetProcessHeap(), 0, lpArgVecA[i]);
236 HeapFree(GetProcessHeap(), 0, lpArgVecA);
239 return TRUE;
242 /******************************************************************************
243 * StartServiceCtrlDispatcherW [ADVAPI32.@]
245 * PARAMS
246 * servent []
248 BOOL WINAPI
249 StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
251 LPSERVICE_MAIN_FUNCTIONW fpMain;
252 HANDLE wait;
253 DWORD dwNumServiceArgs ;
254 LPWSTR *lpServiceArgVectors ;
256 TRACE("(%p)\n", servent);
257 wait = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, _ServiceStartDataW);
258 if(wait == 0)
260 ERR("Couldn't find wait semaphore\n");
261 ERR("perhaps you need to start services using StartService\n");
262 return FALSE;
265 dwNumServiceArgs = start_dwNumServiceArgs;
266 lpServiceArgVectors = start_lpServiceArgVectors;
268 ReleaseSemaphore(wait, 1, NULL);
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;
275 /* try to start the service */
276 fpMain( dwNumServiceArgs, lpServiceArgVectors);
278 servent++;
281 return TRUE;
284 /******************************************************************************
285 * LockServiceDatabase [ADVAPI32.@]
287 LPVOID WINAPI LockServiceDatabase (SC_HANDLE hSCManager)
289 FIXME("%p\n",hSCManager);
290 return (SC_HANDLE)0xcacacafe;
293 /******************************************************************************
294 * UnlockServiceDatabase [ADVAPI32.@]
296 BOOL WINAPI UnlockServiceDatabase (LPVOID ScLock)
298 FIXME(": %p\n",ScLock);
299 return TRUE;
302 /******************************************************************************
303 * RegisterServiceCtrlHandlerA [ADVAPI32.@]
305 SERVICE_STATUS_HANDLE WINAPI
306 RegisterServiceCtrlHandlerA( LPCSTR lpServiceName,
307 LPHANDLER_FUNCTION lpfHandler )
308 { FIXME("%s %p\n", lpServiceName, lpfHandler);
309 return 0xcacacafe;
312 /******************************************************************************
313 * RegisterServiceCtrlHandlerW [ADVAPI32.@]
315 * PARAMS
316 * lpServiceName []
317 * lpfHandler []
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;
326 /******************************************************************************
327 * SetServiceStatus [ADVAPI32.@]
329 * PARAMS
330 * hService []
331 * lpStatus []
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;
346 /******************************************************************************
347 * OpenSCManagerA [ADVAPI32.@]
349 * Establish a connection to the service control manager and open its database.
351 * PARAMS
352 * lpMachineName [I] Pointer to machine name string
353 * lpDatabaseName [I] Pointer to database name string
354 * dwDesiredAccess [I] Type of access
356 * RETURNS
357 * Success: A Handle to the service control manager database
358 * Failure: NULL
360 SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName,
361 DWORD dwDesiredAccess )
363 UNICODE_STRING lpMachineNameW;
364 UNICODE_STRING lpDatabaseNameW;
365 SC_HANDLE ret;
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;
375 /******************************************************************************
376 * OpenSCManagerW [ADVAPI32.@]
378 * See OpenSCManagerA.
380 SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
381 DWORD dwDesiredAccess )
383 struct sc_handle *retval;
384 HKEY hReg;
385 LONG r;
387 TRACE("(%s,%s,0x%08lx)\n", debugstr_w(lpMachineName),
388 debugstr_w(lpDatabaseName), dwDesiredAccess);
391 * FIXME: what is lpDatabaseName?
392 * It should be set to "SERVICES_ACTIVE_DATABASE" according to
393 * docs, but what if it isn't?
396 retval = alloc_sc_handle( SC_HTYPE_MANAGER );
397 if( NULL == retval ) return NULL;
399 retval->u.manager.ref_count = 1;
401 r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg);
402 if (r!=ERROR_SUCCESS)
403 goto error;
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;
411 TRACE("returning %p\n", retval);
413 return (SC_HANDLE) retval;
415 error:
416 free_sc_handle( retval );
417 return NULL;
421 /******************************************************************************
422 * AllocateLocallyUniqueId [ADVAPI32.@]
424 * PARAMS
425 * lpluid []
427 BOOL WINAPI
428 AllocateLocallyUniqueId( PLUID lpluid )
430 lpluid->LowPart = time(NULL);
431 lpluid->HighPart = 0;
432 return TRUE;
436 /******************************************************************************
437 * ControlService [ADVAPI32.@]
439 * Send a control code to a service.
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
446 * RETURNS
447 * Success: TRUE.
448 * Failure: FALSE.
450 BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
451 LPSERVICE_STATUS lpServiceStatus )
453 FIXME("(%p,%ld,%p): stub\n",hService,dwControl,lpServiceStatus);
454 return TRUE;
458 /******************************************************************************
459 * CloseServiceHandle [ADVAPI32.@]
461 * Close a handle to a service or the service control manager database.
463 * PARAMS
464 * hSCObject [I] Handle to service or service control manager database
466 * RETURNS
467 * Success: TRUE
468 * Failure: FALSE
470 BOOL WINAPI
471 CloseServiceHandle( SC_HANDLE hSCObject )
473 TRACE("(%p)\n", hSCObject);
475 free_sc_handle( (struct sc_handle*) hSCObject );
477 return TRUE;
481 /******************************************************************************
482 * OpenServiceA [ADVAPI32.@]
484 * Open a handle to a service.
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
491 * RETURNS
492 * Success: Handle to the service
493 * Failure: NULL
495 SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
496 DWORD dwDesiredAccess )
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;
511 /******************************************************************************
512 * OpenServiceW [ADVAPI32.@]
514 * See OpenServiceA.
516 SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
517 DWORD dwDesiredAccess)
519 struct sc_handle *hscm = hSCManager;
520 struct sc_handle *retval;
521 HKEY hKey;
522 long r;
524 TRACE("(%p,%p,%ld)\n",hSCManager, lpServiceName,
525 dwDesiredAccess);
527 retval = alloc_sc_handle( SC_HTYPE_SERVICE );
528 if( NULL == retval )
529 return NULL;
531 r = RegOpenKeyExW( hscm->u.manager.hkey_scm_db,
532 lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
533 if (r!=ERROR_SUCCESS)
535 free_sc_handle( retval );
536 return NULL;
539 init_service_handle( retval, hscm, hKey, lpServiceName );
541 TRACE("returning %p\n",retval);
543 return (SC_HANDLE) retval;
546 /******************************************************************************
547 * CreateServiceW [ADVAPI32.@]
549 SC_HANDLE WINAPI
550 CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
551 LPCWSTR lpDisplayName, DWORD dwDesiredAccess,
552 DWORD dwServiceType, DWORD dwStartType,
553 DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
554 LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
555 LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
556 LPCWSTR lpPassword )
558 struct sc_handle *hscm = hSCManager;
559 struct sc_handle *retval;
560 HKEY hKey;
561 LONG r;
562 DWORD dp;
563 static const WCHAR szDisplayName[] = { 'D','i','s','p','l','a','y','N','a','m','e', 0 };
564 static const WCHAR szType[] = {'T','y','p','e',0};
565 static const WCHAR szStart[] = {'S','t','a','r','t',0};
566 static const WCHAR szError[] = {'E','r','r','o','r','C','o','n','t','r','o','l', 0};
567 static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
568 static const WCHAR szGroup[] = {'G','r','o','u','p',0};
569 static const WCHAR szDependencies[] = { 'D','e','p','e','n','d','e','n','c','i','e','s',0};
571 FIXME("%p %s %s\n", hSCManager,
572 debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
574 retval = alloc_sc_handle( SC_HTYPE_SERVICE );
575 if( NULL == retval )
576 return NULL;
578 r = RegCreateKeyExW(hscm->u.manager.hkey_scm_db, lpServiceName, 0, NULL,
579 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
580 if (r!=ERROR_SUCCESS)
581 goto error;
583 init_service_handle( retval, hscm, hKey, lpServiceName );
585 if (dp != REG_CREATED_NEW_KEY)
586 goto error;
588 if(lpDisplayName)
590 r = RegSetValueExW(hKey, szDisplayName, 0, REG_SZ, (LPBYTE)lpDisplayName,
591 (strlenW(lpDisplayName)+1)*sizeof(WCHAR) );
592 if (r!=ERROR_SUCCESS)
593 goto error;
596 r = RegSetValueExW(hKey, szType, 0, REG_DWORD, (LPVOID)&dwServiceType, sizeof (DWORD) );
597 if (r!=ERROR_SUCCESS)
598 goto error;
600 r = RegSetValueExW(hKey, szStart, 0, REG_DWORD, (LPVOID)&dwStartType, sizeof (DWORD) );
601 if (r!=ERROR_SUCCESS)
602 goto error;
604 r = RegSetValueExW(hKey, szError, 0, REG_DWORD,
605 (LPVOID)&dwErrorControl, sizeof (DWORD) );
606 if (r!=ERROR_SUCCESS)
607 goto error;
609 if(lpBinaryPathName)
611 r = RegSetValueExW(hKey, szImagePath, 0, REG_SZ, (LPBYTE)lpBinaryPathName,
612 (strlenW(lpBinaryPathName)+1)*sizeof(WCHAR) );
613 if (r!=ERROR_SUCCESS)
614 goto error;
617 if(lpLoadOrderGroup)
619 r = RegSetValueExW(hKey, szGroup, 0, REG_SZ, (LPBYTE)lpLoadOrderGroup,
620 (strlenW(lpLoadOrderGroup)+1)*sizeof(WCHAR) );
621 if (r!=ERROR_SUCCESS)
622 goto error;
625 if(lpDependencies)
627 DWORD len = 0;
629 /* determine the length of a double null terminated multi string */
630 do {
631 len += (strlenW(&lpDependencies[len])+1);
632 } while (lpDependencies[len++]);
634 r = RegSetValueExW(hKey, szDependencies, 0, REG_MULTI_SZ,
635 (LPBYTE)lpDependencies, len );
636 if (r!=ERROR_SUCCESS)
637 goto error;
640 if(lpPassword)
642 FIXME("Don't know how to add a Password for a service.\n");
645 if(lpServiceStartName)
647 FIXME("Don't know how to add a ServiceStartName for a service.\n");
650 return (SC_HANDLE) retval;
652 error:
653 free_sc_handle( retval );
654 return NULL;
658 static inline LPWSTR SERV_dup( LPCSTR str )
660 UINT len;
661 LPWSTR wstr;
663 if( !str )
664 return NULL;
665 len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
666 wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
667 MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len );
668 return wstr;
671 static inline LPWSTR SERV_dupmulti( LPCSTR str )
673 UINT len = 0, n = 0;
674 LPWSTR wstr;
676 if( !str )
677 return NULL;
678 do {
679 len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 );
680 n += (strlen( &str[n] ) + 1);
681 } while (str[n]);
682 len++;
683 n++;
685 wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
686 MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len );
687 return wstr;
690 static inline VOID SERV_free( LPWSTR wstr )
692 HeapFree( GetProcessHeap(), 0, wstr );
695 /******************************************************************************
696 * CreateServiceA [ADVAPI32.@]
698 SC_HANDLE WINAPI
699 CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
700 LPCSTR lpDisplayName, DWORD dwDesiredAccess,
701 DWORD dwServiceType, DWORD dwStartType,
702 DWORD dwErrorControl, LPCSTR lpBinaryPathName,
703 LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
704 LPCSTR lpDependencies, LPCSTR lpServiceStartName,
705 LPCSTR lpPassword )
707 LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW,
708 lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW;
709 SC_HANDLE r;
711 TRACE("%p %s %s\n", hSCManager,
712 debugstr_a(lpServiceName), debugstr_a(lpDisplayName));
714 lpServiceNameW = SERV_dup( lpServiceName );
715 lpDisplayNameW = SERV_dup( lpDisplayName );
716 lpBinaryPathNameW = SERV_dup( lpBinaryPathName );
717 lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup );
718 lpDependenciesW = SERV_dupmulti( lpDependencies );
719 lpServiceStartNameW = SERV_dup( lpServiceStartName );
720 lpPasswordW = SERV_dup( lpPassword );
722 r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW,
723 dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
724 lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId,
725 lpDependenciesW, lpServiceStartNameW, lpPasswordW );
727 SERV_free( lpServiceNameW );
728 SERV_free( lpDisplayNameW );
729 SERV_free( lpBinaryPathNameW );
730 SERV_free( lpLoadOrderGroupW );
731 SERV_free( lpDependenciesW );
732 SERV_free( lpServiceStartNameW );
733 SERV_free( lpPasswordW );
735 return r;
739 /******************************************************************************
740 * DeleteService [ADVAPI32.@]
742 * Delete a service from the service control manager database.
744 * PARAMS
745 * hService [I] Handle of the service to delete
747 * RETURNS
748 * Success: TRUE
749 * Failure: FALSE
751 BOOL WINAPI DeleteService( SC_HANDLE hService )
753 struct sc_handle *hsvc = hService;
754 HKEY hKey = hsvc->u.service.hkey;
755 WCHAR valname[MAX_PATH+1];
756 INT index = 0;
757 LONG rc;
758 DWORD size;
760 size = MAX_PATH+1;
761 /* Clean out the values */
762 rc = RegEnumValueW(hKey, index, valname,&size,0,0,0,0);
763 while (rc == ERROR_SUCCESS)
765 RegDeleteValueW(hKey,valname);
766 index++;
767 size = MAX_PATH+1;
768 rc = RegEnumValueW(hKey, index, valname, &size,0,0,0,0);
771 RegCloseKey(hKey);
772 hsvc->u.service.hkey = NULL;
774 /* delete the key */
775 RegDeleteKeyW(hsvc->u.service.sc_manager->u.manager.hkey_scm_db,
776 hsvc->u.service.name);
778 return TRUE;
782 /******************************************************************************
783 * StartServiceA [ADVAPI32.@]
785 * Start a service
787 * PARAMS
788 * hService [I] Handle of service
789 * dwNumServiceArgs [I] Number of arguments
790 * lpServiceArgVectors [I] Address of array of argument strings
792 * NOTES
793 * - NT implements this function using an obscure RPC call.
794 * - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc
795 * to get things like "%SystemRoot%\\System32\\service.exe" to load.
796 * - This will only work for shared address space. How should the service
797 * args be transferred when address spaces are separated?
798 * - Can only start one service at a time.
799 * - Has no concept of privilege.
801 * RETURNS
802 * Success: TRUE.
803 * Failure: FALSE
805 BOOL WINAPI
806 StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
807 LPCSTR *lpServiceArgVectors )
809 LPWSTR *lpwstr=NULL;
810 UNICODE_STRING usBuffer;
811 int i;
813 TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
815 if(dwNumServiceArgs)
816 lpwstr = (LPWSTR*) HeapAlloc( GetProcessHeap(), 0,
817 dwNumServiceArgs*sizeof(LPWSTR) );
818 else
819 lpwstr = NULL;
821 for(i=0; i<dwNumServiceArgs; i++)
823 RtlCreateUnicodeStringFromAsciiz (&usBuffer,lpServiceArgVectors[i]);
824 lpwstr[i]=usBuffer.Buffer;
827 StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
829 if(dwNumServiceArgs)
831 for(i=0; i<dwNumServiceArgs; i++)
832 HeapFree(GetProcessHeap(), 0, lpwstr[i]);
833 HeapFree(GetProcessHeap(), 0, lpwstr);
836 return TRUE;
840 /******************************************************************************
841 * StartServiceW [ADVAPI32.@]
843 * See StartServiceA.
845 BOOL WINAPI
846 StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
847 LPCWSTR *lpServiceArgVectors )
849 static const WCHAR _WaitServiceStartW[] = {'A','D','V','A','P','I','_','W',
850 'a','i','t','S','e','r','v','i',
851 'c','e','S','t','a','r','t',0};
852 static const WCHAR _ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
854 struct sc_handle *hsvc = hService;
855 WCHAR path[MAX_PATH],str[MAX_PATH];
856 DWORD type,size;
857 long r;
858 HANDLE data,wait;
859 PROCESS_INFORMATION procinfo;
860 STARTUPINFOW startupinfo;
861 TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,
862 lpServiceArgVectors);
864 size = sizeof(str);
865 r = RegQueryValueExW(hsvc->u.service.hkey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
866 if (r!=ERROR_SUCCESS)
867 return FALSE;
868 ExpandEnvironmentStringsW(str,path,sizeof(path));
870 TRACE("Starting service %s\n", debugstr_w(path) );
872 data = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
873 if (!data)
875 ERR("Couldn't create data semaphore\n");
876 return FALSE;
878 wait = CreateSemaphoreW(NULL,0,1,_WaitServiceStartW);
879 if (!wait)
881 ERR("Couldn't create wait semaphore\n");
882 return FALSE;
886 * FIXME: lpServiceArgsVectors need to be stored and returned to
887 * the service when it calls StartServiceCtrlDispatcher
889 * Chuck these in a global (yuk) so we can pass them to
890 * another process - address space separation will break this.
893 r = WaitForSingleObject(data,INFINITE);
895 if( r == WAIT_FAILED)
896 return FALSE;
898 FIXME("problematic because of address space separation.\n");
899 start_dwNumServiceArgs = dwNumServiceArgs;
900 start_lpServiceArgVectors = (LPWSTR *)lpServiceArgVectors;
902 ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
903 startupinfo.cb = sizeof(STARTUPINFOW);
905 r = CreateProcessW(NULL,
906 path,
907 NULL, /* process security attribs */
908 NULL, /* thread security attribs */
909 FALSE, /* inherit handles */
910 0, /* creation flags */
911 NULL, /* environment */
912 NULL, /* current directory */
913 &startupinfo, /* startup info */
914 &procinfo); /* process info */
916 if(r == FALSE)
918 ERR("Couldn't start process\n");
919 /* ReleaseSemaphore(data, 1, NULL);
920 return FALSE; */
923 /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
924 r = WaitForSingleObject(wait,30000);
926 ReleaseSemaphore(data, 1, NULL);
928 if( r == WAIT_FAILED)
929 return FALSE;
931 return TRUE;
934 /******************************************************************************
935 * QueryServiceStatus [ADVAPI32.@]
937 * PARAMS
938 * hService []
939 * lpservicestatus []
942 BOOL WINAPI
943 QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus )
945 struct sc_handle *hsvc = hService;
946 LONG r;
947 DWORD type, val, size;
949 FIXME("(%p,%p) partial\n",hService,lpservicestatus);
951 /* read the service type from the registry */
952 size = sizeof(val);
953 r = RegQueryValueExA(hsvc->u.service.hkey, "Type", NULL, &type, (LPBYTE)&val, &size);
954 if(type!=REG_DWORD)
956 ERR("invalid Type\n");
957 return FALSE;
959 lpservicestatus->dwServiceType = val;
960 /* FIXME: how are these determined or read from the registry? */
961 /* SERVICE: unavailable=0, stopped=1, starting=2, running=3? */;
962 lpservicestatus->dwCurrentState = 1;
963 lpservicestatus->dwControlsAccepted = 0;
964 lpservicestatus->dwWin32ExitCode = NO_ERROR;
965 lpservicestatus->dwServiceSpecificExitCode = 0;
966 lpservicestatus->dwCheckPoint = 0;
967 lpservicestatus->dwWaitHint = 0;
969 return TRUE;
972 /******************************************************************************
973 * QueryServiceStatusEx [ADVAPI32.@]
975 * Get information about a service.
977 * PARAMS
978 * hService [I] Handle to service to get information about
979 * InfoLevel [I] Level of information to get
980 * lpBuffer [O] Destination for requested information
981 * cbBufSize [I] Size of lpBuffer in bytes
982 * pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small
984 * RETURNS
985 * Success: TRUE
986 * FAILURE: FALSE
988 BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel,
989 LPBYTE lpBuffer, DWORD cbBufSize,
990 LPDWORD pcbBytesNeeded)
992 FIXME("stub\n");
993 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
994 return FALSE;
997 /******************************************************************************
998 * QueryServiceConfigA [ADVAPI32.@]
1000 BOOL WINAPI
1001 QueryServiceConfigA( SC_HANDLE hService,
1002 LPQUERY_SERVICE_CONFIGA lpServiceConfig,
1003 DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1005 static const CHAR szDisplayName[] = "DisplayName";
1006 static const CHAR szType[] = "Type";
1007 static const CHAR szStart[] = "Start";
1008 static const CHAR szError[] = "ErrorControl";
1009 static const CHAR szImagePath[] = "ImagePath";
1010 static const CHAR szGroup[] = "Group";
1011 static const CHAR szDependencies[] = "Dependencies";
1012 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1013 CHAR str_buffer[ MAX_PATH ];
1014 LONG r;
1015 DWORD type, val, sz, total, n;
1016 LPBYTE p;
1018 TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1019 cbBufSize, pcbBytesNeeded);
1021 /* calculate the size required first */
1022 total = sizeof (QUERY_SERVICE_CONFIGA);
1024 sz = sizeof(str_buffer);
1025 r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1026 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1028 sz = ExpandEnvironmentStringsA(str_buffer,NULL,0);
1029 if( 0 == sz ) return FALSE;
1031 total += sz;
1033 else
1035 /* FIXME: set last error */
1036 return FALSE;
1039 sz = 0;
1040 r = RegQueryValueExA( hKey, szGroup, 0, &type, NULL, &sz );
1041 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1042 total += sz;
1044 sz = 0;
1045 r = RegQueryValueExA( hKey, szDependencies, 0, &type, NULL, &sz );
1046 if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1047 total += sz;
1049 sz = 0;
1050 r = RegQueryValueExA( hKey, szStart, 0, &type, NULL, &sz );
1051 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1052 total += sz;
1054 sz = 0;
1055 r = RegQueryValueExA( hKey, szDisplayName, 0, &type, NULL, &sz );
1056 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1057 total += sz;
1059 /* if there's not enough memory, return an error */
1060 if( total > *pcbBytesNeeded )
1062 *pcbBytesNeeded = total;
1063 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1064 return FALSE;
1067 *pcbBytesNeeded = total;
1068 ZeroMemory( lpServiceConfig, total );
1070 sz = sizeof val;
1071 r = RegQueryValueExA( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1072 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1073 lpServiceConfig->dwServiceType = val;
1075 sz = sizeof val;
1076 r = RegQueryValueExA( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1077 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1078 lpServiceConfig->dwStartType = val;
1080 sz = sizeof val;
1081 r = RegQueryValueExA( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1082 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1083 lpServiceConfig->dwErrorControl = val;
1085 /* now do the strings */
1086 p = (LPBYTE) &lpServiceConfig[1];
1087 n = total - sizeof (QUERY_SERVICE_CONFIGA);
1089 sz = sizeof(str_buffer);
1090 r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1091 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1093 sz = ExpandEnvironmentStringsA(str_buffer, p, n);
1094 if( 0 == sz || sz > n ) return FALSE;
1096 lpServiceConfig->lpBinaryPathName = (LPSTR) p;
1097 p += sz;
1098 n -= sz;
1100 else
1102 /* FIXME: set last error */
1103 return FALSE;
1106 sz = n;
1107 r = RegQueryValueExA( hKey, szGroup, 0, &type, p, &sz );
1108 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1110 lpServiceConfig->lpLoadOrderGroup = (LPSTR) p;
1111 p += sz;
1112 n -= sz;
1115 sz = n;
1116 r = RegQueryValueExA( hKey, szDependencies, 0, &type, p, &sz );
1117 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1119 lpServiceConfig->lpDependencies = (LPSTR) p;
1120 p += sz;
1121 n -= sz;
1124 if( n < 0 )
1125 ERR("Buffer overflow!\n");
1127 TRACE("Image path = %s\n", lpServiceConfig->lpBinaryPathName );
1128 TRACE("Group = %s\n", lpServiceConfig->lpLoadOrderGroup );
1130 return TRUE;
1133 /******************************************************************************
1134 * QueryServiceConfigW [ADVAPI32.@]
1136 BOOL WINAPI
1137 QueryServiceConfigW( SC_HANDLE hService,
1138 LPQUERY_SERVICE_CONFIGW lpServiceConfig,
1139 DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1141 static const WCHAR szDisplayName[] = {
1142 'D','i','s','p','l','a','y','N','a','m','e', 0 };
1143 static const WCHAR szType[] = {'T','y','p','e',0};
1144 static const WCHAR szStart[] = {'S','t','a','r','t',0};
1145 static const WCHAR szError[] = {
1146 'E','r','r','o','r','C','o','n','t','r','o','l', 0};
1147 static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
1148 static const WCHAR szGroup[] = {'G','r','o','u','p',0};
1149 static const WCHAR szDependencies[] = {
1150 'D','e','p','e','n','d','e','n','c','i','e','s',0};
1151 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1152 WCHAR str_buffer[ MAX_PATH ];
1153 LONG r;
1154 DWORD type, val, sz, total, n;
1155 LPBYTE p;
1157 TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1158 cbBufSize, pcbBytesNeeded);
1160 /* calculate the size required first */
1161 total = sizeof (QUERY_SERVICE_CONFIGW);
1163 sz = sizeof(str_buffer);
1164 r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1165 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1167 sz = ExpandEnvironmentStringsW(str_buffer,NULL,0);
1168 if( 0 == sz ) return FALSE;
1170 total += sizeof(WCHAR) * sz;
1172 else
1174 /* FIXME: set last error */
1175 return FALSE;
1178 sz = 0;
1179 r = RegQueryValueExW( hKey, szGroup, 0, &type, NULL, &sz );
1180 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1181 total += sz;
1183 sz = 0;
1184 r = RegQueryValueExW( hKey, szDependencies, 0, &type, NULL, &sz );
1185 if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1186 total += sz;
1188 sz = 0;
1189 r = RegQueryValueExW( hKey, szStart, 0, &type, NULL, &sz );
1190 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1191 total += sz;
1193 sz = 0;
1194 r = RegQueryValueExW( hKey, szDisplayName, 0, &type, NULL, &sz );
1195 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1196 total += sz;
1198 /* if there's not enough memory, return an error */
1199 if( total > *pcbBytesNeeded )
1201 *pcbBytesNeeded = total;
1202 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1203 return FALSE;
1206 *pcbBytesNeeded = total;
1207 ZeroMemory( lpServiceConfig, total );
1209 sz = sizeof val;
1210 r = RegQueryValueExW( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1211 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1212 lpServiceConfig->dwServiceType = val;
1214 sz = sizeof val;
1215 r = RegQueryValueExW( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1216 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1217 lpServiceConfig->dwStartType = val;
1219 sz = sizeof val;
1220 r = RegQueryValueExW( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1221 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1222 lpServiceConfig->dwErrorControl = val;
1224 /* now do the strings */
1225 p = (LPBYTE) &lpServiceConfig[1];
1226 n = total - sizeof (QUERY_SERVICE_CONFIGW);
1228 sz = sizeof(str_buffer);
1229 r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1230 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1232 sz = ExpandEnvironmentStringsW(str_buffer, (LPWSTR) p, n);
1233 sz *= sizeof(WCHAR);
1234 if( 0 == sz || sz > n ) return FALSE;
1236 lpServiceConfig->lpBinaryPathName = (LPWSTR) p;
1237 p += sz;
1238 n -= sz;
1240 else
1242 /* FIXME: set last error */
1243 return FALSE;
1246 sz = n;
1247 r = RegQueryValueExW( hKey, szGroup, 0, &type, p, &sz );
1248 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1250 lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p;
1251 p += sz;
1252 n -= sz;
1255 sz = n;
1256 r = RegQueryValueExW( hKey, szDependencies, 0, &type, p, &sz );
1257 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1259 lpServiceConfig->lpDependencies = (LPWSTR) p;
1260 p += sz;
1261 n -= sz;
1264 if( n < 0 )
1265 ERR("Buffer overflow!\n");
1267 TRACE("Image path = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) );
1268 TRACE("Group = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) );
1270 return TRUE;
1273 /******************************************************************************
1274 * ChangeServiceConfigW [ADVAPI32.@]
1276 BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType,
1277 DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
1278 LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies,
1279 LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName)
1281 FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1282 hService, dwServiceType, dwStartType, dwErrorControl,
1283 debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup),
1284 lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName),
1285 debugstr_w(lpPassword), debugstr_w(lpDisplayName) );
1286 return TRUE;
1289 /******************************************************************************
1290 * ChangeServiceConfigA [ADVAPI32.@]
1292 BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType,
1293 DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName,
1294 LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies,
1295 LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName)
1297 FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1298 hService, dwServiceType, dwStartType, dwErrorControl,
1299 debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup),
1300 lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName),
1301 debugstr_a(lpPassword), debugstr_a(lpDisplayName) );
1302 return TRUE;
1305 /******************************************************************************
1306 * ChangeServiceConfig2A [ADVAPI32.@]
1308 BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel,
1309 LPVOID lpInfo)
1311 FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1312 return TRUE;
1315 /******************************************************************************
1316 * ChangeServiceConfig2W [ADVAPI32.@]
1318 BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel,
1319 LPVOID lpInfo)
1321 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1323 if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
1325 static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
1326 LPSERVICE_DESCRIPTIONW sd = (LPSERVICE_DESCRIPTIONW)lpInfo;
1327 if (sd->lpDescription)
1329 TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription));
1330 if (sd->lpDescription[0] == 0)
1331 RegDeleteValueW(hKey,szDescription);
1332 else
1333 RegSetValueExW(hKey, szDescription, 0, REG_SZ,
1334 (LPVOID)sd->lpDescription,
1335 sizeof(WCHAR)*(strlenW(sd->lpDescription)+1));
1338 else
1339 FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1340 return TRUE;