wininet: Don't use INTERNET_MAX_HOST_NAME_LENGTH in InternetGetSecurityInfoByURLW,.
[wine.git] / programs / services / rpc.c
blob3ebf378846b87fe8705bbfe4c83c219cf1495251
1 /*
2 * Services.exe - RPC functions
4 * Copyright 2007 Google (Mikolaj Zalewski)
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define WIN32_LEAN_AND_MEAN
22 #define NONAMELESSUNION
24 #include <stdarg.h>
25 #include <windows.h>
26 #include <winternl.h>
27 #include <winsvc.h>
28 #include <ntsecapi.h>
29 #include <rpc.h>
31 #include "wine/list.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
35 #include "services.h"
36 #include "svcctl.h"
38 extern HANDLE CDECL __wine_make_process_system(void);
40 WINE_DEFAULT_DEBUG_CHANNEL(service);
42 static const GENERIC_MAPPING g_scm_generic =
44 (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS),
45 (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG),
46 (STANDARD_RIGHTS_EXECUTE | SC_MANAGER_CONNECT | SC_MANAGER_LOCK),
47 SC_MANAGER_ALL_ACCESS
50 static const GENERIC_MAPPING g_svc_generic =
52 (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS),
53 (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG),
54 (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL),
55 SERVICE_ALL_ACCESS
58 typedef enum
60 SC_HTYPE_DONT_CARE = 0,
61 SC_HTYPE_MANAGER,
62 SC_HTYPE_SERVICE
63 } SC_HANDLE_TYPE;
65 struct sc_handle
67 SC_HANDLE_TYPE type;
68 DWORD access;
71 struct sc_manager_handle /* service control manager handle */
73 struct sc_handle hdr;
74 struct scmdatabase *db;
77 struct sc_service_handle /* service handle */
79 struct sc_handle hdr;
80 struct service_entry *service_entry;
83 struct sc_lock
85 struct scmdatabase *db;
88 static HANDLE timeout_queue_event;
89 static CRITICAL_SECTION timeout_queue_cs;
90 static CRITICAL_SECTION_DEBUG timeout_queue_cs_debug =
92 0, 0, &timeout_queue_cs,
93 { &timeout_queue_cs_debug.ProcessLocksList, &timeout_queue_cs_debug.ProcessLocksList },
94 0, 0, { (DWORD_PTR)(__FILE__ ": timeout_queue_cs") }
96 static CRITICAL_SECTION timeout_queue_cs = { &timeout_queue_cs_debug, -1, 0, 0, 0, 0 };
97 static struct list timeout_queue = LIST_INIT(timeout_queue);
98 struct timeout_queue_elem
100 struct list entry;
102 FILETIME time;
103 void (*func)(struct process_entry *);
104 struct process_entry *process;
107 static void run_after_timeout(void (*func)(struct process_entry *), struct process_entry *process, DWORD timeout)
109 struct timeout_queue_elem *elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct timeout_queue_elem));
110 ULARGE_INTEGER time;
112 if(!elem) {
113 func(process);
114 return;
117 elem->func = func;
118 elem->process = grab_process(process);
120 GetSystemTimeAsFileTime(&elem->time);
121 time.u.LowPart = elem->time.dwLowDateTime;
122 time.u.HighPart = elem->time.dwHighDateTime;
123 time.QuadPart += (ULONGLONG)timeout * 10000;
124 elem->time.dwLowDateTime = time.u.LowPart;
125 elem->time.dwHighDateTime = time.u.HighPart;
127 EnterCriticalSection(&timeout_queue_cs);
128 list_add_head(&timeout_queue, &elem->entry);
129 LeaveCriticalSection(&timeout_queue_cs);
131 SetEvent(timeout_queue_event);
134 static void free_service_strings(struct service_entry *old, struct service_entry *new)
136 QUERY_SERVICE_CONFIGW *old_cfg = &old->config;
137 QUERY_SERVICE_CONFIGW *new_cfg = &new->config;
139 if (old_cfg->lpBinaryPathName != new_cfg->lpBinaryPathName)
140 HeapFree(GetProcessHeap(), 0, old_cfg->lpBinaryPathName);
142 if (old_cfg->lpLoadOrderGroup != new_cfg->lpLoadOrderGroup)
143 HeapFree(GetProcessHeap(), 0, old_cfg->lpLoadOrderGroup);
145 if (old_cfg->lpServiceStartName != new_cfg->lpServiceStartName)
146 HeapFree(GetProcessHeap(), 0, old_cfg->lpServiceStartName);
148 if (old_cfg->lpDisplayName != new_cfg->lpDisplayName)
149 HeapFree(GetProcessHeap(), 0, old_cfg->lpDisplayName);
151 if (old->dependOnServices != new->dependOnServices)
152 HeapFree(GetProcessHeap(), 0, old->dependOnServices);
154 if (old->dependOnGroups != new->dependOnGroups)
155 HeapFree(GetProcessHeap(), 0, old->dependOnGroups);
158 /* Check if the given handle is of the required type and allows the requested access. */
159 static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
161 struct sc_handle *hdr = handle;
163 if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
165 WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
166 return ERROR_INVALID_HANDLE;
169 if ((needed_access & hdr->access) != needed_access)
171 WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
172 return ERROR_ACCESS_DENIED;
175 *out_hdr = hdr;
176 return ERROR_SUCCESS;
179 static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager_handle **manager)
181 struct sc_handle *hdr;
182 DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
183 if (err == ERROR_SUCCESS)
184 *manager = (struct sc_manager_handle *)hdr;
185 return err;
188 static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service_handle **service)
190 struct sc_handle *hdr;
191 DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
192 if (err == ERROR_SUCCESS)
193 *service = (struct sc_service_handle *)hdr;
194 return err;
197 DWORD __cdecl svcctl_OpenSCManagerW(
198 MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
199 LPCWSTR DatabaseName,
200 DWORD dwAccessMask,
201 SC_RPC_HANDLE *handle)
203 struct sc_manager_handle *manager;
205 WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
207 if (DatabaseName != NULL && DatabaseName[0])
209 if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0)
210 return ERROR_DATABASE_DOES_NOT_EXIST;
211 if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
212 return ERROR_INVALID_NAME;
215 if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager))))
216 return ERROR_NOT_ENOUGH_SERVER_MEMORY;
218 manager->hdr.type = SC_HTYPE_MANAGER;
220 if (dwAccessMask & MAXIMUM_ALLOWED)
221 dwAccessMask |= SC_MANAGER_ALL_ACCESS;
222 manager->hdr.access = dwAccessMask;
223 RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
224 manager->db = active_database;
225 *handle = &manager->hdr;
227 return ERROR_SUCCESS;
230 static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
232 struct sc_handle *hdr = handle;
233 switch (hdr->type)
235 case SC_HTYPE_MANAGER:
237 struct sc_manager_handle *manager = (struct sc_manager_handle *)hdr;
238 HeapFree(GetProcessHeap(), 0, manager);
239 break;
241 case SC_HTYPE_SERVICE:
243 struct sc_service_handle *service = (struct sc_service_handle *)hdr;
244 release_service(service->service_entry);
245 HeapFree(GetProcessHeap(), 0, service);
246 break;
248 default:
249 WINE_ERR("invalid handle type %d\n", hdr->type);
250 RpcRaiseException(ERROR_INVALID_HANDLE);
254 DWORD __cdecl svcctl_GetServiceDisplayNameW(
255 SC_RPC_HANDLE hSCManager,
256 LPCWSTR lpServiceName,
257 WCHAR *lpBuffer,
258 DWORD *cchBufSize)
260 struct sc_manager_handle *manager;
261 struct service_entry *entry;
262 DWORD err;
264 WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceName), *cchBufSize);
266 if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
267 return err;
269 scmdatabase_lock(manager->db);
271 entry = scmdatabase_find_service(manager->db, lpServiceName);
272 if (entry != NULL)
274 LPCWSTR name;
275 int len;
276 name = get_display_name(entry);
277 len = strlenW(name);
278 if (len <= *cchBufSize)
280 err = ERROR_SUCCESS;
281 memcpy(lpBuffer, name, (len + 1)*sizeof(*name));
283 else
284 err = ERROR_INSUFFICIENT_BUFFER;
285 *cchBufSize = len;
287 else
288 err = ERROR_SERVICE_DOES_NOT_EXIST;
290 scmdatabase_unlock(manager->db);
292 if (err != ERROR_SUCCESS)
293 lpBuffer[0] = 0;
295 return err;
298 DWORD __cdecl svcctl_GetServiceKeyNameW(
299 SC_RPC_HANDLE hSCManager,
300 LPCWSTR lpServiceDisplayName,
301 WCHAR *lpBuffer,
302 DWORD *cchBufSize)
304 struct service_entry *entry;
305 struct sc_manager_handle *manager;
306 DWORD err;
308 WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), *cchBufSize);
310 if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
311 return err;
313 scmdatabase_lock(manager->db);
315 entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
316 if (entry != NULL)
318 int len;
319 len = strlenW(entry->name);
320 if (len <= *cchBufSize)
322 err = ERROR_SUCCESS;
323 memcpy(lpBuffer, entry->name, (len + 1)*sizeof(*entry->name));
325 else
326 err = ERROR_INSUFFICIENT_BUFFER;
327 *cchBufSize = len;
329 else
330 err = ERROR_SERVICE_DOES_NOT_EXIST;
332 scmdatabase_unlock(manager->db);
334 if (err != ERROR_SUCCESS)
335 lpBuffer[0] = 0;
337 return err;
340 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
342 struct sc_service_handle *service;
344 if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
346 release_service(entry);
347 return ERROR_NOT_ENOUGH_SERVER_MEMORY;
350 if (dwDesiredAccess & MAXIMUM_ALLOWED)
351 dwDesiredAccess |= SERVICE_ALL_ACCESS;
353 service->hdr.type = SC_HTYPE_SERVICE;
354 service->hdr.access = dwDesiredAccess;
355 RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
356 service->service_entry = entry;
358 *phService = &service->hdr;
359 return ERROR_SUCCESS;
362 DWORD __cdecl svcctl_OpenServiceW(
363 SC_RPC_HANDLE hSCManager,
364 LPCWSTR lpServiceName,
365 DWORD dwDesiredAccess,
366 SC_RPC_HANDLE *phService)
368 struct sc_manager_handle *manager;
369 struct service_entry *entry;
370 DWORD err;
372 WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
374 if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
375 return err;
376 if (!validate_service_name(lpServiceName))
377 return ERROR_INVALID_NAME;
379 scmdatabase_lock(manager->db);
380 entry = scmdatabase_find_service(manager->db, lpServiceName);
381 if (entry != NULL)
382 InterlockedIncrement(&entry->ref_count);
383 scmdatabase_unlock(manager->db);
385 if (entry == NULL)
386 return ERROR_SERVICE_DOES_NOT_EXIST;
388 return create_handle_for_service(entry, dwDesiredAccess, phService);
391 static DWORD parse_dependencies(const WCHAR *dependencies, struct service_entry *entry)
393 WCHAR *services = NULL, *groups, *s;
394 DWORD len, len_services = 0, len_groups = 0;
395 const WCHAR *ptr = dependencies;
397 if (!dependencies || !dependencies[0])
399 entry->dependOnServices = NULL;
400 entry->dependOnGroups = NULL;
401 return ERROR_SUCCESS;
404 while (*ptr)
406 len = strlenW(ptr) + 1;
407 if (ptr[0] == '+' && ptr[1])
408 len_groups += len - 1;
409 else
410 len_services += len;
411 ptr += len;
413 if (!len_services) entry->dependOnServices = NULL;
414 else
416 services = HeapAlloc(GetProcessHeap(), 0, (len_services + 1) * sizeof(WCHAR));
417 if (!services)
418 return ERROR_OUTOFMEMORY;
420 s = services;
421 ptr = dependencies;
422 while (*ptr)
424 len = strlenW(ptr) + 1;
425 if (*ptr != '+')
427 strcpyW(s, ptr);
428 s += len;
430 ptr += len;
432 *s = 0;
433 entry->dependOnServices = services;
435 if (!len_groups) entry->dependOnGroups = NULL;
436 else
438 groups = HeapAlloc(GetProcessHeap(), 0, (len_groups + 1) * sizeof(WCHAR));
439 if (!groups)
441 HeapFree(GetProcessHeap(), 0, services);
442 return ERROR_OUTOFMEMORY;
444 s = groups;
445 ptr = dependencies;
446 while (*ptr)
448 len = strlenW(ptr) + 1;
449 if (ptr[0] == '+' && ptr[1])
451 strcpyW(s, ptr + 1);
452 s += len - 1;
454 ptr += len;
456 *s = 0;
457 entry->dependOnGroups = groups;
460 return ERROR_SUCCESS;
463 static DWORD create_serviceW(
464 SC_RPC_HANDLE hSCManager,
465 LPCWSTR lpServiceName,
466 LPCWSTR lpDisplayName,
467 DWORD dwDesiredAccess,
468 DWORD dwServiceType,
469 DWORD dwStartType,
470 DWORD dwErrorControl,
471 LPCWSTR lpBinaryPathName,
472 LPCWSTR lpLoadOrderGroup,
473 DWORD *lpdwTagId,
474 const BYTE *lpDependencies,
475 DWORD dwDependenciesSize,
476 LPCWSTR lpServiceStartName,
477 const BYTE *lpPassword,
478 DWORD dwPasswordSize,
479 SC_RPC_HANDLE *phService,
480 BOOL is_wow64)
482 struct service_entry *entry, *found;
483 struct sc_manager_handle *manager;
484 DWORD err;
486 WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
488 if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
489 return err;
491 if (!validate_service_name(lpServiceName))
492 return ERROR_INVALID_NAME;
493 if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
494 return ERROR_INVALID_PARAMETER;
496 if (lpPassword)
497 WINE_FIXME("Don't know how to add a password\n"); /* I always get ERROR_GEN_FAILURE */
499 err = service_create(lpServiceName, &entry);
500 if (err != ERROR_SUCCESS)
501 return err;
503 err = parse_dependencies((LPCWSTR)lpDependencies, entry);
504 if (err != ERROR_SUCCESS) {
505 free_service_entry(entry);
506 return err;
509 entry->is_wow64 = is_wow64;
510 entry->config.dwServiceType = entry->status.dwServiceType = dwServiceType;
511 entry->config.dwStartType = dwStartType;
512 entry->config.dwErrorControl = dwErrorControl;
513 entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
514 entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
515 entry->config.lpServiceStartName = strdupW(lpServiceStartName);
516 entry->config.lpDisplayName = strdupW(lpDisplayName);
518 if (lpdwTagId) /* TODO: In most situations a non-NULL TagId will generate an ERROR_INVALID_PARAMETER. */
519 entry->config.dwTagId = *lpdwTagId;
520 else
521 entry->config.dwTagId = 0;
523 /* other fields NULL*/
525 if (!validate_service_config(entry))
527 WINE_ERR("Invalid data while trying to create service\n");
528 free_service_entry(entry);
529 return ERROR_INVALID_PARAMETER;
532 scmdatabase_lock(manager->db);
534 if ((found = scmdatabase_find_service(manager->db, lpServiceName)))
536 err = is_marked_for_delete(found) ? ERROR_SERVICE_MARKED_FOR_DELETE : ERROR_SERVICE_EXISTS;
537 scmdatabase_unlock(manager->db);
538 free_service_entry(entry);
539 return err;
542 if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
544 scmdatabase_unlock(manager->db);
545 free_service_entry(entry);
546 return ERROR_DUPLICATE_SERVICE_NAME;
549 err = scmdatabase_add_service(manager->db, entry);
550 if (err != ERROR_SUCCESS)
552 scmdatabase_unlock(manager->db);
553 free_service_entry(entry);
554 return err;
556 scmdatabase_unlock(manager->db);
558 return create_handle_for_service(entry, dwDesiredAccess, phService);
561 DWORD __cdecl svcctl_CreateServiceW(
562 SC_RPC_HANDLE hSCManager,
563 LPCWSTR lpServiceName,
564 LPCWSTR lpDisplayName,
565 DWORD dwDesiredAccess,
566 DWORD dwServiceType,
567 DWORD dwStartType,
568 DWORD dwErrorControl,
569 LPCWSTR lpBinaryPathName,
570 LPCWSTR lpLoadOrderGroup,
571 DWORD *lpdwTagId,
572 const BYTE *lpDependencies,
573 DWORD dwDependenciesSize,
574 LPCWSTR lpServiceStartName,
575 const BYTE *lpPassword,
576 DWORD dwPasswordSize,
577 SC_RPC_HANDLE *phService)
579 WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
580 return create_serviceW(hSCManager, lpServiceName, lpDisplayName, dwDesiredAccess, dwServiceType, dwStartType,
581 dwErrorControl, lpBinaryPathName, lpLoadOrderGroup, lpdwTagId, lpDependencies, dwDependenciesSize, lpServiceStartName,
582 lpPassword, dwPasswordSize, phService, FALSE);
585 DWORD __cdecl svcctl_DeleteService(
586 SC_RPC_HANDLE hService)
588 struct sc_service_handle *service;
589 DWORD err;
591 if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
592 return err;
594 service_lock(service->service_entry);
596 if (!is_marked_for_delete(service->service_entry))
597 err = mark_for_delete(service->service_entry);
598 else
599 err = ERROR_SERVICE_MARKED_FOR_DELETE;
601 service_unlock(service->service_entry);
603 return err;
606 DWORD __cdecl svcctl_QueryServiceConfigW(
607 SC_RPC_HANDLE hService,
608 QUERY_SERVICE_CONFIGW *config,
609 DWORD buf_size,
610 DWORD *needed_size)
612 struct sc_service_handle *service;
613 DWORD err;
615 WINE_TRACE("(%p)\n", config);
617 if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
618 return err;
620 service_lock(service->service_entry);
621 config->dwServiceType = service->service_entry->config.dwServiceType;
622 config->dwStartType = service->service_entry->config.dwStartType;
623 config->dwErrorControl = service->service_entry->config.dwErrorControl;
624 config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
625 config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
626 config->dwTagId = service->service_entry->config.dwTagId;
627 config->lpDependencies = NULL; /* TODO */
628 config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
629 config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
630 service_unlock(service->service_entry);
632 return ERROR_SUCCESS;
635 DWORD __cdecl svcctl_ChangeServiceConfigW(
636 SC_RPC_HANDLE hService,
637 DWORD dwServiceType,
638 DWORD dwStartType,
639 DWORD dwErrorControl,
640 LPCWSTR lpBinaryPathName,
641 LPCWSTR lpLoadOrderGroup,
642 DWORD *lpdwTagId,
643 const BYTE *lpDependencies,
644 DWORD dwDependenciesSize,
645 LPCWSTR lpServiceStartName,
646 const BYTE *lpPassword,
647 DWORD dwPasswordSize,
648 LPCWSTR lpDisplayName)
650 struct service_entry new_entry, *entry;
651 struct sc_service_handle *service;
652 DWORD err;
654 WINE_TRACE("\n");
656 if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
657 return err;
659 if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
660 return ERROR_INVALID_PARAMETER;
662 /* first check if the new configuration is correct */
663 service_lock(service->service_entry);
665 if (is_marked_for_delete(service->service_entry))
667 service_unlock(service->service_entry);
668 return ERROR_SERVICE_MARKED_FOR_DELETE;
671 if (lpDisplayName != NULL &&
672 (entry = scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName)) &&
673 (entry != service->service_entry))
675 service_unlock(service->service_entry);
676 return ERROR_DUPLICATE_SERVICE_NAME;
679 new_entry = *service->service_entry;
681 if (dwServiceType != SERVICE_NO_CHANGE)
682 new_entry.config.dwServiceType = dwServiceType;
684 if (dwStartType != SERVICE_NO_CHANGE)
685 new_entry.config.dwStartType = dwStartType;
687 if (dwErrorControl != SERVICE_NO_CHANGE)
688 new_entry.config.dwErrorControl = dwErrorControl;
690 if (lpBinaryPathName != NULL)
691 new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
693 if (lpLoadOrderGroup != NULL)
694 new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
696 if (lpdwTagId != NULL)
697 WINE_FIXME("Changing tag id not supported\n");
699 if (lpServiceStartName != NULL)
700 new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
702 if (lpPassword != NULL)
703 WINE_FIXME("Setting password not supported\n");
705 if (lpDisplayName != NULL)
706 new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
708 err = parse_dependencies((LPCWSTR)lpDependencies, &new_entry);
709 if (err != ERROR_SUCCESS)
711 service_unlock(service->service_entry);
712 return err;
715 if (!validate_service_config(&new_entry))
717 WINE_ERR("The configuration after the change wouldn't be valid\n");
718 service_unlock(service->service_entry);
719 return ERROR_INVALID_PARAMETER;
722 /* configuration OK. The strings needs to be duplicated */
723 if (lpBinaryPathName != NULL)
724 new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
726 if (lpLoadOrderGroup != NULL)
727 new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
729 if (lpServiceStartName != NULL)
730 new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
732 if (lpDisplayName != NULL)
733 new_entry.config.lpDisplayName = strdupW(lpDisplayName);
735 /* try to save to Registry, commit or rollback depending on success */
736 err = save_service_config(&new_entry);
737 if (ERROR_SUCCESS == err)
739 free_service_strings(service->service_entry, &new_entry);
740 *service->service_entry = new_entry;
742 else free_service_strings(&new_entry, service->service_entry);
743 service_unlock(service->service_entry);
745 return err;
748 DWORD __cdecl svcctl_SetServiceStatus(
749 SC_RPC_HANDLE hServiceStatus,
750 LPSERVICE_STATUS lpServiceStatus)
752 struct sc_service_handle *service;
753 struct process_entry *process;
754 DWORD err;
756 WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
758 if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
759 return err;
761 service_lock(service->service_entry);
762 /* FIXME: be a bit more discriminant about what parts of the status we set
763 * and check that fields are valid */
764 service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
765 service->service_entry->status.dwCurrentState = lpServiceStatus->dwCurrentState;
766 service->service_entry->status.dwControlsAccepted = lpServiceStatus->dwControlsAccepted;
767 service->service_entry->status.dwWin32ExitCode = lpServiceStatus->dwWin32ExitCode;
768 service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
769 service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
770 service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
771 if ((process = service->service_entry->process))
773 if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED)
774 run_after_timeout(process_terminate, process, service_kill_timeout);
775 else
776 SetEvent(process->status_changed_event);
778 service_unlock(service->service_entry);
780 return ERROR_SUCCESS;
783 DWORD __cdecl svcctl_ChangeServiceConfig2W( SC_RPC_HANDLE hService, SC_RPC_CONFIG_INFOW config )
785 struct sc_service_handle *service;
786 DWORD err;
788 if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
789 return err;
791 switch (config.dwInfoLevel)
793 case SERVICE_CONFIG_DESCRIPTION:
795 WCHAR *descr = NULL;
797 if (config.u.descr->lpDescription[0])
799 if (!(descr = strdupW( config.u.descr->lpDescription )))
800 return ERROR_NOT_ENOUGH_MEMORY;
803 WINE_TRACE( "changing service %p descr to %s\n", service, wine_dbgstr_w(descr) );
804 service_lock( service->service_entry );
805 HeapFree( GetProcessHeap(), 0, service->service_entry->description );
806 service->service_entry->description = descr;
807 save_service_config( service->service_entry );
808 service_unlock( service->service_entry );
810 break;
811 case SERVICE_CONFIG_FAILURE_ACTIONS:
812 WINE_FIXME( "SERVICE_CONFIG_FAILURE_ACTIONS not implemented: period %u msg %s cmd %s\n",
813 config.u.actions->dwResetPeriod,
814 wine_dbgstr_w(config.u.actions->lpRebootMsg),
815 wine_dbgstr_w(config.u.actions->lpCommand) );
816 break;
817 case SERVICE_CONFIG_PRESHUTDOWN_INFO:
818 WINE_TRACE( "changing service %p preshutdown timeout to %d\n",
819 service, config.u.preshutdown->dwPreshutdownTimeout );
820 service_lock( service->service_entry );
821 service->service_entry->preshutdown_timeout = config.u.preshutdown->dwPreshutdownTimeout;
822 save_service_config( service->service_entry );
823 service_unlock( service->service_entry );
824 break;
825 default:
826 WINE_FIXME("level %u not implemented\n", config.dwInfoLevel);
827 err = ERROR_INVALID_LEVEL;
828 break;
830 return err;
833 DWORD __cdecl svcctl_QueryServiceConfig2W( SC_RPC_HANDLE hService, DWORD level,
834 BYTE *buffer, DWORD size, LPDWORD needed )
836 struct sc_service_handle *service;
837 DWORD err;
839 memset(buffer, 0, size);
841 if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
842 return err;
844 switch (level)
846 case SERVICE_CONFIG_DESCRIPTION:
848 SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
850 service_lock(service->service_entry);
851 *needed = sizeof(*descr);
852 if (service->service_entry->description)
853 *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR);
854 if (size >= *needed)
856 if (service->service_entry->description)
858 /* store a buffer offset instead of a pointer */
859 descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer);
860 strcpyW( (WCHAR *)(descr + 1), service->service_entry->description );
862 else descr->lpDescription = NULL;
864 else err = ERROR_INSUFFICIENT_BUFFER;
865 service_unlock(service->service_entry);
867 break;
869 case SERVICE_CONFIG_PRESHUTDOWN_INFO:
870 service_lock(service->service_entry);
872 *needed = sizeof(SERVICE_PRESHUTDOWN_INFO);
873 if (size >= *needed)
874 ((LPSERVICE_PRESHUTDOWN_INFO)buffer)->dwPreshutdownTimeout =
875 service->service_entry->preshutdown_timeout;
876 else err = ERROR_INSUFFICIENT_BUFFER;
878 service_unlock(service->service_entry);
879 break;
881 default:
882 WINE_FIXME("level %u not implemented\n", level);
883 err = ERROR_INVALID_LEVEL;
884 break;
886 return err;
889 DWORD __cdecl svcctl_QueryServiceStatusEx(
890 SC_RPC_HANDLE hService,
891 SC_STATUS_TYPE InfoLevel,
892 BYTE *lpBuffer,
893 DWORD cbBufSize,
894 LPDWORD pcbBytesNeeded)
896 struct sc_service_handle *service;
897 DWORD err;
898 LPSERVICE_STATUS_PROCESS pSvcStatusData;
900 memset(lpBuffer, 0, cbBufSize);
902 if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
903 return err;
905 if (InfoLevel != SC_STATUS_PROCESS_INFO)
906 return ERROR_INVALID_LEVEL;
908 pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
909 if (pSvcStatusData == NULL)
910 return ERROR_INVALID_PARAMETER;
912 if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
914 if( pcbBytesNeeded != NULL)
915 *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
917 return ERROR_INSUFFICIENT_BUFFER;
920 service_lock(service->service_entry);
922 pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
923 pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
924 pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
925 pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
926 pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
927 pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
928 pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
929 pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
930 pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
932 service_unlock(service->service_entry);
934 return ERROR_SUCCESS;
937 /******************************************************************************
938 * service_accepts_control
940 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
942 DWORD a = service->status.dwControlsAccepted;
944 switch (dwControl)
946 case SERVICE_CONTROL_INTERROGATE:
947 return TRUE;
948 case SERVICE_CONTROL_STOP:
949 if (a&SERVICE_ACCEPT_STOP)
950 return TRUE;
951 break;
952 case SERVICE_CONTROL_SHUTDOWN:
953 if (a&SERVICE_ACCEPT_SHUTDOWN)
954 return TRUE;
955 break;
956 case SERVICE_CONTROL_PAUSE:
957 case SERVICE_CONTROL_CONTINUE:
958 if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
959 return TRUE;
960 break;
961 case SERVICE_CONTROL_PARAMCHANGE:
962 if (a&SERVICE_ACCEPT_PARAMCHANGE)
963 return TRUE;
964 break;
965 case SERVICE_CONTROL_NETBINDADD:
966 case SERVICE_CONTROL_NETBINDREMOVE:
967 case SERVICE_CONTROL_NETBINDENABLE:
968 case SERVICE_CONTROL_NETBINDDISABLE:
969 if (a&SERVICE_ACCEPT_NETBINDCHANGE)
970 return TRUE;
971 case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
972 if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
973 return TRUE;
974 break;
975 case SERVICE_CONTROL_POWEREVENT:
976 if (a&SERVICE_ACCEPT_POWEREVENT)
977 return TRUE;
978 break;
979 case SERVICE_CONTROL_SESSIONCHANGE:
980 if (a&SERVICE_ACCEPT_SESSIONCHANGE)
981 return TRUE;
982 break;
984 return FALSE;
987 /******************************************************************************
988 * process_send_command
990 BOOL process_send_command(struct process_entry *process, const void *data, DWORD size, DWORD *result)
992 OVERLAPPED overlapped;
993 DWORD count, ret;
994 BOOL r;
996 overlapped.hEvent = process->overlapped_event;
997 r = WriteFile(process->control_pipe, data, size, &count, &overlapped);
998 if (!r && GetLastError() == ERROR_IO_PENDING)
1000 ret = WaitForSingleObject(process->overlapped_event, service_pipe_timeout);
1001 if (ret == WAIT_TIMEOUT)
1003 WINE_ERR("sending command timed out\n");
1004 *result = ERROR_SERVICE_REQUEST_TIMEOUT;
1005 return FALSE;
1007 r = GetOverlappedResult(process->control_pipe, &overlapped, &count, FALSE);
1009 if (!r || count != size)
1011 WINE_ERR("service protocol error - failed to write pipe!\n");
1012 *result = (!r ? GetLastError() : ERROR_WRITE_FAULT);
1013 return FALSE;
1015 r = ReadFile(process->control_pipe, result, sizeof *result, &count, &overlapped);
1016 if (!r && GetLastError() == ERROR_IO_PENDING)
1018 ret = WaitForSingleObject(process->overlapped_event, service_pipe_timeout);
1019 if (ret == WAIT_TIMEOUT)
1021 WINE_ERR("receiving command result timed out\n");
1022 *result = ERROR_SERVICE_REQUEST_TIMEOUT;
1023 return FALSE;
1025 r = GetOverlappedResult(process->control_pipe, &overlapped, &count, FALSE);
1027 if (!r || count != sizeof *result)
1029 WINE_ERR("service protocol error - failed to read pipe "
1030 "r = %d count = %d!\n", r, count);
1031 *result = (!r ? GetLastError() : ERROR_READ_FAULT);
1032 return FALSE;
1035 *result = ERROR_SUCCESS;
1036 return TRUE;
1039 /******************************************************************************
1040 * process_send_control
1042 static BOOL process_send_control(struct process_entry *process, const WCHAR *name, DWORD dwControl, DWORD *result)
1044 service_start_info *ssi;
1045 DWORD len;
1046 BOOL r;
1048 /* calculate how much space we need to send the startup info */
1049 len = strlenW(name) + 1;
1051 ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
1052 ssi->cmd = WINESERV_SENDCONTROL;
1053 ssi->control = dwControl;
1054 ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
1055 ssi->name_size = strlenW(name) + 1;
1056 strcpyW(ssi->data, name);
1058 r = process_send_command(process, ssi, ssi->total_size, result);
1059 HeapFree( GetProcessHeap(), 0, ssi );
1060 return r;
1063 DWORD __cdecl svcctl_StartServiceW(
1064 SC_RPC_HANDLE hService,
1065 DWORD dwNumServiceArgs,
1066 LPCWSTR *lpServiceArgVectors)
1068 struct sc_service_handle *service;
1069 DWORD err;
1071 WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
1073 if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
1074 return err;
1076 if (service->service_entry->config.dwStartType == SERVICE_DISABLED)
1077 return ERROR_SERVICE_DISABLED;
1079 err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
1081 return err;
1084 DWORD __cdecl svcctl_ControlService(
1085 SC_RPC_HANDLE hService,
1086 DWORD dwControl,
1087 SERVICE_STATUS *lpServiceStatus)
1089 DWORD access_required;
1090 struct sc_service_handle *service;
1091 struct process_entry *process;
1092 DWORD result;
1093 BOOL ret;
1095 WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
1097 switch (dwControl)
1099 case SERVICE_CONTROL_CONTINUE:
1100 case SERVICE_CONTROL_NETBINDADD:
1101 case SERVICE_CONTROL_NETBINDDISABLE:
1102 case SERVICE_CONTROL_NETBINDENABLE:
1103 case SERVICE_CONTROL_NETBINDREMOVE:
1104 case SERVICE_CONTROL_PARAMCHANGE:
1105 case SERVICE_CONTROL_PAUSE:
1106 access_required = SERVICE_PAUSE_CONTINUE;
1107 break;
1108 case SERVICE_CONTROL_INTERROGATE:
1109 access_required = SERVICE_INTERROGATE;
1110 break;
1111 case SERVICE_CONTROL_STOP:
1112 access_required = SERVICE_STOP;
1113 break;
1114 default:
1115 if (dwControl >= 128 && dwControl <= 255)
1116 access_required = SERVICE_USER_DEFINED_CONTROL;
1117 else
1118 return ERROR_INVALID_PARAMETER;
1121 if ((result = validate_service_handle(hService, access_required, &service)) != 0)
1122 return result;
1124 service_lock(service->service_entry);
1126 result = ERROR_SUCCESS;
1127 switch (service->service_entry->status.dwCurrentState)
1129 case SERVICE_STOPPED:
1130 result = ERROR_SERVICE_NOT_ACTIVE;
1131 break;
1132 case SERVICE_START_PENDING:
1133 if (dwControl==SERVICE_CONTROL_STOP)
1134 break;
1135 /* fall through */
1136 case SERVICE_STOP_PENDING:
1137 result = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
1138 break;
1141 if (result == ERROR_SUCCESS && service->service_entry->force_shutdown)
1143 result = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
1144 service_terminate(service->service_entry);
1147 if (result != ERROR_SUCCESS)
1149 if (lpServiceStatus)
1151 lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
1152 lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
1153 lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
1154 lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
1155 lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
1156 lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
1157 lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
1159 service_unlock(service->service_entry);
1160 return result;
1163 if (!service_accepts_control(service->service_entry, dwControl))
1165 service_unlock(service->service_entry);
1166 return ERROR_INVALID_SERVICE_CONTROL;
1169 /* Remember that we tried to shutdown this service. When the service is
1170 * still running on the second invocation, it will be forcefully killed. */
1171 if (dwControl == SERVICE_CONTROL_STOP)
1172 service->service_entry->force_shutdown = TRUE;
1174 /* Hold a reference to the process while sending the command. */
1175 process = grab_process(service->service_entry->process);
1176 service_unlock(service->service_entry);
1178 if (!process)
1179 return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
1181 ret = WaitForSingleObject(process->control_mutex, 30000);
1182 if (ret != WAIT_OBJECT_0)
1184 release_process(process);
1185 return ERROR_SERVICE_REQUEST_TIMEOUT;
1188 process_send_control(process, service->service_entry->name, dwControl, &result);
1190 if (lpServiceStatus)
1192 service_lock(service->service_entry);
1193 lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
1194 lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
1195 lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
1196 lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
1197 lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
1198 lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
1199 lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
1200 service_unlock(service->service_entry);
1203 ReleaseMutex(process->control_mutex);
1204 release_process(process);
1205 return result;
1208 DWORD __cdecl svcctl_CloseServiceHandle(
1209 SC_RPC_HANDLE *handle)
1211 WINE_TRACE("(&%p)\n", *handle);
1213 SC_RPC_HANDLE_destroy(*handle);
1214 *handle = NULL;
1216 return ERROR_SUCCESS;
1219 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
1221 struct sc_lock *lock = hLock;
1222 scmdatabase_unlock_startup(lock->db);
1223 HeapFree(GetProcessHeap(), 0, lock);
1226 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
1228 SC_RPC_LOCK_destroy(hLock);
1231 DWORD __cdecl svcctl_LockServiceDatabase(
1232 SC_RPC_HANDLE hSCManager,
1233 SC_RPC_LOCK *phLock)
1235 struct sc_manager_handle *manager;
1236 struct sc_lock *lock;
1237 DWORD err;
1239 WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
1241 if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
1242 return err;
1244 err = scmdatabase_lock_startup(manager->db);
1245 if (err != ERROR_SUCCESS)
1246 return err;
1248 lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
1249 if (!lock)
1251 scmdatabase_unlock_startup(manager->db);
1252 return ERROR_NOT_ENOUGH_SERVER_MEMORY;
1255 lock->db = manager->db;
1256 *phLock = lock;
1258 return ERROR_SUCCESS;
1261 DWORD __cdecl svcctl_UnlockServiceDatabase(
1262 SC_RPC_LOCK *phLock)
1264 WINE_TRACE("(&%p)\n", *phLock);
1266 SC_RPC_LOCK_destroy(*phLock);
1267 *phLock = NULL;
1269 return ERROR_SUCCESS;
1272 static BOOL map_state(DWORD state, DWORD mask)
1274 switch (state)
1276 case SERVICE_START_PENDING:
1277 case SERVICE_STOP_PENDING:
1278 case SERVICE_RUNNING:
1279 case SERVICE_CONTINUE_PENDING:
1280 case SERVICE_PAUSE_PENDING:
1281 case SERVICE_PAUSED:
1282 if (SERVICE_ACTIVE & mask) return TRUE;
1283 break;
1284 case SERVICE_STOPPED:
1285 if (SERVICE_INACTIVE & mask) return TRUE;
1286 break;
1287 default:
1288 WINE_ERR("unknown state %u\n", state);
1289 break;
1291 return FALSE;
1294 DWORD __cdecl svcctl_EnumServicesStatusW(
1295 SC_RPC_HANDLE hmngr,
1296 DWORD type,
1297 DWORD state,
1298 BYTE *buffer,
1299 DWORD size,
1300 LPDWORD needed,
1301 LPDWORD returned,
1302 LPDWORD resume)
1304 DWORD err, sz, total_size, num_services;
1305 DWORD_PTR offset;
1306 struct sc_manager_handle *manager;
1307 struct service_entry *service;
1308 ENUM_SERVICE_STATUSW *s;
1310 WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %p)\n", hmngr, type, state, buffer, size, needed, returned, resume);
1312 if (!type || !state)
1313 return ERROR_INVALID_PARAMETER;
1315 if ((err = validate_scm_handle(hmngr, SC_MANAGER_ENUMERATE_SERVICE, &manager)) != ERROR_SUCCESS)
1316 return err;
1318 if (resume)
1319 WINE_FIXME("resume index not supported\n");
1321 scmdatabase_lock(manager->db);
1323 total_size = num_services = 0;
1324 LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1326 if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
1328 total_size += sizeof(ENUM_SERVICE_STATUSW);
1329 total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
1330 if (service->config.lpDisplayName)
1332 total_size += (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1334 num_services++;
1337 *returned = 0;
1338 *needed = total_size;
1339 if (total_size > size)
1341 scmdatabase_unlock(manager->db);
1342 return ERROR_MORE_DATA;
1344 s = (ENUM_SERVICE_STATUSW *)buffer;
1345 offset = num_services * sizeof(ENUM_SERVICE_STATUSW);
1346 LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1348 if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
1350 sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
1351 memcpy(buffer + offset, service->name, sz);
1352 s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
1353 offset += sz;
1355 if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
1356 else
1358 sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1359 memcpy(buffer + offset, service->config.lpDisplayName, sz);
1360 s->lpDisplayName = (WCHAR *)offset;
1361 offset += sz;
1363 memcpy(&s->ServiceStatus, &service->status, sizeof(SERVICE_STATUS));
1364 s++;
1367 *returned = num_services;
1368 *needed = 0;
1369 scmdatabase_unlock(manager->db);
1370 return ERROR_SUCCESS;
1373 static struct service_entry *find_service_by_group(struct scmdatabase *db, const WCHAR *group)
1375 struct service_entry *service;
1376 LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
1378 if (service->config.lpLoadOrderGroup && !strcmpiW(group, service->config.lpLoadOrderGroup))
1379 return service;
1381 return NULL;
1384 static BOOL match_group(const WCHAR *g1, const WCHAR *g2)
1386 if (!g2) return TRUE;
1387 if (!g2[0] && (!g1 || !g1[0])) return TRUE;
1388 if (g1 && !strcmpW(g1, g2)) return TRUE;
1389 return FALSE;
1392 DWORD __cdecl svcctl_EnumServicesStatusExA(
1393 SC_RPC_HANDLE scmanager,
1394 SC_ENUM_TYPE info_level,
1395 DWORD service_type,
1396 DWORD service_state,
1397 BYTE *buffer,
1398 DWORD buf_size,
1399 DWORD *needed_size,
1400 DWORD *services_count,
1401 DWORD *resume_index,
1402 LPCSTR groupname)
1404 WINE_FIXME("\n");
1405 return ERROR_CALL_NOT_IMPLEMENTED;
1408 DWORD __cdecl svcctl_EnumServicesStatusExW(
1409 SC_RPC_HANDLE hmngr,
1410 SC_ENUM_TYPE info_level,
1411 DWORD type,
1412 DWORD state,
1413 BYTE *buffer,
1414 DWORD size,
1415 LPDWORD needed,
1416 LPDWORD returned,
1417 DWORD *resume_handle,
1418 LPCWSTR group)
1420 DWORD err, sz, total_size, num_services;
1421 DWORD_PTR offset;
1422 struct sc_manager_handle *manager;
1423 struct service_entry *service;
1424 ENUM_SERVICE_STATUS_PROCESSW *s;
1426 WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %s)\n", hmngr, type, state, buffer, size,
1427 needed, returned, wine_dbgstr_w(group));
1429 if (resume_handle)
1430 FIXME("resume handle not supported\n");
1432 if (!type || !state)
1433 return ERROR_INVALID_PARAMETER;
1435 if ((err = validate_scm_handle(hmngr, SC_MANAGER_ENUMERATE_SERVICE, &manager)) != ERROR_SUCCESS)
1436 return err;
1438 scmdatabase_lock(manager->db);
1440 if (group && !find_service_by_group(manager->db, group))
1442 scmdatabase_unlock(manager->db);
1443 return ERROR_SERVICE_DOES_NOT_EXIST;
1446 total_size = num_services = 0;
1447 LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1449 if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
1450 && match_group(service->config.lpLoadOrderGroup, group))
1452 total_size += sizeof(ENUM_SERVICE_STATUS_PROCESSW);
1453 total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
1454 if (service->config.lpDisplayName)
1456 total_size += (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1458 num_services++;
1461 *returned = 0;
1462 *needed = total_size;
1463 if (total_size > size)
1465 scmdatabase_unlock(manager->db);
1466 return ERROR_MORE_DATA;
1468 s = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
1469 offset = num_services * sizeof(ENUM_SERVICE_STATUS_PROCESSW);
1470 LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1472 if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
1473 && match_group(service->config.lpLoadOrderGroup, group))
1475 sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
1476 memcpy(buffer + offset, service->name, sz);
1477 s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
1478 offset += sz;
1480 if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
1481 else
1483 sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1484 memcpy(buffer + offset, service->config.lpDisplayName, sz);
1485 s->lpDisplayName = (WCHAR *)offset;
1486 offset += sz;
1488 s->ServiceStatusProcess = service->status;
1489 s++;
1492 *returned = num_services;
1493 *needed = 0;
1494 scmdatabase_unlock(manager->db);
1495 return ERROR_SUCCESS;
1498 DWORD __cdecl svcctl_unknown43(void)
1500 WINE_FIXME("\n");
1501 return ERROR_CALL_NOT_IMPLEMENTED;
1504 DWORD __cdecl svcctl_CreateServiceWOW64A(
1505 SC_RPC_HANDLE scmanager,
1506 LPCSTR servicename,
1507 LPCSTR displayname,
1508 DWORD accessmask,
1509 DWORD service_type,
1510 DWORD start_type,
1511 DWORD error_control,
1512 LPCSTR imagepath,
1513 LPCSTR loadordergroup,
1514 DWORD *tagid,
1515 const BYTE *dependencies,
1516 DWORD depend_size,
1517 LPCSTR start_name,
1518 const BYTE *password,
1519 DWORD password_size,
1520 SC_RPC_HANDLE *service)
1522 WINE_FIXME("\n");
1523 return ERROR_CALL_NOT_IMPLEMENTED;
1526 DWORD __cdecl svcctl_CreateServiceWOW64W(
1527 SC_RPC_HANDLE scmanager,
1528 LPCWSTR servicename,
1529 LPCWSTR displayname,
1530 DWORD accessmask,
1531 DWORD service_type,
1532 DWORD start_type,
1533 DWORD error_control,
1534 LPCWSTR imagepath,
1535 LPCWSTR loadordergroup,
1536 DWORD *tagid,
1537 const BYTE *dependencies,
1538 DWORD depend_size,
1539 LPCWSTR start_name,
1540 const BYTE *password,
1541 DWORD password_size,
1542 SC_RPC_HANDLE *service)
1544 WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(servicename), wine_dbgstr_w(displayname), accessmask, wine_dbgstr_w(imagepath));
1545 return create_serviceW(scmanager, servicename, displayname, accessmask, service_type, start_type, error_control, imagepath,
1546 loadordergroup, tagid, dependencies, depend_size, start_name, password, password_size, service, TRUE);
1549 DWORD __cdecl svcctl_unknown46(void)
1551 WINE_FIXME("\n");
1552 return ERROR_CALL_NOT_IMPLEMENTED;
1555 DWORD __cdecl svcctl_NotifyServiceStatusChange(
1556 SC_RPC_HANDLE service,
1557 SC_RPC_NOTIFY_PARAMS params,
1558 GUID *clientprocessguid,
1559 GUID *scmprocessguid,
1560 BOOL *createremotequeue,
1561 SC_NOTIFY_RPC_HANDLE *notify)
1563 WINE_FIXME("\n");
1564 return ERROR_CALL_NOT_IMPLEMENTED;
1567 DWORD __cdecl svcctl_GetNotifyResults(
1568 SC_NOTIFY_RPC_HANDLE notify,
1569 SC_RPC_NOTIFY_PARAMS_LIST **params)
1571 WINE_FIXME("\n");
1572 return ERROR_CALL_NOT_IMPLEMENTED;
1575 DWORD __cdecl svcctl_CloseNotifyHandle(
1576 SC_NOTIFY_RPC_HANDLE *notify,
1577 BOOL *apc_fired)
1579 WINE_FIXME("\n");
1580 return ERROR_CALL_NOT_IMPLEMENTED;
1583 DWORD __cdecl svcctl_ControlServiceExA(
1584 SC_RPC_HANDLE service,
1585 DWORD control,
1586 DWORD info_level,
1587 SC_RPC_SERVICE_CONTROL_IN_PARAMSA *in_params,
1588 SC_RPC_SERVICE_CONTROL_OUT_PARAMSA *out_params)
1590 WINE_FIXME("\n");
1591 return ERROR_CALL_NOT_IMPLEMENTED;
1594 DWORD __cdecl svcctl_ControlServiceExW(
1595 SC_RPC_HANDLE service,
1596 DWORD control,
1597 DWORD info_level,
1598 SC_RPC_SERVICE_CONTROL_IN_PARAMSW *in_params,
1599 SC_RPC_SERVICE_CONTROL_OUT_PARAMSW *out_params)
1601 WINE_FIXME("\n");
1602 return ERROR_CALL_NOT_IMPLEMENTED;
1605 DWORD __cdecl svcctl_unknown52(void)
1607 WINE_FIXME("\n");
1608 return ERROR_CALL_NOT_IMPLEMENTED;
1611 DWORD __cdecl svcctl_unknown53(void)
1613 WINE_FIXME("\n");
1614 return ERROR_CALL_NOT_IMPLEMENTED;
1617 DWORD __cdecl svcctl_unknown54(void)
1619 WINE_FIXME("\n");
1620 return ERROR_CALL_NOT_IMPLEMENTED;
1623 DWORD __cdecl svcctl_unknown55(void)
1625 WINE_FIXME("\n");
1626 return ERROR_CALL_NOT_IMPLEMENTED;
1629 DWORD __cdecl svcctl_QueryServiceConfigEx(
1630 SC_RPC_HANDLE service,
1631 DWORD info_level,
1632 SC_RPC_CONFIG_INFOW *info)
1634 WINE_FIXME("\n");
1635 return ERROR_CALL_NOT_IMPLEMENTED;
1638 DWORD __cdecl svcctl_QueryServiceObjectSecurity(
1639 SC_RPC_HANDLE service,
1640 SECURITY_INFORMATION info,
1641 BYTE *descriptor,
1642 DWORD buf_size,
1643 DWORD *needed_size)
1645 WINE_FIXME("\n");
1646 return ERROR_CALL_NOT_IMPLEMENTED;
1649 DWORD __cdecl svcctl_SetServiceObjectSecurity(
1650 SC_RPC_HANDLE service,
1651 SECURITY_INFORMATION info,
1652 BYTE *descriptor,
1653 DWORD buf_size)
1655 WINE_FIXME("\n");
1656 return ERROR_CALL_NOT_IMPLEMENTED;
1659 DWORD __cdecl svcctl_QueryServiceStatus(
1660 SC_RPC_HANDLE service,
1661 SERVICE_STATUS *status)
1663 WINE_FIXME("\n");
1664 return ERROR_CALL_NOT_IMPLEMENTED;
1667 DWORD __cdecl svcctl_NotifyBootConfigStatus(
1668 SVCCTL_HANDLEW machinename,
1669 DWORD boot_acceptable)
1671 WINE_FIXME("\n");
1672 return ERROR_CALL_NOT_IMPLEMENTED;
1675 DWORD __cdecl svcctl_SCSetServiceBitsW(void)
1677 WINE_FIXME("\n");
1678 return ERROR_CALL_NOT_IMPLEMENTED;
1681 DWORD __cdecl svcctl_EnumDependentServicesW(
1682 SC_RPC_HANDLE service,
1683 DWORD state,
1684 BYTE *services,
1685 DWORD buf_size,
1686 DWORD *needed_size,
1687 DWORD *services_ret)
1689 WINE_FIXME("\n");
1690 return ERROR_CALL_NOT_IMPLEMENTED;
1693 DWORD __cdecl svcctl_QueryServiceLockStatusW(
1694 SC_RPC_HANDLE scmanager,
1695 QUERY_SERVICE_LOCK_STATUSW *status,
1696 DWORD buf_size,
1697 DWORD *needed_size)
1699 WINE_FIXME("\n");
1700 return ERROR_CALL_NOT_IMPLEMENTED;
1703 DWORD __cdecl svcctl_SCSetServiceBitsA(void)
1705 WINE_FIXME("\n");
1706 return ERROR_CALL_NOT_IMPLEMENTED;
1709 DWORD __cdecl svcctl_ChangeServiceConfigA(
1710 SC_RPC_HANDLE service,
1711 DWORD service_type,
1712 DWORD start_type,
1713 DWORD error_control,
1714 LPSTR binarypath,
1715 LPSTR loadordergroup,
1716 DWORD *tagid,
1717 BYTE *dependencies,
1718 DWORD depend_size,
1719 LPSTR startname,
1720 BYTE *password,
1721 DWORD password_size,
1722 LPSTR displayname)
1724 WINE_FIXME("\n");
1725 return ERROR_CALL_NOT_IMPLEMENTED;
1728 DWORD __cdecl svcctl_CreateServiceA(
1729 SC_RPC_HANDLE scmanager,
1730 LPCSTR servicename,
1731 LPCSTR displayname,
1732 DWORD desiredaccess,
1733 DWORD service_type,
1734 DWORD start_type,
1735 DWORD error_control,
1736 LPCSTR binarypath,
1737 LPCSTR loadordergroup,
1738 DWORD *tagid,
1739 const BYTE *dependencies,
1740 DWORD depend_size,
1741 LPCSTR startname,
1742 const BYTE *password,
1743 DWORD password_size,
1744 SC_RPC_HANDLE *service)
1746 WINE_FIXME("\n");
1747 return ERROR_CALL_NOT_IMPLEMENTED;
1750 DWORD __cdecl svcctl_EnumDependentServicesA(
1751 SC_RPC_HANDLE service,
1752 DWORD state,
1753 BYTE *services,
1754 DWORD buf_size,
1755 DWORD *needed_size,
1756 DWORD *services_ret)
1758 WINE_FIXME("\n");
1759 return ERROR_CALL_NOT_IMPLEMENTED;
1762 DWORD __cdecl svcctl_EnumServicesStatusA(
1763 SC_RPC_HANDLE hmngr,
1764 DWORD type,
1765 DWORD state,
1766 BYTE *buffer,
1767 DWORD size,
1768 DWORD *needed,
1769 DWORD *returned,
1770 DWORD *resume)
1772 WINE_FIXME("\n");
1773 return ERROR_CALL_NOT_IMPLEMENTED;
1776 DWORD __cdecl svcctl_OpenSCManagerA(
1777 MACHINE_HANDLEA MachineName,
1778 LPCSTR DatabaseName,
1779 DWORD dwAccessMask,
1780 SC_RPC_HANDLE *handle)
1782 WINE_FIXME("\n");
1783 return ERROR_CALL_NOT_IMPLEMENTED;
1786 DWORD __cdecl svcctl_OpenServiceA(
1787 SC_RPC_HANDLE hSCManager,
1788 LPCSTR lpServiceName,
1789 DWORD dwDesiredAccess,
1790 SC_RPC_HANDLE *phService)
1792 WINE_FIXME("\n");
1793 return ERROR_CALL_NOT_IMPLEMENTED;
1796 DWORD __cdecl svcctl_QueryServiceConfigA(
1797 SC_RPC_HANDLE hService,
1798 QUERY_SERVICE_CONFIGA *config,
1799 DWORD buf_size,
1800 DWORD *needed_size)
1802 WINE_FIXME("\n");
1803 return ERROR_CALL_NOT_IMPLEMENTED;
1806 DWORD __cdecl svcctl_QueryServiceLockStatusA(
1807 SC_RPC_HANDLE scmanager,
1808 QUERY_SERVICE_LOCK_STATUSA *status,
1809 DWORD buf_size,
1810 DWORD *needed_size)
1812 WINE_FIXME("\n");
1813 return ERROR_CALL_NOT_IMPLEMENTED;
1816 DWORD __cdecl svcctl_StartServiceA(
1817 SC_RPC_HANDLE service,
1818 DWORD argc,
1819 LPCSTR *args)
1821 WINE_FIXME("\n");
1822 return ERROR_CALL_NOT_IMPLEMENTED;
1825 DWORD __cdecl svcctl_GetServiceDisplayNameA(
1826 SC_RPC_HANDLE hSCManager,
1827 LPCSTR servicename,
1828 CHAR buffer[],
1829 DWORD *buf_size)
1831 WINE_FIXME("\n");
1832 return ERROR_CALL_NOT_IMPLEMENTED;
1835 DWORD __cdecl svcctl_GetServiceKeyNameA(
1836 SC_RPC_HANDLE hSCManager,
1837 LPCSTR servicename,
1838 CHAR buffer[],
1839 DWORD *buf_size)
1841 WINE_FIXME("\n");
1842 return ERROR_CALL_NOT_IMPLEMENTED;
1845 DWORD __cdecl svcctl_GetCurrentGroupStateW(void)
1847 WINE_FIXME("\n");
1848 return ERROR_CALL_NOT_IMPLEMENTED;
1851 DWORD __cdecl svcctl_EnumServiceGroupW(
1852 SC_RPC_HANDLE scmanager,
1853 DWORD service_type,
1854 DWORD service_state,
1855 BYTE *buffer,
1856 DWORD buf_size,
1857 DWORD *needed_size,
1858 DWORD *returned_size,
1859 DWORD *resume_index,
1860 LPCWSTR groupname)
1862 WINE_FIXME("\n");
1863 return ERROR_CALL_NOT_IMPLEMENTED;
1866 DWORD __cdecl svcctl_ChangeServiceConfig2A(
1867 SC_RPC_HANDLE service,
1868 SC_RPC_CONFIG_INFOA info)
1870 WINE_FIXME("\n");
1871 return ERROR_CALL_NOT_IMPLEMENTED;
1874 DWORD __cdecl svcctl_QueryServiceConfig2A(
1875 SC_RPC_HANDLE service,
1876 DWORD info_level,
1877 BYTE *buffer,
1878 DWORD buf_size,
1879 DWORD *needed_size)
1881 WINE_FIXME("\n");
1882 return ERROR_CALL_NOT_IMPLEMENTED;
1885 DWORD RPC_Init(void)
1887 WCHAR transport[] = SVCCTL_TRANSPORT;
1888 WCHAR endpoint[] = SVCCTL_ENDPOINT;
1889 DWORD err;
1891 if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1893 WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1894 return err;
1897 if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1899 WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1900 return err;
1903 if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1905 WINE_ERR("RpcServerListen failed with error %u\n", err);
1906 return err;
1908 return ERROR_SUCCESS;
1911 DWORD events_loop(void)
1913 struct timeout_queue_elem *iter, *iter_safe;
1914 DWORD err;
1915 HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
1916 DWORD timeout = INFINITE;
1918 wait_handles[0] = __wine_make_process_system();
1919 wait_handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL);
1920 timeout_queue_event = wait_handles[1];
1922 SetEvent(g_hStartedEvent);
1924 WINE_TRACE("Entered main loop\n");
1928 DWORD num_handles = 2;
1930 /* monitor tracked process handles for process end */
1931 EnterCriticalSection(&timeout_queue_cs);
1932 LIST_FOR_EACH_ENTRY(iter, &timeout_queue, struct timeout_queue_elem, entry)
1934 if(num_handles == MAXIMUM_WAIT_OBJECTS){
1935 WINE_TRACE("Exceeded maximum wait object count\n");
1936 break;
1938 wait_handles[num_handles] = iter->process->process;
1939 num_handles++;
1941 LeaveCriticalSection(&timeout_queue_cs);
1943 err = WaitForMultipleObjects(num_handles, wait_handles, FALSE, timeout);
1944 WINE_TRACE("Wait returned %d\n", err);
1946 if(err > WAIT_OBJECT_0 || err == WAIT_TIMEOUT)
1948 FILETIME cur_time;
1949 ULARGE_INTEGER time;
1950 DWORD idx = 0;
1952 GetSystemTimeAsFileTime(&cur_time);
1953 time.u.LowPart = cur_time.dwLowDateTime;
1954 time.u.HighPart = cur_time.dwHighDateTime;
1956 EnterCriticalSection(&timeout_queue_cs);
1957 timeout = INFINITE;
1958 LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
1960 if(CompareFileTime(&cur_time, &iter->time) >= 0 ||
1961 (err > WAIT_OBJECT_0 + 1 && idx == err - WAIT_OBJECT_0 - 2))
1963 LeaveCriticalSection(&timeout_queue_cs);
1964 iter->func(iter->process);
1965 EnterCriticalSection(&timeout_queue_cs);
1967 release_process(iter->process);
1968 list_remove(&iter->entry);
1969 HeapFree(GetProcessHeap(), 0, iter);
1971 else
1973 ULARGE_INTEGER time_diff;
1975 time_diff.u.LowPart = iter->time.dwLowDateTime;
1976 time_diff.u.HighPart = iter->time.dwHighDateTime;
1977 time_diff.QuadPart = (time_diff.QuadPart-time.QuadPart)/10000;
1979 if(time_diff.QuadPart < timeout)
1980 timeout = time_diff.QuadPart;
1982 idx++;
1984 LeaveCriticalSection(&timeout_queue_cs);
1986 if(timeout != INFINITE)
1987 timeout += 1000;
1989 } while (err != WAIT_OBJECT_0);
1991 WINE_TRACE("Object signaled - wine shutdown\n");
1992 EnterCriticalSection(&timeout_queue_cs);
1993 LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
1995 LeaveCriticalSection(&timeout_queue_cs);
1996 iter->func(iter->process);
1997 EnterCriticalSection(&timeout_queue_cs);
1999 release_process(iter->process);
2000 list_remove(&iter->entry);
2001 HeapFree(GetProcessHeap(), 0, iter);
2003 LeaveCriticalSection(&timeout_queue_cs);
2005 CloseHandle(wait_handles[0]);
2006 CloseHandle(wait_handles[1]);
2007 return ERROR_SUCCESS;
2010 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
2012 SC_RPC_HANDLE_destroy(handle);
2015 void __RPC_USER SC_NOTIFY_RPC_HANDLE_rundown(SC_NOTIFY_RPC_HANDLE handle)
2019 void __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
2021 return HeapAlloc(GetProcessHeap(), 0, len);
2024 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
2026 HeapFree(GetProcessHeap(), 0, ptr);