77baac1c32366628d8c2f6b115889cb09bcd43ae
[reactos.git] / reactos / base / system / services / rpcserver.c
1 /*
2 * PROJECT: ReactOS Service Control Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/system/services/rpcserver.c
5 * PURPOSE: RPC server interface for the advapi32 calls
6 * COPYRIGHT: Copyright 2005-2006 Eric Kohl
7 * Copyright 2006-2007 Hervé Poussineau <hpoussin@reactos.org>
8 * Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
9 *
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include "services.h"
15 #include "svcctl_s.h"
16
17 #define NDEBUG
18 #include <debug.h>
19
20 /* GLOBALS *****************************************************************/
21
22 #define MANAGER_TAG 0x72674D68 /* 'hMgr' */
23 #define SERVICE_TAG 0x63765368 /* 'hSvc' */
24
25 typedef struct _SCMGR_HANDLE
26 {
27 DWORD Tag;
28 DWORD RefCount;
29 DWORD DesiredAccess;
30 } SCMGR_HANDLE;
31
32
33 typedef struct _MANAGER_HANDLE
34 {
35 SCMGR_HANDLE Handle;
36
37 /* FIXME: Insert more data here */
38
39 WCHAR DatabaseName[1];
40 } MANAGER_HANDLE, *PMANAGER_HANDLE;
41
42
43 typedef struct _SERVICE_HANDLE
44 {
45 SCMGR_HANDLE Handle;
46
47 DWORD DesiredAccess;
48 PSERVICE ServiceEntry;
49
50 /* FIXME: Insert more data here */
51
52 } SERVICE_HANDLE, *PSERVICE_HANDLE;
53
54
55 #define SC_MANAGER_READ \
56 (STANDARD_RIGHTS_READ | \
57 SC_MANAGER_QUERY_LOCK_STATUS | \
58 SC_MANAGER_ENUMERATE_SERVICE)
59
60 #define SC_MANAGER_WRITE \
61 (STANDARD_RIGHTS_WRITE | \
62 SC_MANAGER_MODIFY_BOOT_CONFIG | \
63 SC_MANAGER_CREATE_SERVICE)
64
65 #define SC_MANAGER_EXECUTE \
66 (STANDARD_RIGHTS_EXECUTE | \
67 SC_MANAGER_LOCK | \
68 SC_MANAGER_ENUMERATE_SERVICE | \
69 SC_MANAGER_CONNECT | \
70 SC_MANAGER_CREATE_SERVICE)
71
72
73 #define SERVICE_READ \
74 (STANDARD_RIGHTS_READ | \
75 SERVICE_INTERROGATE | \
76 SERVICE_ENUMERATE_DEPENDENTS | \
77 SERVICE_QUERY_STATUS | \
78 SERVICE_QUERY_CONFIG)
79
80 #define SERVICE_WRITE \
81 (STANDARD_RIGHTS_WRITE | \
82 SERVICE_CHANGE_CONFIG)
83
84 #define SERVICE_EXECUTE \
85 (STANDARD_RIGHTS_EXECUTE | \
86 SERVICE_USER_DEFINED_CONTROL | \
87 SERVICE_PAUSE_CONTINUE | \
88 SERVICE_STOP | \
89 SERVICE_START)
90
91
92 /* VARIABLES ***************************************************************/
93
94 static GENERIC_MAPPING
95 ScmManagerMapping = {SC_MANAGER_READ,
96 SC_MANAGER_WRITE,
97 SC_MANAGER_EXECUTE,
98 SC_MANAGER_ALL_ACCESS};
99
100 static GENERIC_MAPPING
101 ScmServiceMapping = {SERVICE_READ,
102 SERVICE_WRITE,
103 SERVICE_EXECUTE,
104 SC_MANAGER_ALL_ACCESS};
105
106
107 /* FUNCTIONS ***************************************************************/
108
109 VOID
110 ScmStartRpcServer(VOID)
111 {
112 RPC_STATUS Status;
113
114 DPRINT("ScmStartRpcServer() called\n");
115
116 Status = RpcServerUseProtseqEpW(L"ncacn_np",
117 10,
118 L"\\pipe\\ntsvcs",
119 NULL);
120 if (Status != RPC_S_OK)
121 {
122 DPRINT1("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status);
123 return;
124 }
125
126 Status = RpcServerRegisterIf(svcctl_v2_0_s_ifspec,
127 NULL,
128 NULL);
129 if (Status != RPC_S_OK)
130 {
131 DPRINT1("RpcServerRegisterIf() failed (Status %lx)\n", Status);
132 return;
133 }
134
135 Status = RpcServerListen(1, 20, TRUE);
136 if (Status != RPC_S_OK)
137 {
138 DPRINT1("RpcServerListen() failed (Status %lx)\n", Status);
139 return;
140 }
141
142 DPRINT("ScmStartRpcServer() done\n");
143 }
144
145
146 static DWORD
147 ScmCreateManagerHandle(LPWSTR lpDatabaseName,
148 SC_HANDLE *Handle)
149 {
150 PMANAGER_HANDLE Ptr;
151
152 if (lpDatabaseName == NULL)
153 lpDatabaseName = SERVICES_ACTIVE_DATABASEW;
154
155 Ptr = (MANAGER_HANDLE*) HeapAlloc(GetProcessHeap(),
156 HEAP_ZERO_MEMORY,
157 sizeof(MANAGER_HANDLE) + wcslen(lpDatabaseName) * sizeof(WCHAR));
158 if (Ptr == NULL)
159 return ERROR_NOT_ENOUGH_MEMORY;
160
161 Ptr->Handle.Tag = MANAGER_TAG;
162 Ptr->Handle.RefCount = 1;
163
164 /* FIXME: initialize more data here */
165
166 wcscpy(Ptr->DatabaseName, lpDatabaseName);
167
168 *Handle = (SC_HANDLE)Ptr;
169
170 return ERROR_SUCCESS;
171 }
172
173
174 static DWORD
175 ScmCreateServiceHandle(PSERVICE lpServiceEntry,
176 SC_HANDLE *Handle)
177 {
178 PSERVICE_HANDLE Ptr;
179
180 Ptr = (SERVICE_HANDLE*) HeapAlloc(GetProcessHeap(),
181 HEAP_ZERO_MEMORY,
182 sizeof(SERVICE_HANDLE));
183 if (Ptr == NULL)
184 return ERROR_NOT_ENOUGH_MEMORY;
185
186 Ptr->Handle.Tag = SERVICE_TAG;
187 Ptr->Handle.RefCount = 1;
188
189 /* FIXME: initialize more data here */
190 Ptr->ServiceEntry = lpServiceEntry;
191
192 *Handle = (SC_HANDLE)Ptr;
193
194 return ERROR_SUCCESS;
195 }
196
197
198 static DWORD
199 ScmCheckAccess(SC_HANDLE Handle,
200 DWORD dwDesiredAccess)
201 {
202 PMANAGER_HANDLE hMgr;
203
204 hMgr = (PMANAGER_HANDLE)Handle;
205 if (hMgr->Handle.Tag == MANAGER_TAG)
206 {
207 RtlMapGenericMask(&dwDesiredAccess,
208 &ScmManagerMapping);
209
210 hMgr->Handle.DesiredAccess = dwDesiredAccess;
211
212 return ERROR_SUCCESS;
213 }
214 else if (hMgr->Handle.Tag == SERVICE_TAG)
215 {
216 RtlMapGenericMask(&dwDesiredAccess,
217 &ScmServiceMapping);
218
219 hMgr->Handle.DesiredAccess = dwDesiredAccess;
220
221 return ERROR_SUCCESS;
222 }
223
224 return ERROR_INVALID_HANDLE;
225 }
226
227
228 DWORD
229 ScmAssignNewTag(PSERVICE lpService)
230 {
231 /* FIXME */
232 DPRINT("Assigning new tag to service %S\n", lpService->lpServiceName);
233 lpService->dwTag = 0;
234 return ERROR_SUCCESS;
235 }
236
237
238 /* Function 0 */
239 DWORD RCloseServiceHandle(
240 handle_t BindingHandle,
241 LPSC_RPC_HANDLE hSCObject)
242 {
243 PMANAGER_HANDLE hManager;
244
245 DPRINT("RCloseServiceHandle() called\n");
246
247 DPRINT("hSCObject = %p\n", *hSCObject);
248
249 if (*hSCObject == 0)
250 return ERROR_INVALID_HANDLE;
251
252 hManager = (PMANAGER_HANDLE)*hSCObject;
253 if (hManager->Handle.Tag == MANAGER_TAG)
254 {
255 DPRINT("Found manager handle\n");
256
257 hManager->Handle.RefCount--;
258 if (hManager->Handle.RefCount == 0)
259 {
260 /* FIXME: add cleanup code */
261
262 HeapFree(GetProcessHeap(), 0, hManager);
263 }
264
265 DPRINT("RCloseServiceHandle() done\n");
266 return ERROR_SUCCESS;
267 }
268 else if (hManager->Handle.Tag == SERVICE_TAG)
269 {
270 DPRINT("Found service handle\n");
271
272 hManager->Handle.RefCount--;
273 if (hManager->Handle.RefCount == 0)
274 {
275 /* FIXME: add cleanup code */
276
277 HeapFree(GetProcessHeap(), 0, hManager);
278 }
279
280 DPRINT("RCloseServiceHandle() done\n");
281 return ERROR_SUCCESS;
282 }
283
284 DPRINT1("Invalid handle tag (Tag %lx)\n", hManager->Handle.Tag);
285
286 return ERROR_INVALID_HANDLE;
287 }
288
289
290 /* Function 1 */
291 DWORD RControlService(
292 handle_t BindingHandle,
293 SC_RPC_HANDLE hService,
294 DWORD dwControl,
295 LPSERVICE_STATUS lpServiceStatus)
296 {
297 PSERVICE_HANDLE hSvc;
298 PSERVICE lpService;
299 ACCESS_MASK DesiredAccess;
300 DWORD dwError = ERROR_SUCCESS;
301
302 DPRINT("RControlService() called\n");
303
304 if (ScmShutdown)
305 return ERROR_SHUTDOWN_IN_PROGRESS;
306
307 /* Check the service handle */
308 hSvc = (PSERVICE_HANDLE)hService;
309 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
310 {
311 DPRINT1("Invalid handle tag!\n");
312 return ERROR_INVALID_HANDLE;
313 }
314
315 /* Check access rights */
316 switch (dwControl)
317 {
318 case SERVICE_CONTROL_STOP:
319 DesiredAccess = SERVICE_STOP;
320 break;
321
322 case SERVICE_CONTROL_PAUSE:
323 case SERVICE_CONTROL_CONTINUE:
324 DesiredAccess = SERVICE_PAUSE_CONTINUE;
325 break;
326
327 case SERVICE_INTERROGATE:
328 DesiredAccess = SERVICE_INTERROGATE;
329 break;
330
331 default:
332 if (dwControl >= 128 && dwControl <= 255)
333 DesiredAccess = SERVICE_USER_DEFINED_CONTROL;
334 else
335 DesiredAccess = SERVICE_QUERY_CONFIG |
336 SERVICE_CHANGE_CONFIG |
337 SERVICE_QUERY_STATUS |
338 SERVICE_START |
339 SERVICE_PAUSE_CONTINUE;
340 break;
341 }
342
343 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
344 DesiredAccess))
345 return ERROR_ACCESS_DENIED;
346
347 /* Check the service entry point */
348 lpService = hSvc->ServiceEntry;
349 if (lpService == NULL)
350 {
351 DPRINT1("lpService == NULL!\n");
352 return ERROR_INVALID_HANDLE;
353 }
354
355 if (lpService->Status.dwServiceType & SERVICE_DRIVER)
356 {
357 /* Send control code to the driver */
358 dwError = ScmControlDriver(lpService,
359 dwControl,
360 lpServiceStatus);
361 }
362 else
363 {
364 /* Send control code to the service */
365 dwError = ScmControlService(lpService,
366 dwControl,
367 lpServiceStatus);
368 }
369
370 /* Return service status information */
371 RtlCopyMemory(lpServiceStatus,
372 &lpService->Status,
373 sizeof(SERVICE_STATUS));
374
375 return dwError;
376 }
377
378
379 /* Function 2 */
380 DWORD RDeleteService(
381 handle_t BindingHandle,
382 SC_RPC_HANDLE hService)
383 {
384 PSERVICE_HANDLE hSvc;
385 PSERVICE lpService;
386 DWORD dwError;
387
388 DPRINT("RDeleteService() called\n");
389
390 if (ScmShutdown)
391 return ERROR_SHUTDOWN_IN_PROGRESS;
392
393 hSvc = (PSERVICE_HANDLE)hService;
394 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
395 return ERROR_INVALID_HANDLE;
396
397 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
398 DELETE))
399 return ERROR_ACCESS_DENIED;
400
401 lpService = hSvc->ServiceEntry;
402 if (lpService == NULL)
403 {
404 DPRINT1("lpService == NULL!\n");
405 return ERROR_INVALID_HANDLE;
406 }
407
408 /* FIXME: Acquire service database lock exclusively */
409
410 if (lpService->bDeleted)
411 {
412 DPRINT1("The service has already been marked for delete!\n");
413 return ERROR_SERVICE_MARKED_FOR_DELETE;
414 }
415
416 /* Mark service for delete */
417 lpService->bDeleted = TRUE;
418
419 dwError = ScmMarkServiceForDelete(lpService);
420
421 /* FIXME: Release service database lock */
422
423 DPRINT("RDeleteService() done\n");
424
425 return dwError;
426 }
427
428
429 /* Function 3 */
430 DWORD RLockServiceDatabase(
431 handle_t BindingHandle,
432 SC_RPC_HANDLE hSCManager,
433 LPSC_RPC_LOCK lpLock)
434 {
435 PMANAGER_HANDLE hMgr;
436
437 DPRINT("RLockServiceDatabase() called\n");
438
439 *lpLock = 0;
440
441 hMgr = (PMANAGER_HANDLE)hSCManager;
442 if (!hMgr || hMgr->Handle.Tag != MANAGER_TAG)
443 return ERROR_INVALID_HANDLE;
444
445 if (!RtlAreAllAccessesGranted(hMgr->Handle.DesiredAccess,
446 SC_MANAGER_LOCK))
447 return ERROR_ACCESS_DENIED;
448
449 // return ScmLockDatabase(0, hMgr->0xC, hLock);
450
451 /* FIXME: Lock the database */
452 *lpLock = (void *)0x12345678; /* Dummy! */
453
454 return ERROR_SUCCESS;
455 }
456
457
458 /* Function 4 */
459 DWORD RQueryServiceObjectSecurity(
460 handle_t BindingHandle,
461 SC_RPC_HANDLE hService,
462 SECURITY_INFORMATION dwSecurityInformation,
463 LPBYTE lpSecurityDescriptor,
464 DWORD cbBufSize,
465 LPBOUNDED_DWORD_256K pcbBytesNeeded)
466 {
467 #if 0
468 PSERVICE_HANDLE hSvc;
469 PSERVICE lpService;
470 ULONG DesiredAccess = 0;
471 NTSTATUS Status;
472 DWORD dwBytesNeeded;
473 DWORD dwError;
474
475 DPRINT("RQueryServiceObjectSecurity() called\n");
476
477 hSvc = (PSERVICE_HANDLE)hService;
478 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
479 {
480 DPRINT1("Invalid handle tag!\n");
481 return ERROR_INVALID_HANDLE;
482 }
483
484 if (dwSecurityInformation & (DACL_SECURITY_INFORMATION ||
485 GROUP_SECURITY_INFORMATION ||
486 OWNER_SECURITY_INFORMATION))
487 DesiredAccess |= READ_CONTROL;
488
489 if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
490 DesiredAccess |= ACCESS_SYSTEM_SECURITY;
491
492 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
493 DesiredAccess))
494 {
495 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
496 return ERROR_ACCESS_DENIED;
497 }
498
499 lpService = hSvc->ServiceEntry;
500 if (lpService == NULL)
501 {
502 DPRINT1("lpService == NULL!\n");
503 return ERROR_INVALID_HANDLE;
504 }
505
506 /* FIXME: Lock the service list */
507
508 Status = RtlQuerySecurityObject(lpService->lpSecurityDescriptor,
509 dwSecurityInformation,
510 (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
511 dwSecuityDescriptorSize,
512 &dwBytesNeeded);
513
514 /* FIXME: Unlock the service list */
515
516 if (NT_SUCCESS(Status))
517 {
518 *pcbBytesNeeded = dwBytesNeeded;
519 dwError = STATUS_SUCCESS;
520 }
521 else if (Status == STATUS_BUFFER_TOO_SMALL)
522 {
523 *pcbBytesNeeded = dwBytesNeeded;
524 dwError = ERROR_INSUFFICIENT_BUFFER;
525 }
526 else if (Status == STATUS_BAD_DESCRIPTOR_FORMAT)
527 {
528 dwError = ERROR_GEN_FAILURE;
529 }
530 else
531 {
532 dwError = RtlNtStatusToDosError(Status);
533 }
534
535 return dwError;
536 #endif
537 UNIMPLEMENTED;
538 return ERROR_CALL_NOT_IMPLEMENTED;
539 }
540
541
542 /* Function 5 */
543 DWORD RSetServiceObjectSecurity(
544 handle_t BindingHandle,
545 SC_RPC_HANDLE hService,
546 DWORD dwSecurityInformation,
547 LPBYTE lpSecurityDescriptor,
548 DWORD dwSecuityDescriptorSize)
549 {
550 PSERVICE_HANDLE hSvc;
551 PSERVICE lpService;
552 ULONG DesiredAccess = 0;
553 HANDLE hToken = NULL;
554 HKEY hServiceKey;
555 NTSTATUS Status;
556 DWORD dwError;
557
558 DPRINT1("RSetServiceObjectSecurity() called\n");
559
560 hSvc = (PSERVICE_HANDLE)hService;
561 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
562 {
563 DPRINT1("Invalid handle tag!\n");
564 return ERROR_INVALID_HANDLE;
565 }
566
567 if (dwSecurityInformation == 0 ||
568 dwSecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
569 | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
570 return ERROR_INVALID_PARAMETER;
571
572 if (!RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)lpSecurityDescriptor))
573 return ERROR_INVALID_PARAMETER;
574
575 if (dwSecurityInformation & SACL_SECURITY_INFORMATION)
576 DesiredAccess |= ACCESS_SYSTEM_SECURITY;
577
578 if (dwSecurityInformation & DACL_SECURITY_INFORMATION)
579 DesiredAccess |= WRITE_DAC;
580
581 if (dwSecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
582 DesiredAccess |= WRITE_OWNER;
583
584 if ((dwSecurityInformation & OWNER_SECURITY_INFORMATION) &&
585 (((PSECURITY_DESCRIPTOR)lpSecurityDescriptor)->Owner == NULL))
586 return ERROR_INVALID_PARAMETER;
587
588 if ((dwSecurityInformation & GROUP_SECURITY_INFORMATION) &&
589 (((PSECURITY_DESCRIPTOR)lpSecurityDescriptor)->Group == NULL))
590 return ERROR_INVALID_PARAMETER;
591
592 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
593 DesiredAccess))
594 {
595 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
596 return ERROR_ACCESS_DENIED;
597 }
598
599 lpService = hSvc->ServiceEntry;
600 if (lpService == NULL)
601 {
602 DPRINT1("lpService == NULL!\n");
603 return ERROR_INVALID_HANDLE;
604 }
605
606 if (lpService->bDeleted)
607 return ERROR_SERVICE_MARKED_FOR_DELETE;
608
609 RpcImpersonateClient(NULL);
610
611 Status = NtOpenThreadToken(NtCurrentThread(),
612 8,
613 TRUE,
614 &hToken);
615 if (!NT_SUCCESS(Status))
616 return RtlNtStatusToDosError(Status);
617
618 RpcRevertToSelf();
619
620 /* FIXME: Lock service database */
621
622 #if 0
623 Status = RtlSetSecurityObject(dwSecurityInformation,
624 (PSECURITY_DESCRIPTOR)lpSecurityDescriptor,
625 &lpService->lpSecurityDescriptor,
626 &ScmServiceMapping,
627 hToken);
628 if (!NT_SUCCESS(Status))
629 {
630 dwError = RtlNtStatusToDosError(Status);
631 goto Done;
632 }
633 #endif
634
635 dwError = ScmOpenServiceKey(lpService->lpServiceName,
636 READ_CONTROL | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
637 &hServiceKey);
638 if (dwError != ERROR_SUCCESS)
639 goto Done;
640
641 UNIMPLEMENTED;
642 dwError = ERROR_SUCCESS;
643 // dwError = ScmWriteSecurityDescriptor(hServiceKey,
644 // lpService->lpSecurityDescriptor);
645
646 RegFlushKey(hServiceKey);
647 RegCloseKey(hServiceKey);
648
649 Done:
650
651 if (hToken != NULL)
652 NtClose(hToken);
653
654 /* FIXME: Unlock service database */
655
656 DPRINT("RSetServiceObjectSecurity() done (Error %lu)\n", dwError);
657
658 return dwError;
659 }
660
661
662 /* Function 6 */
663 DWORD RQueryServiceStatus(
664 handle_t BindingHandle,
665 SC_RPC_HANDLE hService,
666 LPSERVICE_STATUS lpServiceStatus)
667 {
668 PSERVICE_HANDLE hSvc;
669 PSERVICE lpService;
670
671 DPRINT("RQueryServiceStatus() called\n");
672
673 if (ScmShutdown)
674 return ERROR_SHUTDOWN_IN_PROGRESS;
675
676 hSvc = (PSERVICE_HANDLE)hService;
677 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
678 {
679 DPRINT1("Invalid handle tag!\n");
680 return ERROR_INVALID_HANDLE;
681 }
682
683 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
684 SERVICE_QUERY_STATUS))
685 {
686 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
687 return ERROR_ACCESS_DENIED;
688 }
689
690 lpService = hSvc->ServiceEntry;
691 if (lpService == NULL)
692 {
693 DPRINT1("lpService == NULL!\n");
694 return ERROR_INVALID_HANDLE;
695 }
696
697 /* Return service status information */
698 RtlCopyMemory(lpServiceStatus,
699 &lpService->Status,
700 sizeof(SERVICE_STATUS));
701
702 return ERROR_SUCCESS;
703 }
704
705
706 /* Function 7 */
707 DWORD RSetServiceStatus(
708 handle_t BindingHandle,
709 SC_RPC_HANDLE hServiceStatus,
710 LPSERVICE_STATUS lpServiceStatus)
711 {
712 PSERVICE lpService;
713
714 DPRINT("RSetServiceStatus() called\n");
715
716 if (ScmShutdown)
717 return ERROR_SHUTDOWN_IN_PROGRESS;
718
719 lpService = ScmGetServiceEntryByClientHandle((ULONG)hServiceStatus);
720 if (lpService == NULL)
721 {
722 DPRINT1("lpService == NULL!\n");
723 return ERROR_INVALID_HANDLE;
724 }
725
726 RtlCopyMemory(&lpService->Status,
727 lpServiceStatus,
728 sizeof(SERVICE_STATUS));
729
730 DPRINT("Set %S to %lu\n", lpService->lpDisplayName, lpService->Status.dwCurrentState);
731 DPRINT("RSetServiceStatus() done\n");
732
733 return ERROR_SUCCESS;
734 }
735
736
737 /* Function 8 */
738 DWORD RUnlockServiceDatabase(
739 handle_t BindingHandle,
740 LPSC_RPC_LOCK Lock)
741 {
742 UNIMPLEMENTED;
743 return ERROR_SUCCESS;
744 }
745
746
747 /* Function 9 */
748 DWORD RNotifyBootConfigStatus(
749 handle_t BindingHandle,
750 SVCCTL_HANDLEW lpMachineName,
751 DWORD BootAcceptable)
752 {
753 UNIMPLEMENTED;
754 return ERROR_CALL_NOT_IMPLEMENTED;
755 }
756
757
758 /* Function 10 */
759 DWORD RSetServiceBitsW(
760 handle_t BindingHandle,
761 SC_RPC_HANDLE hServiceStatus,
762 DWORD dwServiceBits,
763 int bSetBitsOn,
764 int bUpdateImmediately,
765 wchar_t *lpString)
766 {
767 UNIMPLEMENTED;
768 return ERROR_CALL_NOT_IMPLEMENTED;
769 }
770
771
772 /* Function 11 */
773 DWORD RChangeServiceConfigW(
774 handle_t BindingHandle,
775 SC_RPC_HANDLE hService,
776 DWORD dwServiceType,
777 DWORD dwStartType,
778 DWORD dwErrorControl,
779 LPWSTR lpBinaryPathName,
780 LPWSTR lpLoadOrderGroup,
781 LPDWORD lpdwTagId,
782 LPBYTE lpDependencies,
783 DWORD dwDependSize,
784 LPWSTR lpServiceStartName,
785 LPBYTE lpPassword,
786 DWORD dwPwSize,
787 LPWSTR lpDisplayName)
788 {
789 DWORD dwError = ERROR_SUCCESS;
790 PSERVICE_HANDLE hSvc;
791 PSERVICE lpService = NULL;
792 HKEY hServiceKey = NULL;
793
794 DPRINT("RChangeServiceConfigW() called\n");
795 DPRINT("dwServiceType = %lu\n", dwServiceType);
796 DPRINT("dwStartType = %lu\n", dwStartType);
797 DPRINT("dwErrorControl = %lu\n", dwErrorControl);
798 DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
799 DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
800 DPRINT("lpDisplayName = %S\n", lpDisplayName);
801
802 if (ScmShutdown)
803 return ERROR_SHUTDOWN_IN_PROGRESS;
804
805 hSvc = (PSERVICE_HANDLE)hService;
806 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
807 {
808 DPRINT1("Invalid handle tag!\n");
809 return ERROR_INVALID_HANDLE;
810 }
811
812 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
813 SERVICE_CHANGE_CONFIG))
814 {
815 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
816 return ERROR_ACCESS_DENIED;
817 }
818
819 lpService = hSvc->ServiceEntry;
820 if (lpService == NULL)
821 {
822 DPRINT1("lpService == NULL!\n");
823 return ERROR_INVALID_HANDLE;
824 }
825
826 /* FIXME: Lock database exclusively */
827
828 if (lpService->bDeleted)
829 {
830 /* FIXME: Unlock database */
831 DPRINT1("The service has already been marked for delete!\n");
832 return ERROR_SERVICE_MARKED_FOR_DELETE;
833 }
834
835 /* Open the service key */
836 dwError = ScmOpenServiceKey(lpService->szServiceName,
837 KEY_SET_VALUE,
838 &hServiceKey);
839 if (dwError != ERROR_SUCCESS)
840 goto done;
841
842 /* Write service data to the registry */
843 /* Set the display name */
844 if (lpDisplayName != NULL && *lpDisplayName != 0)
845 {
846 RegSetValueExW(hServiceKey,
847 L"DisplayName",
848 0,
849 REG_SZ,
850 (LPBYTE)lpDisplayName,
851 (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
852 /* FIXME: update lpService->lpDisplayName */
853 }
854
855 if (dwServiceType != SERVICE_NO_CHANGE)
856 {
857 /* Set the service type */
858 dwError = RegSetValueExW(hServiceKey,
859 L"Type",
860 0,
861 REG_DWORD,
862 (LPBYTE)&dwServiceType,
863 sizeof(DWORD));
864 if (dwError != ERROR_SUCCESS)
865 goto done;
866
867 lpService->Status.dwServiceType = dwServiceType;
868 }
869
870 if (dwStartType != SERVICE_NO_CHANGE)
871 {
872 /* Set the start value */
873 dwError = RegSetValueExW(hServiceKey,
874 L"Start",
875 0,
876 REG_DWORD,
877 (LPBYTE)&dwStartType,
878 sizeof(DWORD));
879 if (dwError != ERROR_SUCCESS)
880 goto done;
881
882 lpService->dwStartType = dwStartType;
883 }
884
885 if (dwErrorControl != SERVICE_NO_CHANGE)
886 {
887 /* Set the error control value */
888 dwError = RegSetValueExW(hServiceKey,
889 L"ErrorControl",
890 0,
891 REG_DWORD,
892 (LPBYTE)&dwErrorControl,
893 sizeof(DWORD));
894 if (dwError != ERROR_SUCCESS)
895 goto done;
896
897 lpService->dwErrorControl = dwErrorControl;
898 }
899
900 #if 0
901 /* FIXME: set the new ImagePath value */
902
903 /* Set the image path */
904 if (dwServiceType & SERVICE_WIN32)
905 {
906 if (lpBinaryPathName != NULL && *lpBinaryPathName != 0)
907 {
908 dwError = RegSetValueExW(hServiceKey,
909 L"ImagePath",
910 0,
911 REG_EXPAND_SZ,
912 (LPBYTE)lpBinaryPathName,
913 (wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR));
914 if (dwError != ERROR_SUCCESS)
915 goto done;
916 }
917 }
918 else if (dwServiceType & SERVICE_DRIVER)
919 {
920 if (lpImagePath != NULL && *lpImagePath != 0)
921 {
922 dwError = RegSetValueExW(hServiceKey,
923 L"ImagePath",
924 0,
925 REG_EXPAND_SZ,
926 (LPBYTE)lpImagePath,
927 (wcslen(lpImagePath) + 1) *sizeof(WCHAR));
928 if (dwError != ERROR_SUCCESS)
929 goto done;
930 }
931 }
932 #endif
933
934 /* Set the group name */
935 if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
936 {
937 dwError = RegSetValueExW(hServiceKey,
938 L"Group",
939 0,
940 REG_SZ,
941 (LPBYTE)lpLoadOrderGroup,
942 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
943 if (dwError != ERROR_SUCCESS)
944 goto done;
945 /* FIXME: update lpService->lpServiceGroup */
946 }
947
948 if (lpdwTagId != NULL)
949 {
950 dwError = ScmAssignNewTag(lpService);
951 if (dwError != ERROR_SUCCESS)
952 goto done;
953
954 dwError = RegSetValueExW(hServiceKey,
955 L"Tag",
956 0,
957 REG_DWORD,
958 (LPBYTE)&lpService->dwTag,
959 sizeof(DWORD));
960 if (dwError != ERROR_SUCCESS)
961 goto done;
962
963 *lpdwTagId = lpService->dwTag;
964 }
965
966 /* Write dependencies */
967 if (lpDependencies != NULL && *lpDependencies != 0)
968 {
969 dwError = ScmWriteDependencies(hServiceKey,
970 (LPWSTR)lpDependencies,
971 dwDependSize);
972 if (dwError != ERROR_SUCCESS)
973 goto done;
974 }
975
976 if (lpPassword != NULL)
977 {
978 /* FIXME: Write password */
979 }
980
981 /* FIXME: Unlock database */
982
983 done:
984 if (hServiceKey != NULL)
985 RegCloseKey(hServiceKey);
986
987 DPRINT("RChangeServiceConfigW() done (Error %lu)\n", dwError);
988
989 return dwError;
990 }
991
992 /* Create a path suitable for the bootloader out of the full path */
993 DWORD
994 ScmConvertToBootPathName(wchar_t *CanonName, wchar_t **RelativeName)
995 {
996 DWORD ServiceNameLen, BufferSize, ExpandedLen;
997 WCHAR Dest;
998 WCHAR *Expanded;
999 UNICODE_STRING NtPathName, SystemRoot, LinkTarget;
1000 OBJECT_ATTRIBUTES ObjectAttributes;
1001 NTSTATUS Status;
1002 HANDLE SymbolicLinkHandle;
1003
1004 DPRINT("ScmConvertToBootPathName %S\n", CanonName);
1005
1006 ServiceNameLen = wcslen(CanonName);
1007 /* First check, if it's already good */
1008 if (ServiceNameLen > 12 &&
1009 !wcsnicmp(L"\\SystemRoot\\", CanonName, 12))
1010 {
1011 *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
1012
1013 if (*RelativeName == NULL)
1014 {
1015 DPRINT1("Error allocating memory for boot driver name!\n");
1016 return ERROR_NOT_ENOUGH_MEMORY;
1017 }
1018
1019 /* Copy it */
1020 wcscpy(*RelativeName, CanonName);
1021
1022 DPRINT1("Bootdriver name %S\n", *RelativeName);
1023 return ERROR_SUCCESS;
1024 }
1025
1026 /* If it has %SystemRoot% prefix, substitute it to \System*/
1027 if (ServiceNameLen > 13 &&
1028 !wcsnicmp(L"%SystemRoot%\\", CanonName, 13))
1029 {
1030 /* There is no +sizeof(wchar_t) because the name is less by 1 wchar */
1031 *RelativeName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR));
1032
1033 if (*RelativeName == NULL)
1034 {
1035 DPRINT1("Error allocating memory for boot driver name!\n");
1036 return ERROR_NOT_ENOUGH_MEMORY;
1037 }
1038
1039 /* Copy it */
1040 wcscpy(*RelativeName, L"\\SystemRoot\\");
1041 wcscat(*RelativeName, CanonName + 13);
1042
1043 DPRINT1("Bootdriver name %S\n", *RelativeName);
1044 return ERROR_SUCCESS;
1045 }
1046
1047 /* Get buffer size needed for expanding env strings */
1048 BufferSize = ExpandEnvironmentStringsW(L"%SystemRoot%\\", &Dest, 1);
1049
1050 if (BufferSize <= 1)
1051 {
1052 DPRINT1("Error during a call to ExpandEnvironmentStringsW()\n");
1053 return ERROR_INVALID_ENVIRONMENT;
1054 }
1055
1056 /* Allocate memory, since the size is known now */
1057 Expanded = LocalAlloc(LMEM_ZEROINIT, BufferSize * sizeof(WCHAR) + sizeof(WCHAR));
1058 if (!Expanded)
1059 {
1060 DPRINT1("Error allocating memory for boot driver name!\n");
1061 return ERROR_NOT_ENOUGH_MEMORY;
1062 }
1063
1064 /* Expand it */
1065 if (ExpandEnvironmentStringsW(L"%SystemRoot%\\", Expanded, BufferSize) >
1066 BufferSize)
1067 {
1068 DPRINT1("Error during a call to ExpandEnvironmentStringsW()\n");
1069 LocalFree(Expanded);
1070 return ERROR_NOT_ENOUGH_MEMORY;
1071 }
1072
1073 /* Convert to NY-style path */
1074 if (!RtlDosPathNameToNtPathName_U(Expanded, &NtPathName, NULL, NULL))
1075 {
1076 DPRINT1("Error during a call to RtlDosPathNameToNtPathName_U()\n");
1077 return ERROR_INVALID_ENVIRONMENT;
1078 }
1079
1080 DPRINT("Converted to NT-style %wZ\n", &NtPathName);
1081
1082 /* No need to keep the dos-path anymore */
1083 LocalFree(Expanded);
1084
1085 /* Copy it to the allocated place */
1086 Expanded = LocalAlloc(LMEM_ZEROINIT, NtPathName.Length + sizeof(WCHAR));
1087 if (!Expanded)
1088 {
1089 DPRINT1("Error allocating memory for boot driver name!\n");
1090 return ERROR_NOT_ENOUGH_MEMORY;
1091 }
1092
1093 ExpandedLen = NtPathName.Length / sizeof(WCHAR);
1094 wcsncpy(Expanded, NtPathName.Buffer, ExpandedLen);
1095 Expanded[ExpandedLen] = 0;
1096
1097 if (ServiceNameLen > ExpandedLen &&
1098 !wcsnicmp(Expanded, CanonName, ExpandedLen))
1099 {
1100 /* Only \SystemRoot\ is missing */
1101 *RelativeName = LocalAlloc(LMEM_ZEROINIT,
1102 (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
1103
1104 if (*RelativeName == NULL)
1105 {
1106 DPRINT1("Error allocating memory for boot driver name!\n");
1107 LocalFree(Expanded);
1108 return ERROR_NOT_ENOUGH_MEMORY;
1109 }
1110
1111 wcscpy(*RelativeName, L"\\SystemRoot\\");
1112 wcscat(*RelativeName, CanonName + ExpandedLen);
1113
1114 RtlFreeUnicodeString(&NtPathName);
1115 return ERROR_SUCCESS;
1116 }
1117
1118 /* The most complex case starts here */
1119 RtlInitUnicodeString(&SystemRoot, L"\\SystemRoot");
1120 InitializeObjectAttributes(&ObjectAttributes,
1121 &SystemRoot,
1122 OBJ_CASE_INSENSITIVE,
1123 NULL,
1124 NULL);
1125
1126 /* Open this symlink */
1127 Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
1128
1129 if (NT_SUCCESS(Status))
1130 {
1131 LinkTarget.Length = 0;
1132 LinkTarget.MaximumLength = 0;
1133
1134 DPRINT("Opened symbolic link object\n");
1135
1136 Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
1137 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
1138 {
1139 /* Check if required buffer size is sane */
1140 if (BufferSize > 0xFFFD)
1141 {
1142 DPRINT1("Too large buffer required\n");
1143 *RelativeName = 0;
1144
1145 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1146 LocalFree(Expanded);
1147 return ERROR_NOT_ENOUGH_MEMORY;
1148 }
1149
1150 /* Alloc the string */
1151 LinkTarget.Buffer = LocalAlloc(LMEM_ZEROINIT, BufferSize + sizeof(WCHAR));
1152 if (!LinkTarget.Buffer)
1153 {
1154 DPRINT1("Unable to alloc buffer\n");
1155 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1156 LocalFree(Expanded);
1157 return ERROR_NOT_ENOUGH_MEMORY;
1158 }
1159
1160 /* Do a real query now */
1161 LinkTarget.Length = BufferSize;
1162 LinkTarget.MaximumLength = LinkTarget.Length + sizeof(WCHAR);
1163
1164 Status = NtQuerySymbolicLinkObject(SymbolicLinkHandle, &LinkTarget, &BufferSize);
1165 if (NT_SUCCESS(Status))
1166 {
1167 DPRINT("LinkTarget: %wZ\n", &LinkTarget);
1168
1169 ExpandedLen = LinkTarget.Length / sizeof(WCHAR);
1170 if ((ServiceNameLen > ExpandedLen) &&
1171 !wcsnicmp(LinkTarget.Buffer, CanonName, ExpandedLen))
1172 {
1173 *RelativeName = LocalAlloc(LMEM_ZEROINIT,
1174 (ServiceNameLen - ExpandedLen) * sizeof(WCHAR) + 13*sizeof(WCHAR));
1175
1176 if (*RelativeName == NULL)
1177 {
1178 DPRINT1("Unable to alloc buffer\n");
1179 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1180 LocalFree(Expanded);
1181 RtlFreeUnicodeString(&NtPathName);
1182 return ERROR_NOT_ENOUGH_MEMORY;
1183 }
1184
1185 /* Copy it over, substituting the first part
1186 with SystemRoot */
1187 wcscpy(*RelativeName, L"\\SystemRoot\\");
1188 wcscat(*RelativeName, CanonName+ExpandedLen+1);
1189
1190 /* Cleanup */
1191 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1192 LocalFree(Expanded);
1193 RtlFreeUnicodeString(&NtPathName);
1194
1195 /* Return success */
1196 return ERROR_SUCCESS;
1197 }
1198 else
1199 {
1200 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1201 LocalFree(Expanded);
1202 RtlFreeUnicodeString(&NtPathName);
1203 return ERROR_INVALID_PARAMETER;
1204 }
1205 }
1206 else
1207 {
1208 DPRINT1("Error, Status = %08X\n", Status);
1209 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1210 LocalFree(Expanded);
1211 RtlFreeUnicodeString(&NtPathName);
1212 return ERROR_INVALID_PARAMETER;
1213 }
1214 }
1215 else
1216 {
1217 DPRINT1("Error, Status = %08X\n", Status);
1218 if (SymbolicLinkHandle) NtClose(SymbolicLinkHandle);
1219 LocalFree(Expanded);
1220 RtlFreeUnicodeString(&NtPathName);
1221 return ERROR_INVALID_PARAMETER;
1222 }
1223 }
1224 else
1225 {
1226 DPRINT1("Error, Status = %08X\n", Status);
1227 LocalFree(Expanded);
1228 return ERROR_INVALID_PARAMETER;
1229 }
1230
1231 /* Failure */
1232 *RelativeName = NULL;
1233 return ERROR_INVALID_PARAMETER;
1234 }
1235
1236 DWORD
1237 ScmCanonDriverImagePath(DWORD dwStartType,
1238 wchar_t *lpServiceName,
1239 wchar_t **lpCanonName)
1240 {
1241 DWORD ServiceNameLen, Result;
1242 UNICODE_STRING NtServiceName;
1243 WCHAR *RelativeName;
1244 WCHAR *SourceName = lpServiceName;
1245
1246 /* Calculate the length of the service's name */
1247 ServiceNameLen = wcslen(lpServiceName);
1248
1249 /* 12 is wcslen(L"\\SystemRoot\\") */
1250 if (ServiceNameLen > 12 &&
1251 !wcsnicmp(L"\\SystemRoot\\", lpServiceName, 12))
1252 {
1253 /* SystemRoot prefix is already included */
1254
1255 *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
1256
1257 if (*lpCanonName == NULL)
1258 {
1259 DPRINT1("Error allocating memory for canonized service name!\n");
1260 return ERROR_NOT_ENOUGH_MEMORY;
1261 }
1262
1263 /* If it's a boot-time driver, it must be systemroot relative */
1264 if (dwStartType == SERVICE_BOOT_START)
1265 SourceName += 12;
1266
1267 /* Copy it */
1268 wcscpy(*lpCanonName, SourceName);
1269
1270 DPRINT("Canonicalized name %S\n", *lpCanonName);
1271 return NO_ERROR;
1272 }
1273
1274 /* Check if it has %SystemRoot% (len=13) */
1275 if (ServiceNameLen > 13 &&
1276 !wcsnicmp(L"%%SystemRoot%%\\", lpServiceName, 13))
1277 {
1278 /* Substitute %SystemRoot% with \\SystemRoot\\ */
1279 *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
1280
1281 if (*lpCanonName == NULL)
1282 {
1283 DPRINT1("Error allocating memory for canonized service name!\n");
1284 return ERROR_NOT_ENOUGH_MEMORY;
1285 }
1286
1287 /* If it's a boot-time driver, it must be systemroot relative */
1288 if (dwStartType == SERVICE_BOOT_START)
1289 wcscpy(*lpCanonName, L"\\SystemRoot\\");
1290
1291 wcscat(*lpCanonName, lpServiceName + 13);
1292
1293 DPRINT("Canonicalized name %S\n", *lpCanonName);
1294 return NO_ERROR;
1295 }
1296
1297 /* Check if it's a relative path name */
1298 if (lpServiceName[0] != L'\\' && lpServiceName[1] != L':')
1299 {
1300 *lpCanonName = LocalAlloc(LMEM_ZEROINIT, ServiceNameLen * sizeof(WCHAR) + sizeof(WCHAR));
1301
1302 if (*lpCanonName == NULL)
1303 {
1304 DPRINT1("Error allocating memory for canonized service name!\n");
1305 return ERROR_NOT_ENOUGH_MEMORY;
1306 }
1307
1308 /* Just copy it over without changing */
1309 wcscpy(*lpCanonName, lpServiceName);
1310
1311 return NO_ERROR;
1312 }
1313
1314 /* It seems to be a DOS path, convert it */
1315 if (!RtlDosPathNameToNtPathName_U(lpServiceName, &NtServiceName, NULL, NULL))
1316 {
1317 DPRINT1("RtlDosPathNameToNtPathName_U() failed!\n");
1318 return ERROR_INVALID_PARAMETER;
1319 }
1320
1321 *lpCanonName = LocalAlloc(LMEM_ZEROINIT, NtServiceName.Length + sizeof(WCHAR));
1322
1323 if (*lpCanonName == NULL)
1324 {
1325 DPRINT1("Error allocating memory for canonized service name!\n");
1326 RtlFreeUnicodeString(&NtServiceName);
1327 return ERROR_NOT_ENOUGH_MEMORY;
1328 }
1329
1330 /* Copy the string */
1331 wcsncpy(*lpCanonName, NtServiceName.Buffer, NtServiceName.Length / sizeof(WCHAR));
1332
1333 /* The unicode string is not needed anymore */
1334 RtlFreeUnicodeString(&NtServiceName);
1335
1336 if (dwStartType != SERVICE_BOOT_START)
1337 {
1338 DPRINT("Canonicalized name %S\n", *lpCanonName);
1339 return NO_ERROR;
1340 }
1341
1342 /* The service is boot-started, so must be relative */
1343 Result = ScmConvertToBootPathName(*lpCanonName, &RelativeName);
1344 if (Result)
1345 {
1346 /* There is a problem, free name and return */
1347 LocalFree(*lpCanonName);
1348 DPRINT1("Error converting named!\n");
1349 return Result;
1350 }
1351
1352 ASSERT(RelativeName);
1353
1354 /* Copy that string */
1355 wcscpy(*lpCanonName, RelativeName + 12);
1356
1357 /* Free the allocated buffer */
1358 LocalFree(RelativeName);
1359
1360 DPRINT("Canonicalized name %S\n", *lpCanonName);
1361
1362 /* Success */
1363 return NO_ERROR;
1364 }
1365
1366
1367 /* Function 12 */
1368 DWORD RCreateServiceW(
1369 handle_t BindingHandle,
1370 SC_RPC_HANDLE hSCManager,
1371 LPWSTR lpServiceName,
1372 LPWSTR lpDisplayName,
1373 DWORD dwDesiredAccess,
1374 DWORD dwServiceType,
1375 DWORD dwStartType,
1376 DWORD dwErrorControl,
1377 LPWSTR lpBinaryPathName,
1378 LPWSTR lpLoadOrderGroup,
1379 LPDWORD lpdwTagId,
1380 LPBYTE lpDependencies,
1381 DWORD dwDependSize,
1382 LPWSTR lpServiceStartName,
1383 LPBYTE lpPassword,
1384 DWORD dwPwSize,
1385 LPSC_RPC_HANDLE lpServiceHandle)
1386 {
1387 PMANAGER_HANDLE hManager;
1388 DWORD dwError = ERROR_SUCCESS;
1389 PSERVICE lpService = NULL;
1390 SC_HANDLE hServiceHandle = NULL;
1391 LPWSTR lpImagePath = NULL;
1392 HKEY hServiceKey = NULL;
1393
1394 DPRINT("RCreateServiceW() called\n");
1395 DPRINT("lpServiceName = %S\n", lpServiceName);
1396 DPRINT("lpDisplayName = %S\n", lpDisplayName);
1397 DPRINT("dwDesiredAccess = %lx\n", dwDesiredAccess);
1398 DPRINT("dwServiceType = %lu\n", dwServiceType);
1399 DPRINT("dwStartType = %lu\n", dwStartType);
1400 DPRINT("dwErrorControl = %lu\n", dwErrorControl);
1401 DPRINT("lpBinaryPathName = %S\n", lpBinaryPathName);
1402 DPRINT("lpLoadOrderGroup = %S\n", lpLoadOrderGroup);
1403
1404 if (ScmShutdown)
1405 return ERROR_SHUTDOWN_IN_PROGRESS;
1406
1407 hManager = (PMANAGER_HANDLE)hSCManager;
1408 if (!hManager || hManager->Handle.Tag != MANAGER_TAG)
1409 {
1410 DPRINT1("Invalid manager handle!\n");
1411 return ERROR_INVALID_HANDLE;
1412 }
1413
1414 /* Check access rights */
1415 if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
1416 SC_MANAGER_CREATE_SERVICE))
1417 {
1418 DPRINT1("Insufficient access rights! 0x%lx\n",
1419 hManager->Handle.DesiredAccess);
1420 return ERROR_ACCESS_DENIED;
1421 }
1422
1423 /* Fail if the service already exists! */
1424 if (ScmGetServiceEntryByName(lpServiceName) != NULL)
1425 return ERROR_SERVICE_EXISTS;
1426
1427 if (dwServiceType & SERVICE_DRIVER)
1428 {
1429 dwError = ScmCanonDriverImagePath(dwStartType,
1430 lpBinaryPathName,
1431 &lpImagePath);
1432
1433 if (dwError != ERROR_SUCCESS)
1434 goto done;
1435 }
1436
1437 /* Allocate a new service entry */
1438 dwError = ScmCreateNewServiceRecord(lpServiceName,
1439 &lpService);
1440 if (dwError != ERROR_SUCCESS)
1441 goto done;
1442
1443 /* Fill the new service entry */
1444 lpService->Status.dwServiceType = dwServiceType;
1445 lpService->dwStartType = dwStartType;
1446 lpService->dwErrorControl = dwErrorControl;
1447
1448 /* Fill the display name */
1449 if (lpDisplayName != NULL &&
1450 *lpDisplayName != 0 &&
1451 wcsicmp(lpService->lpDisplayName, lpDisplayName) != 0)
1452 {
1453 lpService->lpDisplayName = (WCHAR*) HeapAlloc(GetProcessHeap(), 0,
1454 (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
1455 if (lpService->lpDisplayName == NULL)
1456 {
1457 dwError = ERROR_NOT_ENOUGH_MEMORY;
1458 goto done;
1459 }
1460 wcscpy(lpService->lpDisplayName, lpDisplayName);
1461 }
1462
1463 /* Assign the service to a group */
1464 if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
1465 {
1466 dwError = ScmSetServiceGroup(lpService,
1467 lpLoadOrderGroup);
1468 if (dwError != ERROR_SUCCESS)
1469 goto done;
1470 }
1471
1472 /* Assign a new tag */
1473 if (lpdwTagId != NULL)
1474 {
1475 dwError = ScmAssignNewTag(lpService);
1476 if (dwError != ERROR_SUCCESS)
1477 goto done;
1478 }
1479
1480 /* Write service data to the registry */
1481 /* Create the service key */
1482 dwError = ScmCreateServiceKey(lpServiceName,
1483 KEY_WRITE,
1484 &hServiceKey);
1485 if (dwError != ERROR_SUCCESS)
1486 goto done;
1487
1488 /* Set the display name */
1489 if (lpDisplayName != NULL && *lpDisplayName != 0)
1490 {
1491 RegSetValueExW(hServiceKey,
1492 L"DisplayName",
1493 0,
1494 REG_SZ,
1495 (LPBYTE)lpDisplayName,
1496 (wcslen(lpDisplayName) + 1) * sizeof(WCHAR));
1497 }
1498
1499 /* Set the service type */
1500 dwError = RegSetValueExW(hServiceKey,
1501 L"Type",
1502 0,
1503 REG_DWORD,
1504 (LPBYTE)&dwServiceType,
1505 sizeof(DWORD));
1506 if (dwError != ERROR_SUCCESS)
1507 goto done;
1508
1509 /* Set the start value */
1510 dwError = RegSetValueExW(hServiceKey,
1511 L"Start",
1512 0,
1513 REG_DWORD,
1514 (LPBYTE)&dwStartType,
1515 sizeof(DWORD));
1516 if (dwError != ERROR_SUCCESS)
1517 goto done;
1518
1519 /* Set the error control value */
1520 dwError = RegSetValueExW(hServiceKey,
1521 L"ErrorControl",
1522 0,
1523 REG_DWORD,
1524 (LPBYTE)&dwErrorControl,
1525 sizeof(DWORD));
1526 if (dwError != ERROR_SUCCESS)
1527 goto done;
1528
1529 /* Set the image path */
1530 if (dwServiceType & SERVICE_WIN32)
1531 {
1532 dwError = RegSetValueExW(hServiceKey,
1533 L"ImagePath",
1534 0,
1535 REG_EXPAND_SZ,
1536 (LPBYTE)lpBinaryPathName,
1537 (wcslen(lpBinaryPathName) + 1) * sizeof(WCHAR));
1538 if (dwError != ERROR_SUCCESS)
1539 goto done;
1540 }
1541 else if (dwServiceType & SERVICE_DRIVER)
1542 {
1543 dwError = RegSetValueExW(hServiceKey,
1544 L"ImagePath",
1545 0,
1546 REG_EXPAND_SZ,
1547 (LPBYTE)lpImagePath,
1548 (wcslen(lpImagePath) + 1) * sizeof(WCHAR));
1549 if (dwError != ERROR_SUCCESS)
1550 goto done;
1551 }
1552
1553 /* Set the group name */
1554 if (lpLoadOrderGroup != NULL && *lpLoadOrderGroup != 0)
1555 {
1556 dwError = RegSetValueExW(hServiceKey,
1557 L"Group",
1558 0,
1559 REG_SZ,
1560 (LPBYTE)lpLoadOrderGroup,
1561 (wcslen(lpLoadOrderGroup) + 1) * sizeof(WCHAR));
1562 if (dwError != ERROR_SUCCESS)
1563 goto done;
1564 }
1565
1566 if (lpdwTagId != NULL)
1567 {
1568 dwError = RegSetValueExW(hServiceKey,
1569 L"Tag",
1570 0,
1571 REG_DWORD,
1572 (LPBYTE)&lpService->dwTag,
1573 sizeof(DWORD));
1574 if (dwError != ERROR_SUCCESS)
1575 goto done;
1576 }
1577
1578 /* Write dependencies */
1579 if (lpDependencies != NULL && *lpDependencies != 0)
1580 {
1581 dwError = ScmWriteDependencies(hServiceKey,
1582 (LPWSTR)lpDependencies,
1583 dwDependSize);
1584 if (dwError != ERROR_SUCCESS)
1585 goto done;
1586 }
1587
1588 if (lpPassword != NULL)
1589 {
1590 /* FIXME: Write password */
1591 }
1592
1593 dwError = ScmCreateServiceHandle(lpService,
1594 &hServiceHandle);
1595 if (dwError != ERROR_SUCCESS)
1596 goto done;
1597
1598 dwError = ScmCheckAccess(hServiceHandle,
1599 dwDesiredAccess);
1600 if (dwError != ERROR_SUCCESS)
1601 goto done;
1602
1603 done:;
1604 if (hServiceKey != NULL)
1605 RegCloseKey(hServiceKey);
1606
1607 if (dwError == ERROR_SUCCESS)
1608 {
1609 DPRINT("hService %p\n", hServiceHandle);
1610 *lpServiceHandle = (unsigned long)hServiceHandle; /* FIXME: 64 bit portability */
1611
1612 if (lpdwTagId != NULL)
1613 *lpdwTagId = lpService->dwTag;
1614 }
1615 else
1616 {
1617 /* Release the display name buffer */
1618 if (lpService->lpServiceName != NULL)
1619 HeapFree(GetProcessHeap(), 0, lpService->lpDisplayName);
1620
1621 if (hServiceHandle)
1622 {
1623 /* Remove the service handle */
1624 HeapFree(GetProcessHeap(), 0, hServiceHandle);
1625 }
1626
1627 if (lpService != NULL)
1628 {
1629 /* FIXME: remove the service entry */
1630 }
1631 }
1632
1633 if (lpImagePath != NULL)
1634 HeapFree(GetProcessHeap(), 0, lpImagePath);
1635
1636 DPRINT("RCreateServiceW() done (Error %lu)\n", dwError);
1637
1638 return dwError;
1639 }
1640
1641
1642 /* Function 13 */
1643 DWORD REnumDependentServicesW(
1644 handle_t BindingHandle,
1645 SC_RPC_HANDLE hService,
1646 DWORD dwServiceState,
1647 LPBYTE lpServices,
1648 DWORD cbBufSize,
1649 LPBOUNDED_DWORD_256K pcbBytesNeeded,
1650 LPBOUNDED_DWORD_256K lpServicesReturned)
1651 {
1652 DWORD dwError = ERROR_SUCCESS;
1653
1654 UNIMPLEMENTED;
1655 *pcbBytesNeeded = 0;
1656 *lpServicesReturned = 0;
1657
1658 DPRINT1("REnumDependentServicesW() done (Error %lu)\n", dwError);
1659
1660 return dwError;
1661 }
1662
1663
1664 /* Function 14 */
1665 DWORD REnumServicesStatusW(
1666 handle_t BindingHandle,
1667 SC_RPC_HANDLE hSCManager,
1668 DWORD dwServiceType,
1669 DWORD dwServiceState,
1670 LPBYTE lpBuffer,
1671 DWORD dwBufSize,
1672 LPBOUNDED_DWORD_256K pcbBytesNeeded,
1673 LPBOUNDED_DWORD_256K lpServicesReturned,
1674 LPBOUNDED_DWORD_256K lpResumeHandle)
1675 {
1676 PMANAGER_HANDLE hManager;
1677 PSERVICE lpService;
1678 DWORD dwError = ERROR_SUCCESS;
1679 PLIST_ENTRY ServiceEntry;
1680 PSERVICE CurrentService;
1681 DWORD dwState;
1682 DWORD dwRequiredSize;
1683 DWORD dwServiceCount;
1684 DWORD dwSize;
1685 DWORD dwLastResumeCount;
1686 LPENUM_SERVICE_STATUSW lpStatusPtr;
1687 LPWSTR lpStringPtr;
1688
1689 DPRINT("REnumServicesStatusW() called\n");
1690
1691 if (ScmShutdown)
1692 return ERROR_SHUTDOWN_IN_PROGRESS;
1693
1694 hManager = (PMANAGER_HANDLE)hSCManager;
1695 if (!hManager || hManager->Handle.Tag != MANAGER_TAG)
1696 {
1697 DPRINT1("Invalid manager handle!\n");
1698 return ERROR_INVALID_HANDLE;
1699 }
1700
1701 /* Check access rights */
1702 if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
1703 SC_MANAGER_ENUMERATE_SERVICE))
1704 {
1705 DPRINT1("Insufficient access rights! 0x%lx\n",
1706 hManager->Handle.DesiredAccess);
1707 return ERROR_ACCESS_DENIED;
1708 }
1709
1710 *pcbBytesNeeded = 0;
1711 *lpServicesReturned = 0;
1712
1713 dwLastResumeCount = *lpResumeHandle;
1714
1715 /* FIXME: Lock the service list shared */
1716
1717 lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
1718 if (lpService == NULL)
1719 {
1720 dwError = ERROR_SUCCESS;
1721 goto Done;
1722 }
1723
1724 dwRequiredSize = 0;
1725 dwServiceCount = 0;
1726
1727 for (ServiceEntry = &lpService->ServiceListEntry;
1728 ServiceEntry != &ServiceListHead;
1729 ServiceEntry = ServiceEntry->Flink)
1730 {
1731 CurrentService = CONTAINING_RECORD(ServiceEntry,
1732 SERVICE,
1733 ServiceListEntry);
1734
1735 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
1736 continue;
1737
1738 dwState = SERVICE_ACTIVE;
1739 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
1740 dwState = SERVICE_INACTIVE;
1741
1742 if ((dwState & dwServiceState) == 0)
1743 continue;
1744
1745 dwSize = sizeof(ENUM_SERVICE_STATUSW) +
1746 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
1747 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
1748
1749 if (dwRequiredSize + dwSize <= dwBufSize)
1750 {
1751 DPRINT("Service name: %S fit\n", CurrentService->lpServiceName);
1752 dwRequiredSize += dwSize;
1753 dwServiceCount++;
1754 dwLastResumeCount = CurrentService->dwResumeCount;
1755 }
1756 else
1757 {
1758 DPRINT("Service name: %S no fit\n", CurrentService->lpServiceName);
1759 break;
1760 }
1761
1762 }
1763
1764 DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
1765 DPRINT("dwServiceCount: %lu\n", dwServiceCount);
1766
1767 for (;
1768 ServiceEntry != &ServiceListHead;
1769 ServiceEntry = ServiceEntry->Flink)
1770 {
1771 CurrentService = CONTAINING_RECORD(ServiceEntry,
1772 SERVICE,
1773 ServiceListEntry);
1774
1775 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
1776 continue;
1777
1778 dwState = SERVICE_ACTIVE;
1779 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
1780 dwState = SERVICE_INACTIVE;
1781
1782 if ((dwState & dwServiceState) == 0)
1783 continue;
1784
1785 dwRequiredSize += (sizeof(ENUM_SERVICE_STATUSW) +
1786 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
1787 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
1788
1789 dwError = ERROR_MORE_DATA;
1790 }
1791
1792 DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
1793
1794 *lpResumeHandle = dwLastResumeCount;
1795 *lpServicesReturned = dwServiceCount;
1796 *pcbBytesNeeded = dwRequiredSize;
1797
1798 lpStatusPtr = (LPENUM_SERVICE_STATUSW)lpBuffer;
1799 lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
1800 dwServiceCount * sizeof(ENUM_SERVICE_STATUSW));
1801
1802 dwRequiredSize = 0;
1803 for (ServiceEntry = &lpService->ServiceListEntry;
1804 ServiceEntry != &ServiceListHead;
1805 ServiceEntry = ServiceEntry->Flink)
1806 {
1807 CurrentService = CONTAINING_RECORD(ServiceEntry,
1808 SERVICE,
1809 ServiceListEntry);
1810
1811 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
1812 continue;
1813
1814 dwState = SERVICE_ACTIVE;
1815 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
1816 dwState = SERVICE_INACTIVE;
1817
1818 if ((dwState & dwServiceState) == 0)
1819 continue;
1820
1821 dwSize = sizeof(ENUM_SERVICE_STATUSW) +
1822 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
1823 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
1824
1825 if (dwRequiredSize + dwSize <= dwBufSize)
1826 {
1827 /* Copy the service name */
1828 wcscpy(lpStringPtr,
1829 CurrentService->lpServiceName);
1830 lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
1831 lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
1832
1833 /* Copy the display name */
1834 wcscpy(lpStringPtr,
1835 CurrentService->lpDisplayName);
1836 lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
1837 lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
1838
1839 /* Copy the status information */
1840 memcpy(&lpStatusPtr->ServiceStatus,
1841 &CurrentService->Status,
1842 sizeof(SERVICE_STATUS));
1843
1844 lpStatusPtr++;
1845 dwRequiredSize += dwSize;
1846 }
1847 else
1848 {
1849 break;
1850 }
1851
1852 }
1853
1854 Done:;
1855 /* FIXME: Unlock the service list */
1856
1857 DPRINT("REnumServicesStatusW() done (Error %lu)\n", dwError);
1858
1859 return dwError;
1860 }
1861
1862
1863 /* Function 15 */
1864 DWORD ROpenSCManagerW(
1865 handle_t BindingHandle,
1866 LPWSTR lpMachineName,
1867 LPWSTR lpDatabaseName,
1868 DWORD dwDesiredAccess,
1869 LPSC_RPC_HANDLE lpScHandle)
1870 {
1871 DWORD dwError;
1872 SC_HANDLE hHandle;
1873
1874 DPRINT("ROpenSCManagerW() called\n");
1875 DPRINT("lpMachineName = %p\n", lpMachineName);
1876 DPRINT("lpMachineName: %S\n", lpMachineName);
1877 DPRINT("lpDataBaseName = %p\n", lpDatabaseName);
1878 DPRINT("lpDataBaseName: %S\n", lpDatabaseName);
1879 DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
1880
1881 if (ScmShutdown)
1882 return ERROR_SHUTDOWN_IN_PROGRESS;
1883
1884 if (!lpScHandle)
1885 return ERROR_INVALID_PARAMETER;
1886
1887 dwError = ScmCreateManagerHandle(lpDatabaseName,
1888 &hHandle);
1889 if (dwError != ERROR_SUCCESS)
1890 {
1891 DPRINT1("ScmCreateManagerHandle() failed (Error %lu)\n", dwError);
1892 return dwError;
1893 }
1894
1895 /* Check the desired access */
1896 dwError = ScmCheckAccess(hHandle,
1897 dwDesiredAccess | SC_MANAGER_CONNECT);
1898 if (dwError != ERROR_SUCCESS)
1899 {
1900 DPRINT1("ScmCheckAccess() failed (Error %lu)\n", dwError);
1901 HeapFree(GetProcessHeap(), 0, hHandle);
1902 return dwError;
1903 }
1904
1905 *lpScHandle = (unsigned long)hHandle; /* FIXME: 64 bit portability */
1906 DPRINT("*hScm = %p\n", *lpScHandle);
1907
1908 DPRINT("ROpenSCManagerW() done\n");
1909
1910 return ERROR_SUCCESS;
1911 }
1912
1913
1914 /* Function 16 */
1915 DWORD ROpenServiceW(
1916 handle_t BindingHandle,
1917 SC_RPC_HANDLE hSCManager,
1918 LPWSTR lpServiceName,
1919 DWORD dwDesiredAccess,
1920 LPSC_RPC_HANDLE lpServiceHandle)
1921 {
1922 PSERVICE lpService;
1923 PMANAGER_HANDLE hManager;
1924 SC_HANDLE hHandle;
1925 DWORD dwError;
1926
1927 DPRINT("ROpenServiceW() called\n");
1928 DPRINT("hSCManager = %p\n", hSCManager);
1929 DPRINT("lpServiceName = %p\n", lpServiceName);
1930 DPRINT("lpServiceName: %S\n", lpServiceName);
1931 DPRINT("dwDesiredAccess = %x\n", dwDesiredAccess);
1932
1933 if (ScmShutdown)
1934 return ERROR_SHUTDOWN_IN_PROGRESS;
1935
1936 if (!lpServiceHandle)
1937 return ERROR_INVALID_PARAMETER;
1938
1939 hManager = (PMANAGER_HANDLE)hSCManager;
1940 if (!hManager || hManager->Handle.Tag != MANAGER_TAG)
1941 {
1942 DPRINT1("Invalid manager handle!\n");
1943 return ERROR_INVALID_HANDLE;
1944 }
1945
1946 /* FIXME: Lock the service list */
1947
1948 /* Get service database entry */
1949 lpService = ScmGetServiceEntryByName(lpServiceName);
1950 if (lpService == NULL)
1951 {
1952 DPRINT("Could not find a service!\n");
1953 return ERROR_SERVICE_DOES_NOT_EXIST;
1954 }
1955
1956 /* Create a service handle */
1957 dwError = ScmCreateServiceHandle(lpService,
1958 &hHandle);
1959 if (dwError != ERROR_SUCCESS)
1960 {
1961 DPRINT1("ScmCreateServiceHandle() failed (Error %lu)\n", dwError);
1962 return dwError;
1963 }
1964
1965 /* Check the desired access */
1966 dwError = ScmCheckAccess(hHandle,
1967 dwDesiredAccess);
1968 if (dwError != ERROR_SUCCESS)
1969 {
1970 DPRINT1("ScmCheckAccess() failed (Error %lu)\n", dwError);
1971 HeapFree(GetProcessHeap(), 0, hHandle);
1972 return dwError;
1973 }
1974
1975 *lpServiceHandle = (unsigned long)hHandle; /* FIXME: 64 bit portability */
1976 DPRINT("*hService = %p\n", *lpServiceHandle);
1977
1978 DPRINT("ROpenServiceW() done\n");
1979
1980 return ERROR_SUCCESS;
1981 }
1982
1983
1984 /* Function 17 */
1985 DWORD RQueryServiceConfigW(
1986 handle_t BindingHandle,
1987 SC_RPC_HANDLE hService,
1988 LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGW lpServiceConfig,
1989 DWORD cbBufSize,
1990 LPBOUNDED_DWORD_8K pcbBytesNeeded)
1991 {
1992 LPQUERY_SERVICE_CONFIGW lpServiceConfig = (LPQUERY_SERVICE_CONFIGW)lpBuf;
1993 DWORD dwError = ERROR_SUCCESS;
1994 PSERVICE_HANDLE hSvc;
1995 PSERVICE lpService = NULL;
1996 HKEY hServiceKey = NULL;
1997 LPWSTR lpImagePath = NULL;
1998 LPWSTR lpServiceStartName = NULL;
1999 DWORD dwRequiredSize;
2000 LPQUERY_SERVICE_CONFIGW lpConfig;
2001 LPWSTR lpStr;
2002
2003 DPRINT("RQueryServiceConfigW() called\n");
2004
2005 if (ScmShutdown)
2006 return ERROR_SHUTDOWN_IN_PROGRESS;
2007
2008 hSvc = (PSERVICE_HANDLE)hService;
2009 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2010 {
2011 DPRINT1("Invalid handle tag!\n");
2012 return ERROR_INVALID_HANDLE;
2013 }
2014
2015 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2016 SERVICE_QUERY_CONFIG))
2017 {
2018 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2019 return ERROR_ACCESS_DENIED;
2020 }
2021
2022 lpService = hSvc->ServiceEntry;
2023 if (lpService == NULL)
2024 {
2025 DPRINT1("lpService == NULL!\n");
2026 return ERROR_INVALID_HANDLE;
2027 }
2028
2029 /* FIXME: Lock the service database shared */
2030
2031 dwError = ScmOpenServiceKey(lpService->lpServiceName,
2032 KEY_READ,
2033 &hServiceKey);
2034 if (dwError != ERROR_SUCCESS)
2035 goto Done;
2036
2037 dwError = ScmReadString(hServiceKey,
2038 L"ImagePath",
2039 &lpImagePath);
2040 if (dwError != ERROR_SUCCESS)
2041 goto Done;
2042
2043 ScmReadString(hServiceKey,
2044 L"ObjectName",
2045 &lpServiceStartName);
2046
2047 dwRequiredSize = sizeof(QUERY_SERVICE_CONFIGW);
2048
2049 if (lpImagePath != NULL)
2050 dwRequiredSize += ((wcslen(lpImagePath) + 1) * sizeof(WCHAR));
2051
2052 if (lpService->lpGroup != NULL)
2053 dwRequiredSize += ((wcslen(lpService->lpGroup->lpGroupName) + 1) * sizeof(WCHAR));
2054
2055 /* FIXME: Add Dependencies length*/
2056
2057 if (lpServiceStartName != NULL)
2058 dwRequiredSize += ((wcslen(lpServiceStartName) + 1) * sizeof(WCHAR));
2059
2060 if (lpService->lpDisplayName != NULL)
2061 dwRequiredSize += ((wcslen(lpService->lpDisplayName) + 1) * sizeof(WCHAR));
2062
2063 if (lpServiceConfig == NULL || cbBufSize < dwRequiredSize)
2064 {
2065 dwError = ERROR_INSUFFICIENT_BUFFER;
2066 }
2067 else
2068 {
2069 lpConfig = (LPQUERY_SERVICE_CONFIGW)lpServiceConfig;
2070 lpConfig->dwServiceType = lpService->Status.dwServiceType;
2071 lpConfig->dwStartType = lpService->dwStartType;
2072 lpConfig->dwErrorControl = lpService->dwErrorControl;
2073 lpConfig->dwTagId = lpService->dwTag;
2074
2075 lpStr = (LPWSTR)(lpConfig + 1);
2076
2077 if (lpImagePath != NULL)
2078 {
2079 wcscpy(lpStr, lpImagePath);
2080 lpConfig->lpBinaryPathName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
2081 lpStr += (wcslen(lpImagePath) + 1);
2082 }
2083 else
2084 {
2085 lpConfig->lpBinaryPathName = NULL;
2086 }
2087
2088 if (lpService->lpGroup != NULL)
2089 {
2090 wcscpy(lpStr, lpService->lpGroup->lpGroupName);
2091 lpConfig->lpLoadOrderGroup = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
2092 lpStr += (wcslen(lpService->lpGroup->lpGroupName) + 1);
2093 }
2094 else
2095 {
2096 lpConfig->lpLoadOrderGroup = NULL;
2097 }
2098
2099 /* FIXME: Append Dependencies */
2100 lpConfig->lpDependencies = NULL;
2101
2102 if (lpServiceStartName != NULL)
2103 {
2104 wcscpy(lpStr, lpServiceStartName);
2105 lpConfig->lpServiceStartName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
2106 lpStr += (wcslen(lpServiceStartName) + 1);
2107 }
2108 else
2109 {
2110 lpConfig->lpServiceStartName = NULL;
2111 }
2112
2113 if (lpService->lpDisplayName != NULL)
2114 {
2115 wcscpy(lpStr, lpService->lpDisplayName);
2116 lpConfig->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpConfig);
2117 }
2118 else
2119 {
2120 lpConfig->lpDisplayName = NULL;
2121 }
2122 }
2123
2124 if (pcbBytesNeeded != NULL)
2125 *pcbBytesNeeded = dwRequiredSize;
2126
2127 Done:;
2128 if (lpImagePath != NULL)
2129 HeapFree(GetProcessHeap(), 0, lpImagePath);
2130
2131 if (lpServiceStartName != NULL)
2132 HeapFree(GetProcessHeap(), 0, lpServiceStartName);
2133
2134 if (hServiceKey != NULL)
2135 RegCloseKey(hServiceKey);
2136
2137 /* FIXME: Unlock the service database */
2138
2139 DPRINT("RQueryServiceConfigW() done\n");
2140
2141 return dwError;
2142 }
2143
2144
2145 /* Function 18 */
2146 DWORD RQueryServiceLockStatusW(
2147 handle_t BindingHandle,
2148 SC_RPC_HANDLE hSCManager,
2149 LPQUERY_SERVICE_LOCK_STATUSW lpLockStatus,
2150 DWORD cbBufSize,
2151 LPBOUNDED_DWORD_4K pcbBytesNeeded)
2152 {
2153 UNIMPLEMENTED;
2154 return ERROR_CALL_NOT_IMPLEMENTED;
2155 }
2156
2157
2158 /* Function 19 */
2159 DWORD RStartServiceW(
2160 handle_t BindingHandle,
2161 SC_RPC_HANDLE hService,
2162 DWORD argc,
2163 LPSTRING_PTRSW argv)
2164 {
2165 DWORD dwError = ERROR_SUCCESS;
2166 PSERVICE_HANDLE hSvc;
2167 PSERVICE lpService = NULL;
2168
2169 DPRINT("RStartServiceW() called\n");
2170
2171 if (ScmShutdown)
2172 return ERROR_SHUTDOWN_IN_PROGRESS;
2173
2174 hSvc = (PSERVICE_HANDLE)hService;
2175 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2176 {
2177 DPRINT1("Invalid handle tag!\n");
2178 return ERROR_INVALID_HANDLE;
2179 }
2180
2181 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2182 SERVICE_START))
2183 {
2184 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2185 return ERROR_ACCESS_DENIED;
2186 }
2187
2188 lpService = hSvc->ServiceEntry;
2189 if (lpService == NULL)
2190 {
2191 DPRINT1("lpService == NULL!\n");
2192 return ERROR_INVALID_HANDLE;
2193 }
2194
2195 if (lpService->dwStartType == SERVICE_DISABLED)
2196 return ERROR_SERVICE_DISABLED;
2197
2198 if (lpService->bDeleted)
2199 return ERROR_SERVICE_MARKED_FOR_DELETE;
2200
2201 if (argv) {
2202 UNIMPLEMENTED;
2203 argv = NULL;
2204 }
2205
2206 /* Start the service */
2207 dwError = ScmStartService(lpService, argc, (LPWSTR *)argv);
2208
2209 return dwError;
2210 }
2211
2212
2213 /* Function 20 */
2214 DWORD RGetServiceDisplayNameW(
2215 handle_t BindingHandle,
2216 SC_RPC_HANDLE hSCManager,
2217 LPWSTR lpServiceName,
2218 LPWSTR lpDisplayName,
2219 DWORD *lpcchBuffer)
2220 {
2221 // PMANAGER_HANDLE hManager;
2222 PSERVICE lpService;
2223 DWORD dwLength;
2224 DWORD dwError;
2225
2226 DPRINT("RGetServiceDisplayNameW() called\n");
2227 DPRINT("hSCManager = %p\n", hSCManager);
2228 DPRINT("lpServiceName: %S\n", lpServiceName);
2229 DPRINT("lpDisplayName: %p\n", lpDisplayName);
2230 DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
2231
2232 // hManager = (PMANAGER_HANDLE)hSCManager;
2233 // if (hManager->Handle.Tag != MANAGER_TAG)
2234 // {
2235 // DPRINT1("Invalid manager handle!\n");
2236 // return ERROR_INVALID_HANDLE;
2237 // }
2238
2239 /* Get service database entry */
2240 lpService = ScmGetServiceEntryByName(lpServiceName);
2241 if (lpService == NULL)
2242 {
2243 DPRINT1("Could not find a service!\n");
2244 return ERROR_SERVICE_DOES_NOT_EXIST;
2245 }
2246
2247 dwLength = wcslen(lpService->lpDisplayName) + 1;
2248
2249 if (lpDisplayName != NULL &&
2250 *lpcchBuffer >= dwLength)
2251 {
2252 wcscpy(lpDisplayName, lpService->lpDisplayName);
2253 }
2254
2255 dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
2256
2257 *lpcchBuffer = dwLength;
2258
2259 return dwError;
2260 }
2261
2262
2263 /* Function 21 */
2264 DWORD RGetServiceKeyNameW(
2265 handle_t BindingHandle,
2266 SC_RPC_HANDLE hSCManager,
2267 LPWSTR lpDisplayName,
2268 LPWSTR lpServiceName,
2269 DWORD *lpcchBuffer)
2270 {
2271 // PMANAGER_HANDLE hManager;
2272 PSERVICE lpService;
2273 DWORD dwLength;
2274 DWORD dwError;
2275
2276 DPRINT("RGetServiceKeyNameW() called\n");
2277 DPRINT("hSCManager = %p\n", hSCManager);
2278 DPRINT("lpDisplayName: %S\n", lpDisplayName);
2279 DPRINT("lpServiceName: %p\n", lpServiceName);
2280 DPRINT("*lpcchBuffer: %lu\n", *lpcchBuffer);
2281
2282 // hManager = (PMANAGER_HANDLE)hSCManager;
2283 // if (hManager->Handle.Tag != MANAGER_TAG)
2284 // {
2285 // DPRINT1("Invalid manager handle!\n");
2286 // return ERROR_INVALID_HANDLE;
2287 // }
2288
2289 /* Get service database entry */
2290 lpService = ScmGetServiceEntryByDisplayName(lpDisplayName);
2291 if (lpService == NULL)
2292 {
2293 DPRINT1("Could not find a service!\n");
2294 return ERROR_SERVICE_DOES_NOT_EXIST;
2295 }
2296
2297 dwLength = wcslen(lpService->lpServiceName) + 1;
2298
2299 if (lpServiceName != NULL &&
2300 *lpcchBuffer >= dwLength)
2301 {
2302 wcscpy(lpServiceName, lpService->lpServiceName);
2303 }
2304
2305 dwError = (*lpcchBuffer > dwLength) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
2306
2307 *lpcchBuffer = dwLength;
2308
2309 return dwError;
2310 }
2311
2312
2313 /* Function 22 */
2314 DWORD RSetServiceBitsA(
2315 handle_t BindingHandle,
2316 SC_RPC_HANDLE hServiceStatus,
2317 DWORD dwServiceBits,
2318 int bSetBitsOn,
2319 int bUpdateImmediately,
2320 char *lpString)
2321 {
2322 UNIMPLEMENTED;
2323 return ERROR_CALL_NOT_IMPLEMENTED;
2324 }
2325
2326
2327 /* Function 23 */
2328 DWORD RChangeServiceConfigA(
2329 handle_t BindingHandle,
2330 SC_RPC_HANDLE hService,
2331 DWORD dwServiceType,
2332 DWORD dwStartType,
2333 DWORD dwErrorControl,
2334 LPSTR lpBinaryPathName,
2335 LPSTR lpLoadOrderGroup,
2336 LPDWORD lpdwTagId,
2337 LPSTR lpDependencies,
2338 DWORD dwDependSize,
2339 LPSTR lpServiceStartName,
2340 LPBYTE lpPassword,
2341 DWORD dwPwSize,
2342 LPSTR lpDisplayName)
2343 {
2344 UNIMPLEMENTED;
2345 return ERROR_CALL_NOT_IMPLEMENTED;
2346 }
2347
2348
2349 /* Function 24 */
2350 DWORD RCreateServiceA(
2351 handle_t BindingHandle,
2352 SC_RPC_HANDLE hSCManager,
2353 LPSTR lpServiceName,
2354 LPSTR lpDisplayName,
2355 DWORD dwDesiredAccess,
2356 DWORD dwServiceType,
2357 DWORD dwStartType,
2358 DWORD dwErrorControl,
2359 LPSTR lpBinaryPathName,
2360 LPSTR lpLoadOrderGroup,
2361 LPDWORD lpdwTagId,
2362 LPBYTE lpDependencies,
2363 DWORD dwDependSize,
2364 LPSTR lpServiceStartName,
2365 LPBYTE lpPassword,
2366 DWORD dwPwSize,
2367 LPSC_RPC_HANDLE lpServiceHandle)
2368 {
2369 UNIMPLEMENTED;
2370 return ERROR_CALL_NOT_IMPLEMENTED;
2371 }
2372
2373
2374 /* Function 25 */
2375 DWORD REnumDependentServicesA(
2376 handle_t BindingHandle,
2377 SC_RPC_HANDLE hService,
2378 DWORD dwServiceState,
2379 LPBYTE lpServices,
2380 DWORD cbBufSize,
2381 LPBOUNDED_DWORD_256K pcbBytesNeeded,
2382 LPBOUNDED_DWORD_256K lpServicesReturned)
2383 {
2384 UNIMPLEMENTED;
2385 *pcbBytesNeeded = 0;
2386 *lpServicesReturned = 0;
2387 return ERROR_CALL_NOT_IMPLEMENTED;
2388 }
2389
2390
2391 /* Function 26 */
2392 DWORD REnumServicesStatusA(
2393 handle_t BindingHandle,
2394 SC_RPC_HANDLE hSCManager,
2395 DWORD dwServiceType,
2396 DWORD dwServiceState,
2397 LPBYTE lpBuffer,
2398 DWORD dwBufSize,
2399 LPBOUNDED_DWORD_256K pcbBytesNeeded,
2400 LPBOUNDED_DWORD_256K lpServicesReturned,
2401 LPBOUNDED_DWORD_256K lpResumeHandle)
2402 {
2403 UNIMPLEMENTED;
2404 return ERROR_CALL_NOT_IMPLEMENTED;
2405 }
2406
2407
2408 /* Function 27 */
2409 DWORD ROpenSCManagerA(
2410 handle_t BindingHandle,
2411 LPSTR lpMachineName,
2412 LPSTR lpDatabaseName,
2413 DWORD dwDesiredAccess,
2414 LPSC_RPC_HANDLE lpScHandle)
2415 {
2416 UNICODE_STRING MachineName;
2417 UNICODE_STRING DatabaseName;
2418 DWORD dwError;
2419
2420 DPRINT("ROpenSCManagerA() called\n");
2421
2422 if (lpMachineName)
2423 RtlCreateUnicodeStringFromAsciiz(&MachineName,
2424 lpMachineName);
2425
2426 if (lpDatabaseName)
2427 RtlCreateUnicodeStringFromAsciiz(&DatabaseName,
2428 lpDatabaseName);
2429
2430 dwError = ROpenSCManagerW(BindingHandle,
2431 lpMachineName ? MachineName.Buffer : NULL,
2432 lpDatabaseName ? DatabaseName.Buffer : NULL,
2433 dwDesiredAccess,
2434 lpScHandle);
2435
2436 if (lpMachineName)
2437 RtlFreeUnicodeString(&MachineName);
2438
2439 if (lpDatabaseName)
2440 RtlFreeUnicodeString(&DatabaseName);
2441
2442 return dwError;
2443 }
2444
2445
2446 /* Function 28 */
2447 DWORD ROpenServiceA(
2448 handle_t BindingHandle,
2449 SC_RPC_HANDLE hSCManager,
2450 LPSTR lpServiceName,
2451 DWORD dwDesiredAccess,
2452 LPSC_RPC_HANDLE lpServiceHandle)
2453 {
2454 UNICODE_STRING ServiceName;
2455 DWORD dwError;
2456
2457 DPRINT("ROpenServiceA() called\n");
2458
2459 RtlCreateUnicodeStringFromAsciiz(&ServiceName,
2460 lpServiceName);
2461
2462 dwError = ROpenServiceW(BindingHandle,
2463 hSCManager,
2464 ServiceName.Buffer,
2465 dwDesiredAccess,
2466 lpServiceHandle);
2467
2468 RtlFreeUnicodeString(&ServiceName);
2469
2470 return dwError;
2471 }
2472
2473
2474 /* Function 29 */
2475 DWORD RQueryServiceConfigA(
2476 handle_t BindingHandle,
2477 SC_RPC_HANDLE hService,
2478 LPBYTE lpBuf, //LPQUERY_SERVICE_CONFIGA lpServiceConfig,
2479 DWORD cbBufSize,
2480 LPBOUNDED_DWORD_8K pcbBytesNeeded)
2481 {
2482 UNIMPLEMENTED;
2483 return ERROR_CALL_NOT_IMPLEMENTED;
2484 }
2485
2486
2487 /* Function 30 */
2488 DWORD RQueryServiceLockStatusA(
2489 handle_t BindingHandle,
2490 SC_RPC_HANDLE hSCManager,
2491 LPQUERY_SERVICE_LOCK_STATUSA lpLockStatus,
2492 DWORD cbBufSize,
2493 LPBOUNDED_DWORD_4K pcbBytesNeeded)
2494 {
2495 UNIMPLEMENTED;
2496 return ERROR_CALL_NOT_IMPLEMENTED;
2497 }
2498
2499
2500 /* Function 31 */
2501 DWORD RStartServiceA(
2502 handle_t BindingHandle,
2503 SC_RPC_HANDLE hService,
2504 DWORD argc,
2505 LPSTRING_PTRSA argv)
2506 {
2507 DWORD dwError = ERROR_SUCCESS;
2508 PSERVICE_HANDLE hSvc;
2509 PSERVICE lpService = NULL;
2510
2511 DPRINT1("RStartServiceA() called\n");
2512
2513 if (ScmShutdown)
2514 return ERROR_SHUTDOWN_IN_PROGRESS;
2515
2516 hSvc = (PSERVICE_HANDLE)hService;
2517 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2518 {
2519 DPRINT1("Invalid handle tag!\n");
2520 return ERROR_INVALID_HANDLE;
2521 }
2522
2523 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2524 SERVICE_START))
2525 {
2526 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2527 return ERROR_ACCESS_DENIED;
2528 }
2529
2530 lpService = hSvc->ServiceEntry;
2531 if (lpService == NULL)
2532 {
2533 DPRINT1("lpService == NULL!\n");
2534 return ERROR_INVALID_HANDLE;
2535 }
2536
2537 if (lpService->dwStartType == SERVICE_DISABLED)
2538 return ERROR_SERVICE_DISABLED;
2539
2540 if (lpService->bDeleted)
2541 return ERROR_SERVICE_MARKED_FOR_DELETE;
2542
2543 /* FIXME: Convert argument vector to Unicode */
2544
2545 /* Start the service */
2546 dwError = ScmStartService(lpService, 0, NULL);
2547
2548 /* FIXME: Free argument vector */
2549
2550 return dwError;
2551 }
2552
2553
2554 /* Function 32 */
2555 DWORD RGetServiceDisplayNameA(
2556 handle_t BindingHandle,
2557 SC_RPC_HANDLE hSCManager,
2558 LPSTR lpServiceName,
2559 LPSTR lpDisplayName,
2560 LPBOUNDED_DWORD_4K lpcchBuffer)
2561 {
2562 UNIMPLEMENTED;
2563 return ERROR_CALL_NOT_IMPLEMENTED;
2564 }
2565
2566
2567 /* Function 33 */
2568 DWORD RGetServiceKeyNameA(
2569 handle_t BindingHandle,
2570 SC_RPC_HANDLE hSCManager,
2571 LPSTR lpDisplayName,
2572 LPSTR lpKeyName,
2573 LPBOUNDED_DWORD_4K lpcchBuffer)
2574 {
2575 UNIMPLEMENTED;
2576 return ERROR_CALL_NOT_IMPLEMENTED;
2577 }
2578
2579
2580 /* Function 34 */
2581 DWORD RGetCurrentGroupStateW(
2582 handle_t BindingHandle)
2583 {
2584 UNIMPLEMENTED;
2585 return ERROR_CALL_NOT_IMPLEMENTED;
2586 }
2587
2588
2589 /* Function 35 */
2590 DWORD REnumServiceGroupW(
2591 handle_t BindingHandle,
2592 SC_RPC_HANDLE hSCManager,
2593 DWORD dwServiceType,
2594 DWORD dwServiceState,
2595 LPBYTE lpBuffer,
2596 DWORD cbBufSize,
2597 LPBOUNDED_DWORD_256K pcbBytesNeeded,
2598 LPBOUNDED_DWORD_256K lpServicesReturned,
2599 LPBOUNDED_DWORD_256K lpResumeIndex,
2600 LPCWSTR pszGroupName)
2601 {
2602 UNIMPLEMENTED;
2603 return ERROR_CALL_NOT_IMPLEMENTED;
2604 }
2605
2606
2607 /* Function 36 */
2608 DWORD RChangeServiceConfig2A(
2609 handle_t BindingHandle,
2610 SC_RPC_HANDLE hService,
2611 SC_RPC_CONFIG_INFOA Info)
2612 {
2613 UNIMPLEMENTED;
2614 return ERROR_CALL_NOT_IMPLEMENTED;
2615 }
2616
2617
2618 /* Function 37 */
2619 DWORD RChangeServiceConfig2W(
2620 handle_t BindingHandle,
2621 SC_RPC_HANDLE hService,
2622 SC_RPC_CONFIG_INFOW Info)
2623 {
2624 DWORD dwError = ERROR_SUCCESS;
2625 PSERVICE_HANDLE hSvc;
2626 PSERVICE lpService = NULL;
2627 HKEY hServiceKey = NULL;
2628
2629 DPRINT("RChangeServiceConfig2W() called\n");
2630 DPRINT("dwInfoLevel = %lu\n", Info.dwInfoLevel);
2631
2632 if (ScmShutdown)
2633 return ERROR_SHUTDOWN_IN_PROGRESS;
2634
2635 hSvc = (PSERVICE_HANDLE)hService;
2636 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2637 {
2638 DPRINT1("Invalid handle tag!\n");
2639 return ERROR_INVALID_HANDLE;
2640 }
2641
2642 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2643 SERVICE_CHANGE_CONFIG))
2644 {
2645 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2646 return ERROR_ACCESS_DENIED;
2647 }
2648
2649 lpService = hSvc->ServiceEntry;
2650 if (lpService == NULL)
2651 {
2652 DPRINT1("lpService == NULL!\n");
2653 return ERROR_INVALID_HANDLE;
2654 }
2655
2656 /* FIXME: Lock database exclusively */
2657
2658 if (lpService->bDeleted)
2659 {
2660 /* FIXME: Unlock database */
2661 DPRINT1("The service has already been marked for delete!\n");
2662 return ERROR_SERVICE_MARKED_FOR_DELETE;
2663 }
2664
2665 /* Open the service key */
2666 dwError = ScmOpenServiceKey(lpService->szServiceName,
2667 KEY_SET_VALUE,
2668 &hServiceKey);
2669 if (dwError != ERROR_SUCCESS)
2670 goto done;
2671
2672 if (Info.dwInfoLevel & SERVICE_CONFIG_DESCRIPTION)
2673 {
2674 LPSERVICE_DESCRIPTIONW lpServiceDescription;
2675
2676 lpServiceDescription = (LPSERVICE_DESCRIPTIONW)&Info;
2677 lpServiceDescription->lpDescription = (LPWSTR)(&Info + sizeof(LPSERVICE_DESCRIPTIONW));
2678
2679 if (lpServiceDescription != NULL &&
2680 lpServiceDescription->lpDescription != NULL)
2681 {
2682 RegSetValueExW(hServiceKey,
2683 L"Description",
2684 0,
2685 REG_SZ,
2686 (LPBYTE)lpServiceDescription->lpDescription,
2687 (wcslen(lpServiceDescription->lpDescription) + 1) * sizeof(WCHAR));
2688
2689 if (dwError != ERROR_SUCCESS)
2690 goto done;
2691 }
2692 }
2693 else if (Info.dwInfoLevel & SERVICE_CONFIG_FAILURE_ACTIONS)
2694 {
2695 UNIMPLEMENTED;
2696 dwError = ERROR_CALL_NOT_IMPLEMENTED;
2697 goto done;
2698 }
2699
2700 done:
2701 /* FIXME: Unlock database */
2702 if (hServiceKey != NULL)
2703 RegCloseKey(hServiceKey);
2704
2705 DPRINT("RChangeServiceConfig2W() done (Error %lu)\n", dwError);
2706
2707 return dwError;
2708 }
2709
2710
2711 /* Function 38 */
2712 DWORD RQueryServiceConfig2A(
2713 handle_t BindingHandle,
2714 SC_RPC_HANDLE hService,
2715 DWORD dwInfoLevel,
2716 LPBYTE lpBuffer,
2717 DWORD cbBufSize,
2718 LPBOUNDED_DWORD_8K pcbBytesNeeded)
2719 {
2720 UNIMPLEMENTED;
2721 return ERROR_CALL_NOT_IMPLEMENTED;
2722 }
2723
2724
2725 /* Function 39 */
2726 DWORD RQueryServiceConfig2W(
2727 handle_t BindingHandle,
2728 SC_RPC_HANDLE hService,
2729 DWORD dwInfoLevel,
2730 LPBYTE lpBuffer,
2731 DWORD cbBufSize,
2732 LPBOUNDED_DWORD_8K pcbBytesNeeded)
2733 {
2734 DWORD dwError = ERROR_SUCCESS;
2735 PSERVICE_HANDLE hSvc;
2736 PSERVICE lpService = NULL;
2737 HKEY hServiceKey = NULL;
2738 DWORD dwRequiredSize;
2739 LPWSTR lpDescription = NULL;
2740
2741 DPRINT("RQueryServiceConfig2W() called\n");
2742
2743 if (ScmShutdown)
2744 return ERROR_SHUTDOWN_IN_PROGRESS;
2745
2746 hSvc = (PSERVICE_HANDLE)hService;
2747 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2748 {
2749 DPRINT1("Invalid handle tag!\n");
2750 return ERROR_INVALID_HANDLE;
2751 }
2752
2753 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2754 SERVICE_QUERY_CONFIG))
2755 {
2756 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2757 return ERROR_ACCESS_DENIED;
2758 }
2759
2760 lpService = hSvc->ServiceEntry;
2761 if (lpService == NULL)
2762 {
2763 DPRINT1("lpService == NULL!\n");
2764 return ERROR_INVALID_HANDLE;
2765 }
2766
2767 /* FIXME: Lock the service database shared */
2768
2769 dwError = ScmOpenServiceKey(lpService->lpServiceName,
2770 KEY_READ,
2771 &hServiceKey);
2772 if (dwError != ERROR_SUCCESS)
2773 goto done;
2774
2775 if (dwInfoLevel & SERVICE_CONFIG_DESCRIPTION)
2776 {
2777 LPSERVICE_DESCRIPTIONW lpServiceDescription = (LPSERVICE_DESCRIPTIONW)lpBuffer;
2778 LPWSTR lpStr;
2779
2780 dwError = ScmReadString(hServiceKey,
2781 L"Description",
2782 &lpDescription);
2783 if (dwError != ERROR_SUCCESS)
2784 goto done;
2785
2786 dwRequiredSize = sizeof(SERVICE_DESCRIPTIONW) + ((wcslen(lpDescription) + 1) * sizeof(WCHAR));
2787
2788 if (cbBufSize < dwRequiredSize)
2789 {
2790 *pcbBytesNeeded = dwRequiredSize;
2791 dwError = ERROR_INSUFFICIENT_BUFFER;
2792 goto done;
2793 }
2794 else
2795 {
2796 lpStr = (LPWSTR)(lpServiceDescription + 1);
2797 wcscpy(lpStr, lpDescription);
2798 lpServiceDescription->lpDescription = (LPWSTR)((ULONG_PTR)lpStr - (ULONG_PTR)lpServiceDescription);
2799 }
2800 }
2801 else if (dwInfoLevel & SERVICE_CONFIG_FAILURE_ACTIONS)
2802 {
2803 UNIMPLEMENTED;
2804 dwError = ERROR_CALL_NOT_IMPLEMENTED;
2805 goto done;
2806 }
2807
2808 done:
2809 if (lpDescription != NULL)
2810 HeapFree(GetProcessHeap(), 0, lpDescription);
2811
2812 if (hServiceKey != NULL)
2813 RegCloseKey(hServiceKey);
2814
2815 /* FIXME: Unlock database */
2816
2817 DPRINT("RQueryServiceConfig2W() done (Error %lu)\n", dwError);
2818
2819 return dwError;
2820 }
2821
2822
2823 /* Function 40 */
2824 DWORD RQueryServiceStatusEx(
2825 handle_t BindingHandle,
2826 SC_RPC_HANDLE hService,
2827 SC_STATUS_TYPE InfoLevel,
2828 LPBYTE lpBuffer,
2829 DWORD cbBufSize,
2830 LPBOUNDED_DWORD_8K pcbBytesNeeded)
2831 {
2832 LPSERVICE_STATUS_PROCESS lpStatus;
2833 PSERVICE_HANDLE hSvc;
2834 PSERVICE lpService;
2835
2836 DPRINT("RQueryServiceStatusEx() called\n");
2837
2838 if (ScmShutdown)
2839 return ERROR_SHUTDOWN_IN_PROGRESS;
2840
2841 if (InfoLevel != SC_STATUS_PROCESS_INFO)
2842 return ERROR_INVALID_LEVEL;
2843
2844 *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
2845
2846 if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
2847 return ERROR_INSUFFICIENT_BUFFER;
2848
2849 hSvc = (PSERVICE_HANDLE)hService;
2850 if (!hSvc || hSvc->Handle.Tag != SERVICE_TAG)
2851 {
2852 DPRINT1("Invalid handle tag!\n");
2853 return ERROR_INVALID_HANDLE;
2854 }
2855
2856 if (!RtlAreAllAccessesGranted(hSvc->Handle.DesiredAccess,
2857 SERVICE_QUERY_STATUS))
2858 {
2859 DPRINT1("Insufficient access rights! 0x%lx\n", hSvc->Handle.DesiredAccess);
2860 return ERROR_ACCESS_DENIED;
2861 }
2862
2863 lpService = hSvc->ServiceEntry;
2864 if (lpService == NULL)
2865 {
2866 DPRINT1("lpService == NULL!\n");
2867 return ERROR_INVALID_HANDLE;
2868 }
2869
2870 lpStatus = (LPSERVICE_STATUS_PROCESS)lpBuffer;
2871
2872 /* Return service status information */
2873 RtlCopyMemory(lpStatus,
2874 &lpService->Status,
2875 sizeof(SERVICE_STATUS));
2876
2877 lpStatus->dwProcessId = lpService->ProcessId; /* FIXME */
2878 lpStatus->dwServiceFlags = 0; /* FIXME */
2879
2880 return ERROR_SUCCESS;
2881 }
2882
2883
2884 /* Function 41 */
2885 DWORD REnumServicesStatusExA(
2886 handle_t BindingHandle,
2887 SC_RPC_HANDLE hSCManager,
2888 SC_ENUM_TYPE InfoLevel,
2889 DWORD dwServiceType,
2890 DWORD dwServiceState,
2891 LPBYTE lpBuffer,
2892 DWORD cbBufSize,
2893 LPBOUNDED_DWORD_256K pcbBytesNeeded,
2894 LPBOUNDED_DWORD_256K lpServicesReturned,
2895 LPBOUNDED_DWORD_256K lpResumeIndex,
2896 LPCSTR pszGroupName)
2897 {
2898 UNIMPLEMENTED;
2899 *pcbBytesNeeded = 0;
2900 *lpServicesReturned = 0;
2901 return ERROR_CALL_NOT_IMPLEMENTED;
2902 }
2903
2904
2905 /* Function 42 */
2906 DWORD REnumServicesStatusExW(
2907 handle_t BindingHandle,
2908 SC_RPC_HANDLE hSCManager,
2909 SC_ENUM_TYPE InfoLevel,
2910 DWORD dwServiceType,
2911 DWORD dwServiceState,
2912 LPBYTE lpBuffer,
2913 DWORD cbBufSize,
2914 LPBOUNDED_DWORD_256K pcbBytesNeeded,
2915 LPBOUNDED_DWORD_256K lpServicesReturned,
2916 LPBOUNDED_DWORD_256K lpResumeIndex,
2917 LPCWSTR pszGroupName)
2918 {
2919 PMANAGER_HANDLE hManager;
2920 PSERVICE lpService;
2921 DWORD dwError = ERROR_SUCCESS;
2922 PLIST_ENTRY ServiceEntry;
2923 PSERVICE CurrentService;
2924 DWORD dwState;
2925 DWORD dwRequiredSize;
2926 DWORD dwServiceCount;
2927 DWORD dwSize;
2928 DWORD dwLastResumeCount;
2929 LPENUM_SERVICE_STATUS_PROCESSW lpStatusPtr;
2930 LPWSTR lpStringPtr;
2931
2932 DPRINT("REnumServicesStatusExW() called\n");
2933
2934 if (ScmShutdown)
2935 return ERROR_SHUTDOWN_IN_PROGRESS;
2936
2937 if (InfoLevel != SC_ENUM_PROCESS_INFO)
2938 return ERROR_INVALID_LEVEL;
2939
2940 hManager = (PMANAGER_HANDLE)hSCManager;
2941 if (!hManager || hManager->Handle.Tag != MANAGER_TAG)
2942 {
2943 DPRINT1("Invalid manager handle!\n");
2944 return ERROR_INVALID_HANDLE;
2945 }
2946
2947 /* Check access rights */
2948 if (!RtlAreAllAccessesGranted(hManager->Handle.DesiredAccess,
2949 SC_MANAGER_ENUMERATE_SERVICE))
2950 {
2951 DPRINT1("Insufficient access rights! 0x%lx\n",
2952 hManager->Handle.DesiredAccess);
2953 return ERROR_ACCESS_DENIED;
2954 }
2955
2956 *pcbBytesNeeded = 0;
2957 *lpServicesReturned = 0;
2958
2959 dwLastResumeCount = *lpResumeIndex;
2960
2961 /* Lock the service list shared */
2962
2963 lpService = ScmGetServiceEntryByResumeCount(dwLastResumeCount);
2964 if (lpService == NULL)
2965 {
2966 dwError = ERROR_SUCCESS;
2967 goto Done;
2968 }
2969
2970 dwRequiredSize = 0;
2971 dwServiceCount = 0;
2972
2973 for (ServiceEntry = &lpService->ServiceListEntry;
2974 ServiceEntry != &ServiceListHead;
2975 ServiceEntry = ServiceEntry->Flink)
2976 {
2977 CurrentService = CONTAINING_RECORD(ServiceEntry,
2978 SERVICE,
2979 ServiceListEntry);
2980
2981 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
2982 continue;
2983
2984 dwState = SERVICE_ACTIVE;
2985 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
2986 dwState = SERVICE_INACTIVE;
2987
2988 if ((dwState & dwServiceState) == 0)
2989 continue;
2990
2991 if (pszGroupName)
2992 {
2993 if (*pszGroupName == 0)
2994 {
2995 if (CurrentService->lpGroup != NULL)
2996 continue;
2997 }
2998 else
2999 {
3000 if ((CurrentService->lpGroup == NULL) ||
3001 _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
3002 continue;
3003 }
3004 }
3005
3006 dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
3007 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
3008 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
3009
3010 if (dwRequiredSize + dwSize <= cbBufSize)
3011 {
3012 DPRINT("Service name: %S fit\n", CurrentService->lpServiceName);
3013 dwRequiredSize += dwSize;
3014 dwServiceCount++;
3015 dwLastResumeCount = CurrentService->dwResumeCount;
3016 }
3017 else
3018 {
3019 DPRINT("Service name: %S no fit\n", CurrentService->lpServiceName);
3020 break;
3021 }
3022
3023 }
3024
3025 DPRINT("dwRequiredSize: %lu\n", dwRequiredSize);
3026 DPRINT("dwServiceCount: %lu\n", dwServiceCount);
3027
3028 for (;
3029 ServiceEntry != &ServiceListHead;
3030 ServiceEntry = ServiceEntry->Flink)
3031 {
3032 CurrentService = CONTAINING_RECORD(ServiceEntry,
3033 SERVICE,
3034 ServiceListEntry);
3035
3036 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
3037 continue;
3038
3039 dwState = SERVICE_ACTIVE;
3040 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
3041 dwState = SERVICE_INACTIVE;
3042
3043 if ((dwState & dwServiceState) == 0)
3044 continue;
3045
3046 if (pszGroupName)
3047 {
3048 if (*pszGroupName == 0)
3049 {
3050 if (CurrentService->lpGroup != NULL)
3051 continue;
3052 }
3053 else
3054 {
3055 if ((CurrentService->lpGroup == NULL) ||
3056 _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
3057 continue;
3058 }
3059 }
3060
3061 dwRequiredSize += (sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
3062 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
3063 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR)));
3064
3065 dwError = ERROR_MORE_DATA;
3066 }
3067
3068 DPRINT("*pcbBytesNeeded: %lu\n", dwRequiredSize);
3069
3070 *lpResumeIndex = dwLastResumeCount;
3071 *lpServicesReturned = dwServiceCount;
3072 *pcbBytesNeeded = dwRequiredSize;
3073
3074 lpStatusPtr = (LPENUM_SERVICE_STATUS_PROCESSW)lpBuffer;
3075 lpStringPtr = (LPWSTR)((ULONG_PTR)lpBuffer +
3076 dwServiceCount * sizeof(ENUM_SERVICE_STATUS_PROCESSW));
3077
3078 dwRequiredSize = 0;
3079 for (ServiceEntry = &lpService->ServiceListEntry;
3080 ServiceEntry != &ServiceListHead;
3081 ServiceEntry = ServiceEntry->Flink)
3082 {
3083 CurrentService = CONTAINING_RECORD(ServiceEntry,
3084 SERVICE,
3085 ServiceListEntry);
3086
3087 if ((CurrentService->Status.dwServiceType & dwServiceType) == 0)
3088 continue;
3089
3090 dwState = SERVICE_ACTIVE;
3091 if (CurrentService->Status.dwCurrentState == SERVICE_STOPPED)
3092 dwState = SERVICE_INACTIVE;
3093
3094 if ((dwState & dwServiceState) == 0)
3095 continue;
3096
3097 if (pszGroupName)
3098 {
3099 if (*pszGroupName == 0)
3100 {
3101 if (CurrentService->lpGroup != NULL)
3102 continue;
3103 }
3104 else
3105 {
3106 if ((CurrentService->lpGroup == NULL) ||
3107 _wcsicmp(pszGroupName, CurrentService->lpGroup->lpGroupName))
3108 continue;
3109 }
3110 }
3111
3112 dwSize = sizeof(ENUM_SERVICE_STATUS_PROCESSW) +
3113 ((wcslen(CurrentService->lpServiceName) + 1) * sizeof(WCHAR)) +
3114 ((wcslen(CurrentService->lpDisplayName) + 1) * sizeof(WCHAR));
3115
3116 if (dwRequiredSize + dwSize <= cbBufSize)
3117 {
3118 /* Copy the service name */
3119 wcscpy(lpStringPtr,
3120 CurrentService->lpServiceName);
3121 lpStatusPtr->lpServiceName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
3122 lpStringPtr += (wcslen(CurrentService->lpServiceName) + 1);
3123
3124 /* Copy the display name */
3125 wcscpy(lpStringPtr,
3126 CurrentService->lpDisplayName);
3127 lpStatusPtr->lpDisplayName = (LPWSTR)((ULONG_PTR)lpStringPtr - (ULONG_PTR)lpBuffer);
3128 lpStringPtr += (wcslen(CurrentService->lpDisplayName) + 1);
3129
3130 /* Copy the status information */
3131 memcpy(&lpStatusPtr->ServiceStatusProcess,
3132 &CurrentService->Status,
3133 sizeof(SERVICE_STATUS));
3134 lpStatusPtr->ServiceStatusProcess.dwProcessId = CurrentService->ProcessId; /* FIXME */
3135 lpStatusPtr->ServiceStatusProcess.dwServiceFlags = 0; /* FIXME */
3136
3137 lpStatusPtr++;
3138 dwRequiredSize += dwSize;
3139 }
3140 else
3141 {
3142 break;
3143 }
3144
3145 }
3146
3147 Done:;
3148 /* Unlock the service list */
3149
3150 DPRINT("REnumServicesStatusExW() done (Error %lu)\n", dwError);
3151
3152 return dwError;
3153 }
3154
3155
3156 /* Function 43 */
3157 DWORD RSendTSMessage(
3158 handle_t BindingHandle)
3159 {
3160 UNIMPLEMENTED;
3161 return ERROR_CALL_NOT_IMPLEMENTED;
3162 }
3163
3164
3165 /* Function 44 */
3166 DWORD RCreateServiceWOW64A(
3167 handle_t BindingHandle,
3168 LPSTR lpServiceName,
3169 LPSTR lpDisplayName,
3170 DWORD dwDesiredAccess,
3171 DWORD dwServiceType,
3172 DWORD dwStartType,
3173 DWORD dwErrorControl,
3174 LPSTR lpBinaryPathName,
3175 LPSTR lpLoadOrderGroup,
3176 LPDWORD lpdwTagId,
3177 LPBYTE lpDependencies,
3178 DWORD dwDependSize,
3179 LPSTR lpServiceStartName,
3180 LPBYTE lpPassword,
3181 DWORD dwPwSize,
3182 LPSC_RPC_HANDLE lpServiceHandle)
3183 {
3184 UNIMPLEMENTED;
3185 return ERROR_CALL_NOT_IMPLEMENTED;
3186 }
3187
3188
3189 /* Function 45 */
3190 DWORD RCreateServiceWOW64W(
3191 handle_t BindingHandle,
3192 LPWSTR lpServiceName,
3193 LPWSTR lpDisplayName,
3194 DWORD dwDesiredAccess,
3195 DWORD dwServiceType,
3196 DWORD dwStartType,
3197 DWORD dwErrorControl,
3198 LPWSTR lpBinaryPathName,
3199 LPWSTR lpLoadOrderGroup,
3200 LPDWORD lpdwTagId,
3201 LPBYTE lpDependencies,
3202 DWORD dwDependSize,
3203 LPWSTR lpServiceStartName,
3204 LPBYTE lpPassword,
3205 DWORD dwPwSize,
3206 LPSC_RPC_HANDLE lpServiceHandle)
3207 {
3208 UNIMPLEMENTED;
3209 return ERROR_CALL_NOT_IMPLEMENTED;
3210 }
3211
3212
3213 /* Function 46 */
3214 DWORD RQueryServiceTagInfo(
3215 handle_t BindingHandle)
3216 {
3217 UNIMPLEMENTED;
3218 return ERROR_CALL_NOT_IMPLEMENTED;
3219 }
3220
3221
3222 /* Function 47 */
3223 DWORD RNotifyServiceStatusChange(
3224 handle_t BindingHandle,
3225 SC_RPC_HANDLE hService,
3226 SC_RPC_NOTIFY_PARAMS NotifyParams,
3227 GUID *pClientProcessGuid,
3228 GUID *pSCMProcessGuid,
3229 PBOOL pfCreateRemoteQueue,
3230 LPSC_NOTIFY_RPC_HANDLE phNotify)
3231 {
3232 UNIMPLEMENTED;
3233 return ERROR_CALL_NOT_IMPLEMENTED;
3234 }
3235
3236
3237 /* Function 48 */
3238 DWORD RGetNotifyResults(
3239 handle_t BindingHandle,
3240 SC_NOTIFY_RPC_HANDLE hNotify,
3241 PSC_RPC_NOTIFY_PARAMS_LIST *ppNotifyParams)
3242 {
3243 UNIMPLEMENTED;
3244 return ERROR_CALL_NOT_IMPLEMENTED;
3245 }
3246
3247
3248 /* Function 49 */
3249 DWORD RCloseNotifyHandle(
3250 handle_t BindingHandle,
3251 LPSC_NOTIFY_RPC_HANDLE phNotify,
3252 PBOOL pfApcFired)
3253 {
3254 UNIMPLEMENTED;
3255 return ERROR_CALL_NOT_IMPLEMENTED;
3256 }
3257
3258
3259 /* Function 50 */
3260 DWORD RControlServiceExA(
3261 handle_t BindingHandle,
3262 SC_RPC_HANDLE hService,
3263 DWORD dwControl,
3264 DWORD dwInfoLevel)
3265 {
3266 UNIMPLEMENTED;
3267 return ERROR_CALL_NOT_IMPLEMENTED;
3268 }
3269
3270
3271 /* Function 51 */
3272 DWORD RControlServiceExW(
3273 handle_t BindingHandle,
3274 SC_RPC_HANDLE hService,
3275 DWORD dwControl,
3276 DWORD dwInfoLevel)
3277 {
3278 UNIMPLEMENTED;
3279 return ERROR_CALL_NOT_IMPLEMENTED;
3280 }
3281
3282
3283 /* Function 52 */
3284 DWORD RSendPnPMessage(
3285 handle_t BindingHandle)
3286 {
3287 UNIMPLEMENTED;
3288 return ERROR_CALL_NOT_IMPLEMENTED;
3289 }
3290
3291
3292 /* Function 53 */
3293 DWORD RValidatePnPService(
3294 handle_t BindingHandle)
3295 {
3296 UNIMPLEMENTED;
3297 return ERROR_CALL_NOT_IMPLEMENTED;
3298 }
3299
3300
3301 /* Function 54 */
3302 DWORD ROpenServiceStatusHandle(
3303 handle_t BindingHandle)
3304 {
3305 UNIMPLEMENTED;
3306 return ERROR_CALL_NOT_IMPLEMENTED;
3307 }
3308
3309
3310 /* Function 55 */
3311 DWORD RFunction55(
3312 handle_t BindingHandle)
3313 {
3314 UNIMPLEMENTED;
3315 return ERROR_CALL_NOT_IMPLEMENTED;
3316 }
3317
3318
3319 void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
3320 {
3321 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
3322 }
3323
3324
3325 void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
3326 {
3327 HeapFree(GetProcessHeap(), 0, ptr);
3328 }
3329
3330
3331 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE hSCObject)
3332 {
3333 }
3334
3335
3336 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK Lock)
3337 {
3338 }
3339
3340
3341 void __RPC_USER SC_NOTIFY_RPC_HANDLE_rundown(SC_NOTIFY_RPC_HANDLE hNotify)
3342 {
3343 }
3344
3345
3346 /* EOF */