Status update.
[wine.git] / dlls / advapi32 / service.c
blob8401435ee58509d00e365fd86997676126d4e720
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 };
46 static const WCHAR szSCMLock[] = {'A','D','V','A','P','I','_','S','C','M',
47 'L','O','C','K',0};
49 /******************************************************************************
50 * SC_HANDLEs
53 #define MAX_SERVICE_NAME 256
55 typedef enum { SC_HTYPE_MANAGER, SC_HTYPE_SERVICE } SC_HANDLE_TYPE;
57 struct sc_handle;
59 struct sc_manager /* SCM handle */
61 HKEY hkey_scm_db; /* handle to services database in the registry */
62 LONG ref_count; /* handle must remain alive until any related service */
63 /* handle exists because DeleteService requires it */
66 struct sc_service /* service handle */
68 HKEY hkey; /* handle to service entry in the registry (under hkey_scm_db) */
69 struct sc_handle *sc_manager; /* pointer to SCM handle */
70 WCHAR name[ MAX_SERVICE_NAME ];
73 struct sc_handle
75 SC_HANDLE_TYPE htype;
76 union
78 struct sc_manager manager;
79 struct sc_service service;
80 } u;
83 static struct sc_handle* alloc_sc_handle( SC_HANDLE_TYPE htype )
85 struct sc_handle *retval;
87 retval = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct sc_handle) );
88 if( retval != NULL )
90 retval->htype = htype;
92 TRACE("SC_HANDLE type=%d -> %p\n",htype,retval);
93 return retval;
96 static void free_sc_handle( struct sc_handle* handle )
98 if( NULL == handle )
99 return;
101 switch( handle->htype )
103 case SC_HTYPE_MANAGER:
105 if( InterlockedDecrement( &handle->u.manager.ref_count ) )
106 /* there are references to this handle */
107 return;
109 if( handle->u.manager.hkey_scm_db )
110 RegCloseKey( handle->u.manager.hkey_scm_db );
111 break;
114 case SC_HTYPE_SERVICE:
116 struct sc_handle *h = handle->u.service.sc_manager;
118 if( h )
120 /* release SCM handle */
121 if( 0 == InterlockedDecrement( &h->u.manager.ref_count ) )
123 /* it's time to destroy SCM handle */
124 if( h->u.manager.hkey_scm_db )
125 RegCloseKey( h->u.manager.hkey_scm_db );
127 TRACE("SC_HANDLE (SCM) %p type=%d\n",h,h->htype);
129 HeapFree( GetProcessHeap(), 0, h );
132 if( handle->u.service.hkey )
133 RegCloseKey( handle->u.service.hkey );
134 break;
138 TRACE("SC_HANDLE %p type=%d\n",handle,handle->htype);
140 HeapFree( GetProcessHeap(), 0, handle );
143 static void init_service_handle( struct sc_handle* handle,
144 struct sc_handle* sc_manager,
145 HKEY hKey, LPCWSTR lpServiceName )
147 /* init sc_service structure */
148 handle->u.service.hkey = hKey;
149 lstrcpynW( handle->u.service.name, lpServiceName, MAX_SERVICE_NAME );
151 /* add reference to SCM handle */
152 InterlockedIncrement( &sc_manager->u.manager.ref_count );
153 handle->u.service.sc_manager = sc_manager;
156 /******************************************************************************
157 * EnumServicesStatusA [ADVAPI32.@]
159 BOOL WINAPI
160 EnumServicesStatusA( SC_HANDLE hSCManager, DWORD dwServiceType,
161 DWORD dwServiceState, LPENUM_SERVICE_STATUSA lpServices,
162 DWORD cbBufSize, LPDWORD pcbBytesNeeded,
163 LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
164 { FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager,
165 dwServiceType, dwServiceState, lpServices, cbBufSize,
166 pcbBytesNeeded, lpServicesReturned, lpResumeHandle);
167 SetLastError (ERROR_ACCESS_DENIED);
168 return FALSE;
171 /******************************************************************************
172 * EnumServicesStatusW [ADVAPI32.@]
174 BOOL WINAPI
175 EnumServicesStatusW( SC_HANDLE hSCManager, DWORD dwServiceType,
176 DWORD dwServiceState, LPENUM_SERVICE_STATUSW lpServices,
177 DWORD cbBufSize, LPDWORD pcbBytesNeeded,
178 LPDWORD lpServicesReturned, LPDWORD lpResumeHandle )
179 { FIXME("%p type=%lx state=%lx %p %lx %p %p %p\n", hSCManager,
180 dwServiceType, dwServiceState, lpServices, cbBufSize,
181 pcbBytesNeeded, lpServicesReturned, lpResumeHandle);
182 SetLastError (ERROR_ACCESS_DENIED);
183 return FALSE;
186 /******************************************************************************
187 * StartServiceCtrlDispatcherA [ADVAPI32.@]
189 BOOL WINAPI
190 StartServiceCtrlDispatcherA( LPSERVICE_TABLE_ENTRYA servent )
192 LPSERVICE_MAIN_FUNCTIONA fpMain;
193 HANDLE wait;
194 DWORD dwNumServiceArgs ;
195 LPWSTR *lpArgVecW;
196 LPSTR *lpArgVecA;
197 unsigned int i;
199 TRACE("(%p)\n", servent);
200 wait = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
201 if (!wait)
203 ERR("Couldn't create data semaphore\n");
204 return FALSE;
207 dwNumServiceArgs = start_dwNumServiceArgs;
208 lpArgVecW = start_lpServiceArgVectors;
210 ReleaseSemaphore(wait, 1, NULL);
212 /* Convert the Unicode arg vectors back to ASCII */
213 if(dwNumServiceArgs)
214 lpArgVecA = (LPSTR*) HeapAlloc( GetProcessHeap(), 0,
215 dwNumServiceArgs*sizeof(LPSTR) );
216 else
217 lpArgVecA = NULL;
219 for(i=0; i<dwNumServiceArgs; i++)
220 lpArgVecA[i]=HEAP_strdupWtoA(GetProcessHeap(), 0, lpArgVecW[i]);
222 /* FIXME: should we blindly start all services? */
223 while (servent->lpServiceName) {
224 TRACE("%s at %p)\n", debugstr_a(servent->lpServiceName),servent);
225 fpMain = servent->lpServiceProc;
227 /* try to start the service */
228 fpMain( dwNumServiceArgs, lpArgVecA);
230 servent++;
233 if(dwNumServiceArgs)
235 /* free arg strings */
236 for(i=0; i<dwNumServiceArgs; i++)
237 HeapFree(GetProcessHeap(), 0, lpArgVecA[i]);
238 HeapFree(GetProcessHeap(), 0, lpArgVecA);
241 return TRUE;
244 /******************************************************************************
245 * StartServiceCtrlDispatcherW [ADVAPI32.@]
247 * PARAMS
248 * servent []
250 BOOL WINAPI
251 StartServiceCtrlDispatcherW( LPSERVICE_TABLE_ENTRYW servent )
253 LPSERVICE_MAIN_FUNCTIONW fpMain;
254 HANDLE wait;
255 DWORD dwNumServiceArgs ;
256 LPWSTR *lpServiceArgVectors ;
258 TRACE("(%p)\n", servent);
259 wait = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, _ServiceStartDataW);
260 if(wait == 0)
262 ERR("Couldn't find wait semaphore\n");
263 ERR("perhaps you need to start services using StartService\n");
264 return FALSE;
267 dwNumServiceArgs = start_dwNumServiceArgs;
268 lpServiceArgVectors = start_lpServiceArgVectors;
270 ReleaseSemaphore(wait, 1, NULL);
272 /* FIXME: should we blindly start all services? */
273 while (servent->lpServiceName) {
274 TRACE("%s at %p)\n", debugstr_w(servent->lpServiceName),servent);
275 fpMain = servent->lpServiceProc;
277 /* try to start the service */
278 fpMain( dwNumServiceArgs, lpServiceArgVectors);
280 servent++;
283 return TRUE;
286 /******************************************************************************
287 * LockServiceDatabase [ADVAPI32.@]
289 LPVOID WINAPI LockServiceDatabase (SC_HANDLE hSCManager)
291 HANDLE ret;
293 TRACE("%p\n",hSCManager);
295 ret = CreateFileMappingW( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
296 0, MAX_SERVICE_NAME * sizeof(WCHAR), szSCMLock );
297 if( ret && GetLastError() == ERROR_ALREADY_EXISTS )
299 CloseHandle( ret );
300 ret = NULL;
301 SetLastError( ERROR_SERVICE_DATABASE_LOCKED );
304 TRACE("returning %p\n", ret);
306 return ret;
309 /******************************************************************************
310 * UnlockServiceDatabase [ADVAPI32.@]
312 BOOL WINAPI UnlockServiceDatabase (LPVOID ScLock)
314 TRACE("%p\n",ScLock);
316 return CloseHandle( (HANDLE) ScLock );
319 /******************************************************************************
320 * RegisterServiceCtrlHandlerA [ADVAPI32.@]
322 SERVICE_STATUS_HANDLE WINAPI
323 RegisterServiceCtrlHandlerA( LPCSTR lpServiceName,
324 LPHANDLER_FUNCTION lpfHandler )
325 { FIXME("%s %p\n", lpServiceName, lpfHandler);
326 return 0xcacacafe;
329 /******************************************************************************
330 * RegisterServiceCtrlHandlerW [ADVAPI32.@]
332 * PARAMS
333 * lpServiceName []
334 * lpfHandler []
336 SERVICE_STATUS_HANDLE WINAPI
337 RegisterServiceCtrlHandlerW( LPCWSTR lpServiceName,
338 LPHANDLER_FUNCTION lpfHandler )
339 { FIXME("%s %p\n", debugstr_w(lpServiceName), lpfHandler);
340 return 0xcacacafe;
343 /******************************************************************************
344 * SetServiceStatus [ADVAPI32.@]
346 * PARAMS
347 * hService []
348 * lpStatus []
350 BOOL WINAPI
351 SetServiceStatus( SERVICE_STATUS_HANDLE hService, LPSERVICE_STATUS lpStatus )
352 { FIXME("0x%lx %p\n",hService, lpStatus);
353 TRACE("\tType:%lx\n",lpStatus->dwServiceType);
354 TRACE("\tState:%lx\n",lpStatus->dwCurrentState);
355 TRACE("\tControlAccepted:%lx\n",lpStatus->dwControlsAccepted);
356 TRACE("\tExitCode:%lx\n",lpStatus->dwWin32ExitCode);
357 TRACE("\tServiceExitCode:%lx\n",lpStatus->dwServiceSpecificExitCode);
358 TRACE("\tCheckPoint:%lx\n",lpStatus->dwCheckPoint);
359 TRACE("\tWaitHint:%lx\n",lpStatus->dwWaitHint);
360 return TRUE;
363 /******************************************************************************
364 * OpenSCManagerA [ADVAPI32.@]
366 * Establish a connection to the service control manager and open its database.
368 * PARAMS
369 * lpMachineName [I] Pointer to machine name string
370 * lpDatabaseName [I] Pointer to database name string
371 * dwDesiredAccess [I] Type of access
373 * RETURNS
374 * Success: A Handle to the service control manager database
375 * Failure: NULL
377 SC_HANDLE WINAPI OpenSCManagerA( LPCSTR lpMachineName, LPCSTR lpDatabaseName,
378 DWORD dwDesiredAccess )
380 UNICODE_STRING lpMachineNameW;
381 UNICODE_STRING lpDatabaseNameW;
382 SC_HANDLE ret;
384 RtlCreateUnicodeStringFromAsciiz (&lpMachineNameW,lpMachineName);
385 RtlCreateUnicodeStringFromAsciiz (&lpDatabaseNameW,lpDatabaseName);
386 ret = OpenSCManagerW(lpMachineNameW.Buffer,lpDatabaseNameW.Buffer, dwDesiredAccess);
387 RtlFreeUnicodeString(&lpDatabaseNameW);
388 RtlFreeUnicodeString(&lpMachineNameW);
389 return ret;
392 /******************************************************************************
393 * OpenSCManagerW [ADVAPI32.@]
395 * See OpenSCManagerA.
397 SC_HANDLE WINAPI OpenSCManagerW( LPCWSTR lpMachineName, LPCWSTR lpDatabaseName,
398 DWORD dwDesiredAccess )
400 struct sc_handle *retval;
401 HKEY hReg;
402 LONG r;
404 TRACE("(%s,%s,0x%08lx)\n", debugstr_w(lpMachineName),
405 debugstr_w(lpDatabaseName), dwDesiredAccess);
408 * FIXME: what is lpDatabaseName?
409 * It should be set to "SERVICES_ACTIVE_DATABASE" according to
410 * docs, but what if it isn't?
413 retval = alloc_sc_handle( SC_HTYPE_MANAGER );
414 if( NULL == retval ) return NULL;
416 retval->u.manager.ref_count = 1;
418 r = RegConnectRegistryW(lpMachineName,HKEY_LOCAL_MACHINE,&hReg);
419 if (r!=ERROR_SUCCESS)
420 goto error;
422 r = RegOpenKeyExW(hReg, szServiceManagerKey,
423 0, KEY_ALL_ACCESS, &retval->u.manager.hkey_scm_db);
424 RegCloseKey( hReg );
425 if (r!=ERROR_SUCCESS)
426 goto error;
428 TRACE("returning %p\n", retval);
430 return (SC_HANDLE) retval;
432 error:
433 free_sc_handle( retval );
434 return NULL;
438 /******************************************************************************
439 * AllocateLocallyUniqueId [ADVAPI32.@]
441 * PARAMS
442 * lpluid []
444 BOOL WINAPI
445 AllocateLocallyUniqueId( PLUID lpluid )
447 lpluid->LowPart = time(NULL);
448 lpluid->HighPart = 0;
449 return TRUE;
453 /******************************************************************************
454 * ControlService [ADVAPI32.@]
456 * Send a control code to a service.
458 * PARAMS
459 * hService [I] Handle of the service control manager database
460 * dwControl [I] Control code to send (SERVICE_CONTROL_* flags from "winsvc.h")
461 * lpServiceStatus [O] Destination for the status of the service, if available
463 * RETURNS
464 * Success: TRUE.
465 * Failure: FALSE.
467 BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
468 LPSERVICE_STATUS lpServiceStatus )
470 FIXME("(%p,%ld,%p): stub\n",hService,dwControl,lpServiceStatus);
471 return TRUE;
475 /******************************************************************************
476 * CloseServiceHandle [ADVAPI32.@]
478 * Close a handle to a service or the service control manager database.
480 * PARAMS
481 * hSCObject [I] Handle to service or service control manager database
483 * RETURNS
484 * Success: TRUE
485 * Failure: FALSE
487 BOOL WINAPI
488 CloseServiceHandle( SC_HANDLE hSCObject )
490 TRACE("(%p)\n", hSCObject);
492 free_sc_handle( (struct sc_handle*) hSCObject );
494 return TRUE;
498 /******************************************************************************
499 * OpenServiceA [ADVAPI32.@]
501 * Open a handle to a service.
503 * PARAMS
504 * hSCManager [I] Handle of the service control manager database
505 * lpServiceName [I] Name of the service to open
506 * dwDesiredAccess [I] Access required to the service
508 * RETURNS
509 * Success: Handle to the service
510 * Failure: NULL
512 SC_HANDLE WINAPI OpenServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
513 DWORD dwDesiredAccess )
515 UNICODE_STRING lpServiceNameW;
516 SC_HANDLE ret;
517 RtlCreateUnicodeStringFromAsciiz (&lpServiceNameW,lpServiceName);
518 if(lpServiceName)
519 TRACE("Request for service %s\n",lpServiceName);
520 else
521 return FALSE;
522 ret = OpenServiceW( hSCManager, lpServiceNameW.Buffer, dwDesiredAccess);
523 RtlFreeUnicodeString(&lpServiceNameW);
524 return ret;
528 /******************************************************************************
529 * OpenServiceW [ADVAPI32.@]
531 * See OpenServiceA.
533 SC_HANDLE WINAPI OpenServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
534 DWORD dwDesiredAccess)
536 struct sc_handle *hscm = hSCManager;
537 struct sc_handle *retval;
538 HKEY hKey;
539 long r;
541 TRACE("(%p,%p,%ld)\n",hSCManager, lpServiceName,
542 dwDesiredAccess);
544 retval = alloc_sc_handle( SC_HTYPE_SERVICE );
545 if( NULL == retval )
546 return NULL;
548 r = RegOpenKeyExW( hscm->u.manager.hkey_scm_db,
549 lpServiceName, 0, KEY_ALL_ACCESS, &hKey );
550 if (r!=ERROR_SUCCESS)
552 free_sc_handle( retval );
553 SetLastError( ERROR_SERVICE_DOES_NOT_EXIST );
554 return NULL;
557 init_service_handle( retval, hscm, hKey, lpServiceName );
559 TRACE("returning %p\n",retval);
561 return (SC_HANDLE) retval;
564 /******************************************************************************
565 * CreateServiceW [ADVAPI32.@]
567 SC_HANDLE WINAPI
568 CreateServiceW( SC_HANDLE hSCManager, LPCWSTR lpServiceName,
569 LPCWSTR lpDisplayName, DWORD dwDesiredAccess,
570 DWORD dwServiceType, DWORD dwStartType,
571 DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
572 LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
573 LPCWSTR lpDependencies, LPCWSTR lpServiceStartName,
574 LPCWSTR lpPassword )
576 struct sc_handle *hscm = hSCManager;
577 struct sc_handle *retval;
578 HKEY hKey;
579 LONG r;
580 DWORD dp;
581 static const WCHAR szDisplayName[] = { 'D','i','s','p','l','a','y','N','a','m','e', 0 };
582 static const WCHAR szType[] = {'T','y','p','e',0};
583 static const WCHAR szStart[] = {'S','t','a','r','t',0};
584 static const WCHAR szError[] = {'E','r','r','o','r','C','o','n','t','r','o','l', 0};
585 static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
586 static const WCHAR szGroup[] = {'G','r','o','u','p',0};
587 static const WCHAR szDependencies[] = { 'D','e','p','e','n','d','e','n','c','i','e','s',0};
589 FIXME("%p %s %s\n", hSCManager,
590 debugstr_w(lpServiceName), debugstr_w(lpDisplayName));
592 retval = alloc_sc_handle( SC_HTYPE_SERVICE );
593 if( NULL == retval )
594 return NULL;
596 r = RegCreateKeyExW(hscm->u.manager.hkey_scm_db, lpServiceName, 0, NULL,
597 REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dp);
598 if (r!=ERROR_SUCCESS)
599 goto error;
601 init_service_handle( retval, hscm, hKey, lpServiceName );
603 if (dp != REG_CREATED_NEW_KEY)
604 goto error;
606 if(lpDisplayName)
608 r = RegSetValueExW(hKey, szDisplayName, 0, REG_SZ, (LPBYTE)lpDisplayName,
609 (strlenW(lpDisplayName)+1)*sizeof(WCHAR) );
610 if (r!=ERROR_SUCCESS)
611 goto error;
614 r = RegSetValueExW(hKey, szType, 0, REG_DWORD, (LPVOID)&dwServiceType, sizeof (DWORD) );
615 if (r!=ERROR_SUCCESS)
616 goto error;
618 r = RegSetValueExW(hKey, szStart, 0, REG_DWORD, (LPVOID)&dwStartType, sizeof (DWORD) );
619 if (r!=ERROR_SUCCESS)
620 goto error;
622 r = RegSetValueExW(hKey, szError, 0, REG_DWORD,
623 (LPVOID)&dwErrorControl, sizeof (DWORD) );
624 if (r!=ERROR_SUCCESS)
625 goto error;
627 if(lpBinaryPathName)
629 r = RegSetValueExW(hKey, szImagePath, 0, REG_SZ, (LPBYTE)lpBinaryPathName,
630 (strlenW(lpBinaryPathName)+1)*sizeof(WCHAR) );
631 if (r!=ERROR_SUCCESS)
632 goto error;
635 if(lpLoadOrderGroup)
637 r = RegSetValueExW(hKey, szGroup, 0, REG_SZ, (LPBYTE)lpLoadOrderGroup,
638 (strlenW(lpLoadOrderGroup)+1)*sizeof(WCHAR) );
639 if (r!=ERROR_SUCCESS)
640 goto error;
643 if(lpDependencies)
645 DWORD len = 0;
647 /* determine the length of a double null terminated multi string */
648 do {
649 len += (strlenW(&lpDependencies[len])+1);
650 } while (lpDependencies[len++]);
652 r = RegSetValueExW(hKey, szDependencies, 0, REG_MULTI_SZ,
653 (LPBYTE)lpDependencies, len );
654 if (r!=ERROR_SUCCESS)
655 goto error;
658 if(lpPassword)
660 FIXME("Don't know how to add a Password for a service.\n");
663 if(lpServiceStartName)
665 FIXME("Don't know how to add a ServiceStartName for a service.\n");
668 return (SC_HANDLE) retval;
670 error:
671 free_sc_handle( retval );
672 return NULL;
676 static inline LPWSTR SERV_dup( LPCSTR str )
678 UINT len;
679 LPWSTR wstr;
681 if( !str )
682 return NULL;
683 len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
684 wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
685 MultiByteToWideChar( CP_ACP, 0, str, -1, wstr, len );
686 return wstr;
689 static inline LPWSTR SERV_dupmulti( LPCSTR str )
691 UINT len = 0, n = 0;
692 LPWSTR wstr;
694 if( !str )
695 return NULL;
696 do {
697 len += MultiByteToWideChar( CP_ACP, 0, &str[n], -1, NULL, 0 );
698 n += (strlen( &str[n] ) + 1);
699 } while (str[n]);
700 len++;
701 n++;
703 wstr = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
704 MultiByteToWideChar( CP_ACP, 0, str, n, wstr, len );
705 return wstr;
708 static inline VOID SERV_free( LPWSTR wstr )
710 HeapFree( GetProcessHeap(), 0, wstr );
713 /******************************************************************************
714 * CreateServiceA [ADVAPI32.@]
716 SC_HANDLE WINAPI
717 CreateServiceA( SC_HANDLE hSCManager, LPCSTR lpServiceName,
718 LPCSTR lpDisplayName, DWORD dwDesiredAccess,
719 DWORD dwServiceType, DWORD dwStartType,
720 DWORD dwErrorControl, LPCSTR lpBinaryPathName,
721 LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId,
722 LPCSTR lpDependencies, LPCSTR lpServiceStartName,
723 LPCSTR lpPassword )
725 LPWSTR lpServiceNameW, lpDisplayNameW, lpBinaryPathNameW,
726 lpLoadOrderGroupW, lpDependenciesW, lpServiceStartNameW, lpPasswordW;
727 SC_HANDLE r;
729 TRACE("%p %s %s\n", hSCManager,
730 debugstr_a(lpServiceName), debugstr_a(lpDisplayName));
732 lpServiceNameW = SERV_dup( lpServiceName );
733 lpDisplayNameW = SERV_dup( lpDisplayName );
734 lpBinaryPathNameW = SERV_dup( lpBinaryPathName );
735 lpLoadOrderGroupW = SERV_dup( lpLoadOrderGroup );
736 lpDependenciesW = SERV_dupmulti( lpDependencies );
737 lpServiceStartNameW = SERV_dup( lpServiceStartName );
738 lpPasswordW = SERV_dup( lpPassword );
740 r = CreateServiceW( hSCManager, lpServiceNameW, lpDisplayNameW,
741 dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
742 lpBinaryPathNameW, lpLoadOrderGroupW, lpdwTagId,
743 lpDependenciesW, lpServiceStartNameW, lpPasswordW );
745 SERV_free( lpServiceNameW );
746 SERV_free( lpDisplayNameW );
747 SERV_free( lpBinaryPathNameW );
748 SERV_free( lpLoadOrderGroupW );
749 SERV_free( lpDependenciesW );
750 SERV_free( lpServiceStartNameW );
751 SERV_free( lpPasswordW );
753 return r;
757 /******************************************************************************
758 * DeleteService [ADVAPI32.@]
760 * Delete a service from the service control manager database.
762 * PARAMS
763 * hService [I] Handle of the service to delete
765 * RETURNS
766 * Success: TRUE
767 * Failure: FALSE
769 BOOL WINAPI DeleteService( SC_HANDLE hService )
771 struct sc_handle *hsvc = hService;
772 HKEY hKey = hsvc->u.service.hkey;
773 WCHAR valname[MAX_PATH+1];
774 INT index = 0;
775 LONG rc;
776 DWORD size;
778 size = MAX_PATH+1;
779 /* Clean out the values */
780 rc = RegEnumValueW(hKey, index, valname,&size,0,0,0,0);
781 while (rc == ERROR_SUCCESS)
783 RegDeleteValueW(hKey,valname);
784 index++;
785 size = MAX_PATH+1;
786 rc = RegEnumValueW(hKey, index, valname, &size,0,0,0,0);
789 RegCloseKey(hKey);
790 hsvc->u.service.hkey = NULL;
792 /* delete the key */
793 RegDeleteKeyW(hsvc->u.service.sc_manager->u.manager.hkey_scm_db,
794 hsvc->u.service.name);
796 return TRUE;
800 /******************************************************************************
801 * StartServiceA [ADVAPI32.@]
803 * Start a service
805 * PARAMS
806 * hService [I] Handle of service
807 * dwNumServiceArgs [I] Number of arguments
808 * lpServiceArgVectors [I] Address of array of argument strings
810 * NOTES
811 * - NT implements this function using an obscure RPC call.
812 * - You might need to do a "setenv SystemRoot \\WINNT" in your .cshrc
813 * to get things like "%SystemRoot%\\System32\\service.exe" to load.
814 * - This will only work for shared address space. How should the service
815 * args be transferred when address spaces are separated?
816 * - Can only start one service at a time.
817 * - Has no concept of privilege.
819 * RETURNS
820 * Success: TRUE.
821 * Failure: FALSE
823 BOOL WINAPI
824 StartServiceA( SC_HANDLE hService, DWORD dwNumServiceArgs,
825 LPCSTR *lpServiceArgVectors )
827 LPWSTR *lpwstr=NULL;
828 UNICODE_STRING usBuffer;
829 unsigned int i;
831 TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,lpServiceArgVectors);
833 if(dwNumServiceArgs)
834 lpwstr = (LPWSTR*) HeapAlloc( GetProcessHeap(), 0,
835 dwNumServiceArgs*sizeof(LPWSTR) );
836 else
837 lpwstr = NULL;
839 for(i=0; i<dwNumServiceArgs; i++)
841 RtlCreateUnicodeStringFromAsciiz (&usBuffer,lpServiceArgVectors[i]);
842 lpwstr[i]=usBuffer.Buffer;
845 StartServiceW(hService, dwNumServiceArgs, (LPCWSTR *)lpwstr);
847 if(dwNumServiceArgs)
849 for(i=0; i<dwNumServiceArgs; i++)
850 HeapFree(GetProcessHeap(), 0, lpwstr[i]);
851 HeapFree(GetProcessHeap(), 0, lpwstr);
854 return TRUE;
858 /******************************************************************************
859 * StartServiceW [ADVAPI32.@]
861 * See StartServiceA.
863 BOOL WINAPI
864 StartServiceW( SC_HANDLE hService, DWORD dwNumServiceArgs,
865 LPCWSTR *lpServiceArgVectors )
867 static const WCHAR _WaitServiceStartW[] = {'A','D','V','A','P','I','_','W',
868 'a','i','t','S','e','r','v','i',
869 'c','e','S','t','a','r','t',0};
870 static const WCHAR _ImagePathW[] = {'I','m','a','g','e','P','a','t','h',0};
872 struct sc_handle *hsvc = hService;
873 WCHAR path[MAX_PATH],str[MAX_PATH];
874 DWORD type,size;
875 long r;
876 HANDLE data,wait;
877 PROCESS_INFORMATION procinfo;
878 STARTUPINFOW startupinfo;
879 TRACE("(%p,%ld,%p)\n",hService,dwNumServiceArgs,
880 lpServiceArgVectors);
882 size = sizeof(str);
883 r = RegQueryValueExW(hsvc->u.service.hkey, _ImagePathW, NULL, &type, (LPVOID)str, &size);
884 if (r!=ERROR_SUCCESS)
885 return FALSE;
886 ExpandEnvironmentStringsW(str,path,sizeof(path));
888 TRACE("Starting service %s\n", debugstr_w(path) );
890 data = CreateSemaphoreW(NULL,1,1,_ServiceStartDataW);
891 if (!data)
893 ERR("Couldn't create data semaphore\n");
894 return FALSE;
896 wait = CreateSemaphoreW(NULL,0,1,_WaitServiceStartW);
897 if (!wait)
899 ERR("Couldn't create wait semaphore\n");
900 return FALSE;
904 * FIXME: lpServiceArgsVectors need to be stored and returned to
905 * the service when it calls StartServiceCtrlDispatcher
907 * Chuck these in a global (yuk) so we can pass them to
908 * another process - address space separation will break this.
911 r = WaitForSingleObject(data,INFINITE);
913 if( r == WAIT_FAILED)
914 return FALSE;
916 FIXME("problematic because of address space separation.\n");
917 start_dwNumServiceArgs = dwNumServiceArgs;
918 start_lpServiceArgVectors = (LPWSTR *)lpServiceArgVectors;
920 ZeroMemory(&startupinfo,sizeof(STARTUPINFOW));
921 startupinfo.cb = sizeof(STARTUPINFOW);
923 r = CreateProcessW(NULL,
924 path,
925 NULL, /* process security attribs */
926 NULL, /* thread security attribs */
927 FALSE, /* inherit handles */
928 0, /* creation flags */
929 NULL, /* environment */
930 NULL, /* current directory */
931 &startupinfo, /* startup info */
932 &procinfo); /* process info */
934 if(r == FALSE)
936 ERR("Couldn't start process\n");
937 /* ReleaseSemaphore(data, 1, NULL);
938 return FALSE; */
941 /* docs for StartServiceCtrlDispatcher say this should be 30 sec */
942 r = WaitForSingleObject(wait,30000);
944 ReleaseSemaphore(data, 1, NULL);
946 if( r == WAIT_FAILED)
947 return FALSE;
949 return TRUE;
952 /******************************************************************************
953 * QueryServiceStatus [ADVAPI32.@]
955 * PARAMS
956 * hService []
957 * lpservicestatus []
960 BOOL WINAPI
961 QueryServiceStatus( SC_HANDLE hService, LPSERVICE_STATUS lpservicestatus )
963 struct sc_handle *hsvc = hService;
964 LONG r;
965 DWORD type, val, size;
967 FIXME("(%p,%p) partial\n",hService,lpservicestatus);
969 /* read the service type from the registry */
970 size = sizeof(val);
971 r = RegQueryValueExA(hsvc->u.service.hkey, "Type", NULL, &type, (LPBYTE)&val, &size);
972 if(type!=REG_DWORD)
974 ERR("invalid Type\n");
975 return FALSE;
977 lpservicestatus->dwServiceType = val;
978 /* FIXME: how are these determined or read from the registry? */
979 /* SERVICE: unavailable=0, stopped=1, starting=2, running=3? */;
980 lpservicestatus->dwCurrentState = 1;
981 lpservicestatus->dwControlsAccepted = 0;
982 lpservicestatus->dwWin32ExitCode = NO_ERROR;
983 lpservicestatus->dwServiceSpecificExitCode = 0;
984 lpservicestatus->dwCheckPoint = 0;
985 lpservicestatus->dwWaitHint = 0;
987 return TRUE;
990 /******************************************************************************
991 * QueryServiceStatusEx [ADVAPI32.@]
993 * Get information about a service.
995 * PARAMS
996 * hService [I] Handle to service to get information about
997 * InfoLevel [I] Level of information to get
998 * lpBuffer [O] Destination for requested information
999 * cbBufSize [I] Size of lpBuffer in bytes
1000 * pcbBytesNeeded [O] Destination for number of bytes needed, if cbBufSize is too small
1002 * RETURNS
1003 * Success: TRUE
1004 * FAILURE: FALSE
1006 BOOL WINAPI QueryServiceStatusEx(SC_HANDLE hService, SC_STATUS_TYPE InfoLevel,
1007 LPBYTE lpBuffer, DWORD cbBufSize,
1008 LPDWORD pcbBytesNeeded)
1010 FIXME("stub\n");
1011 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1012 return FALSE;
1015 /******************************************************************************
1016 * QueryServiceConfigA [ADVAPI32.@]
1018 BOOL WINAPI
1019 QueryServiceConfigA( SC_HANDLE hService,
1020 LPQUERY_SERVICE_CONFIGA lpServiceConfig,
1021 DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1023 static const CHAR szDisplayName[] = "DisplayName";
1024 static const CHAR szType[] = "Type";
1025 static const CHAR szStart[] = "Start";
1026 static const CHAR szError[] = "ErrorControl";
1027 static const CHAR szImagePath[] = "ImagePath";
1028 static const CHAR szGroup[] = "Group";
1029 static const CHAR szDependencies[] = "Dependencies";
1030 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1031 CHAR str_buffer[ MAX_PATH ];
1032 LONG r;
1033 DWORD type, val, sz, total, n;
1034 LPBYTE p;
1036 TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1037 cbBufSize, pcbBytesNeeded);
1039 /* calculate the size required first */
1040 total = sizeof (QUERY_SERVICE_CONFIGA);
1042 sz = sizeof(str_buffer);
1043 r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1044 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1046 sz = ExpandEnvironmentStringsA(str_buffer,NULL,0);
1047 if( 0 == sz ) return FALSE;
1049 total += sz;
1051 else
1053 /* FIXME: set last error */
1054 return FALSE;
1057 sz = 0;
1058 r = RegQueryValueExA( hKey, szGroup, 0, &type, NULL, &sz );
1059 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1060 total += sz;
1062 sz = 0;
1063 r = RegQueryValueExA( hKey, szDependencies, 0, &type, NULL, &sz );
1064 if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1065 total += sz;
1067 sz = 0;
1068 r = RegQueryValueExA( hKey, szStart, 0, &type, NULL, &sz );
1069 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1070 total += sz;
1072 sz = 0;
1073 r = RegQueryValueExA( hKey, szDisplayName, 0, &type, NULL, &sz );
1074 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1075 total += sz;
1077 /* if there's not enough memory, return an error */
1078 if( total > *pcbBytesNeeded )
1080 *pcbBytesNeeded = total;
1081 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1082 return FALSE;
1085 *pcbBytesNeeded = total;
1086 ZeroMemory( lpServiceConfig, total );
1088 sz = sizeof val;
1089 r = RegQueryValueExA( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1090 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1091 lpServiceConfig->dwServiceType = val;
1093 sz = sizeof val;
1094 r = RegQueryValueExA( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1095 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1096 lpServiceConfig->dwStartType = val;
1098 sz = sizeof val;
1099 r = RegQueryValueExA( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1100 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1101 lpServiceConfig->dwErrorControl = val;
1103 /* now do the strings */
1104 p = (LPBYTE) &lpServiceConfig[1];
1105 n = total - sizeof (QUERY_SERVICE_CONFIGA);
1107 sz = sizeof(str_buffer);
1108 r = RegQueryValueExA( hKey, szImagePath, 0, &type, str_buffer, &sz );
1109 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1111 sz = ExpandEnvironmentStringsA(str_buffer, p, n);
1112 if( 0 == sz || sz > n ) return FALSE;
1114 lpServiceConfig->lpBinaryPathName = (LPSTR) p;
1115 p += sz;
1116 n -= sz;
1118 else
1120 /* FIXME: set last error */
1121 return FALSE;
1124 sz = n;
1125 r = RegQueryValueExA( hKey, szGroup, 0, &type, p, &sz );
1126 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1128 lpServiceConfig->lpLoadOrderGroup = (LPSTR) p;
1129 p += sz;
1130 n -= sz;
1133 sz = n;
1134 r = RegQueryValueExA( hKey, szDependencies, 0, &type, p, &sz );
1135 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1137 lpServiceConfig->lpDependencies = (LPSTR) p;
1138 p += sz;
1139 n -= sz;
1142 if( n < 0 )
1143 ERR("Buffer overflow!\n");
1145 TRACE("Image path = %s\n", lpServiceConfig->lpBinaryPathName );
1146 TRACE("Group = %s\n", lpServiceConfig->lpLoadOrderGroup );
1148 return TRUE;
1151 /******************************************************************************
1152 * QueryServiceConfigW [ADVAPI32.@]
1154 BOOL WINAPI
1155 QueryServiceConfigW( SC_HANDLE hService,
1156 LPQUERY_SERVICE_CONFIGW lpServiceConfig,
1157 DWORD cbBufSize, LPDWORD pcbBytesNeeded)
1159 static const WCHAR szDisplayName[] = {
1160 'D','i','s','p','l','a','y','N','a','m','e', 0 };
1161 static const WCHAR szType[] = {'T','y','p','e',0};
1162 static const WCHAR szStart[] = {'S','t','a','r','t',0};
1163 static const WCHAR szError[] = {
1164 'E','r','r','o','r','C','o','n','t','r','o','l', 0};
1165 static const WCHAR szImagePath[] = {'I','m','a','g','e','P','a','t','h',0};
1166 static const WCHAR szGroup[] = {'G','r','o','u','p',0};
1167 static const WCHAR szDependencies[] = {
1168 'D','e','p','e','n','d','e','n','c','i','e','s',0};
1169 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1170 WCHAR str_buffer[ MAX_PATH ];
1171 LONG r;
1172 DWORD type, val, sz, total, n;
1173 LPBYTE p;
1175 TRACE("%p %p %ld %p\n", hService, lpServiceConfig,
1176 cbBufSize, pcbBytesNeeded);
1178 /* calculate the size required first */
1179 total = sizeof (QUERY_SERVICE_CONFIGW);
1181 sz = sizeof(str_buffer);
1182 r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1183 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1185 sz = ExpandEnvironmentStringsW(str_buffer,NULL,0);
1186 if( 0 == sz ) return FALSE;
1188 total += sizeof(WCHAR) * sz;
1190 else
1192 /* FIXME: set last error */
1193 return FALSE;
1196 sz = 0;
1197 r = RegQueryValueExW( hKey, szGroup, 0, &type, NULL, &sz );
1198 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1199 total += sz;
1201 sz = 0;
1202 r = RegQueryValueExW( hKey, szDependencies, 0, &type, NULL, &sz );
1203 if( ( r == ERROR_SUCCESS ) && ( type == REG_MULTI_SZ ) )
1204 total += sz;
1206 sz = 0;
1207 r = RegQueryValueExW( hKey, szStart, 0, &type, NULL, &sz );
1208 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1209 total += sz;
1211 sz = 0;
1212 r = RegQueryValueExW( hKey, szDisplayName, 0, &type, NULL, &sz );
1213 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ ) )
1214 total += sz;
1216 /* if there's not enough memory, return an error */
1217 if( total > *pcbBytesNeeded )
1219 *pcbBytesNeeded = total;
1220 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1221 return FALSE;
1224 *pcbBytesNeeded = total;
1225 ZeroMemory( lpServiceConfig, total );
1227 sz = sizeof val;
1228 r = RegQueryValueExW( hKey, szType, 0, &type, (LPBYTE)&val, &sz );
1229 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1230 lpServiceConfig->dwServiceType = val;
1232 sz = sizeof val;
1233 r = RegQueryValueExW( hKey, szStart, 0, &type, (LPBYTE)&val, &sz );
1234 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1235 lpServiceConfig->dwStartType = val;
1237 sz = sizeof val;
1238 r = RegQueryValueExW( hKey, szError, 0, &type, (LPBYTE)&val, &sz );
1239 if( ( r == ERROR_SUCCESS ) || ( type == REG_DWORD ) )
1240 lpServiceConfig->dwErrorControl = val;
1242 /* now do the strings */
1243 p = (LPBYTE) &lpServiceConfig[1];
1244 n = total - sizeof (QUERY_SERVICE_CONFIGW);
1246 sz = sizeof(str_buffer);
1247 r = RegQueryValueExW( hKey, szImagePath, 0, &type, (LPBYTE) str_buffer, &sz );
1248 if( ( r == ERROR_SUCCESS ) && ( type == REG_SZ || type == REG_EXPAND_SZ ) )
1250 sz = ExpandEnvironmentStringsW(str_buffer, (LPWSTR) p, n);
1251 sz *= sizeof(WCHAR);
1252 if( 0 == sz || sz > n ) return FALSE;
1254 lpServiceConfig->lpBinaryPathName = (LPWSTR) p;
1255 p += sz;
1256 n -= sz;
1258 else
1260 /* FIXME: set last error */
1261 return FALSE;
1264 sz = n;
1265 r = RegQueryValueExW( hKey, szGroup, 0, &type, p, &sz );
1266 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1268 lpServiceConfig->lpLoadOrderGroup = (LPWSTR) p;
1269 p += sz;
1270 n -= sz;
1273 sz = n;
1274 r = RegQueryValueExW( hKey, szDependencies, 0, &type, p, &sz );
1275 if( ( r == ERROR_SUCCESS ) || ( type == REG_SZ ) )
1277 lpServiceConfig->lpDependencies = (LPWSTR) p;
1278 p += sz;
1279 n -= sz;
1282 if( n < 0 )
1283 ERR("Buffer overflow!\n");
1285 TRACE("Image path = %s\n", debugstr_w(lpServiceConfig->lpBinaryPathName) );
1286 TRACE("Group = %s\n", debugstr_w(lpServiceConfig->lpLoadOrderGroup) );
1288 return TRUE;
1291 /******************************************************************************
1292 * ChangeServiceConfigW [ADVAPI32.@]
1294 BOOL WINAPI ChangeServiceConfigW( SC_HANDLE hService, DWORD dwServiceType,
1295 DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName,
1296 LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies,
1297 LPCWSTR lpServiceStartName, LPCWSTR lpPassword, LPCWSTR lpDisplayName)
1299 FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1300 hService, dwServiceType, dwStartType, dwErrorControl,
1301 debugstr_w(lpBinaryPathName), debugstr_w(lpLoadOrderGroup),
1302 lpdwTagId, lpDependencies, debugstr_w(lpServiceStartName),
1303 debugstr_w(lpPassword), debugstr_w(lpDisplayName) );
1304 return TRUE;
1307 /******************************************************************************
1308 * ChangeServiceConfigA [ADVAPI32.@]
1310 BOOL WINAPI ChangeServiceConfigA( SC_HANDLE hService, DWORD dwServiceType,
1311 DWORD dwStartType, DWORD dwErrorControl, LPCSTR lpBinaryPathName,
1312 LPCSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCSTR lpDependencies,
1313 LPCSTR lpServiceStartName, LPCSTR lpPassword, LPCSTR lpDisplayName)
1315 FIXME("%p %ld %ld %ld %s %s %p %p %s %s %s\n",
1316 hService, dwServiceType, dwStartType, dwErrorControl,
1317 debugstr_a(lpBinaryPathName), debugstr_a(lpLoadOrderGroup),
1318 lpdwTagId, lpDependencies, debugstr_a(lpServiceStartName),
1319 debugstr_a(lpPassword), debugstr_a(lpDisplayName) );
1320 return TRUE;
1323 /******************************************************************************
1324 * ChangeServiceConfig2A [ADVAPI32.@]
1326 BOOL WINAPI ChangeServiceConfig2A( SC_HANDLE hService, DWORD dwInfoLevel,
1327 LPVOID lpInfo)
1329 FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1330 return TRUE;
1333 /******************************************************************************
1334 * ChangeServiceConfig2W [ADVAPI32.@]
1336 BOOL WINAPI ChangeServiceConfig2W( SC_HANDLE hService, DWORD dwInfoLevel,
1337 LPVOID lpInfo)
1339 HKEY hKey = ((struct sc_handle*) hService)->u.service.hkey;
1341 if (dwInfoLevel == SERVICE_CONFIG_DESCRIPTION)
1343 static const WCHAR szDescription[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
1344 LPSERVICE_DESCRIPTIONW sd = (LPSERVICE_DESCRIPTIONW)lpInfo;
1345 if (sd->lpDescription)
1347 TRACE("Setting Description to %s\n",debugstr_w(sd->lpDescription));
1348 if (sd->lpDescription[0] == 0)
1349 RegDeleteValueW(hKey,szDescription);
1350 else
1351 RegSetValueExW(hKey, szDescription, 0, REG_SZ,
1352 (LPVOID)sd->lpDescription,
1353 sizeof(WCHAR)*(strlenW(sd->lpDescription)+1));
1356 else
1357 FIXME("STUB: %p %ld %p\n",hService, dwInfoLevel, lpInfo);
1358 return TRUE;