~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/programs/services/rpc.c

Version: ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * Services.exe - RPC functions
  3  *
  4  * Copyright 2007 Google (Mikolaj Zalewski)
  5  *
  6  * This library is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU Lesser General Public
  8  * License as published by the Free Software Foundation; either
  9  * version 2.1 of the License, or (at your option) any later version.
 10  *
 11  * This library is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public
 17  * License along with this library; if not, write to the Free Software
 18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 19  */
 20 
 21 #define WIN32_LEAN_AND_MEAN
 22 
 23 #include <stdarg.h>
 24 #include <windows.h>
 25 #include <winternl.h>
 26 #include <winsvc.h>
 27 #include <ntsecapi.h>
 28 #include <rpc.h>
 29 
 30 #include "wine/list.h"
 31 #include "wine/unicode.h"
 32 #include "wine/debug.h"
 33 
 34 #include "services.h"
 35 #include "svcctl.h"
 36 
 37 extern HANDLE CDECL __wine_make_process_system(void);
 38 
 39 WINE_DEFAULT_DEBUG_CHANNEL(service);
 40 
 41 static const GENERIC_MAPPING g_scm_generic =
 42 {
 43     (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS),
 44     (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG),
 45     (STANDARD_RIGHTS_EXECUTE | SC_MANAGER_CONNECT | SC_MANAGER_LOCK),
 46     SC_MANAGER_ALL_ACCESS
 47 };
 48 
 49 static const GENERIC_MAPPING g_svc_generic =
 50 {
 51     (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS),
 52     (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG),
 53     (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL),
 54     SERVICE_ALL_ACCESS
 55 };
 56 
 57 typedef enum
 58 {
 59     SC_HTYPE_DONT_CARE = 0,
 60     SC_HTYPE_MANAGER,
 61     SC_HTYPE_SERVICE
 62 } SC_HANDLE_TYPE;
 63 
 64 struct sc_handle
 65 {
 66     SC_HANDLE_TYPE type;
 67     DWORD access;
 68 };
 69 
 70 struct sc_manager_handle       /* service control manager handle */
 71 {
 72     struct sc_handle hdr;
 73     struct scmdatabase *db;
 74 };
 75 
 76 struct sc_service_handle       /* service handle */
 77 {
 78     struct sc_handle hdr;
 79     struct service_entry *service_entry;
 80 };
 81 
 82 struct sc_lock
 83 {
 84     struct scmdatabase *db;
 85 };
 86 
 87 static void free_config_strings(QUERY_SERVICE_CONFIGW *old_cfg, QUERY_SERVICE_CONFIGW *new_cfg)
 88 {
 89     if (old_cfg->lpBinaryPathName != new_cfg->lpBinaryPathName)
 90         HeapFree(GetProcessHeap(), 0, old_cfg->lpBinaryPathName);
 91 
 92     if (old_cfg->lpLoadOrderGroup != new_cfg->lpLoadOrderGroup)
 93         HeapFree(GetProcessHeap(), 0, old_cfg->lpLoadOrderGroup);
 94 
 95     if (old_cfg->lpServiceStartName != new_cfg->lpServiceStartName)
 96         HeapFree(GetProcessHeap(), 0, old_cfg->lpServiceStartName);
 97 
 98     if (old_cfg->lpDisplayName != new_cfg->lpDisplayName)
 99         HeapFree(GetProcessHeap(), 0, old_cfg->lpDisplayName);
100 }
101 
102 /* Check if the given handle is of the required type and allows the requested access. */
103 static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
104 {
105     struct sc_handle *hdr = handle;
106 
107     if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
108     {
109         WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
110         return ERROR_INVALID_HANDLE;
111     }
112 
113     if ((needed_access & hdr->access) != needed_access)
114     {
115         WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
116         return ERROR_ACCESS_DENIED;
117     }
118 
119     *out_hdr = hdr;
120     return ERROR_SUCCESS;
121 }
122 
123 static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager_handle **manager)
124 {
125     struct sc_handle *hdr;
126     DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
127     if (err == ERROR_SUCCESS)
128         *manager = (struct sc_manager_handle *)hdr;
129     return err;
130 }
131 
132 static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service_handle **service)
133 {
134     struct sc_handle *hdr;
135     DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
136     if (err == ERROR_SUCCESS)
137         *service = (struct sc_service_handle *)hdr;
138     return err;
139 }
140 
141 DWORD svcctl_OpenSCManagerW(
142     MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
143     LPCWSTR DatabaseName,
144     DWORD dwAccessMask,
145     SC_RPC_HANDLE *handle)
146 {
147     struct sc_manager_handle *manager;
148 
149     WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
150 
151     if (DatabaseName != NULL && DatabaseName[0])
152     {
153         if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0)
154             return ERROR_DATABASE_DOES_NOT_EXIST;
155         if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
156             return ERROR_INVALID_NAME;
157     }
158 
159     if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager))))
160         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
161 
162     manager->hdr.type = SC_HTYPE_MANAGER;
163 
164     if (dwAccessMask & MAXIMUM_ALLOWED)
165         dwAccessMask |= SC_MANAGER_ALL_ACCESS;
166     manager->hdr.access = dwAccessMask;
167     RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
168     manager->db = active_database;
169     *handle = &manager->hdr;
170 
171     return ERROR_SUCCESS;
172 }
173 
174 static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
175 {
176     struct sc_handle *hdr = handle;
177     switch (hdr->type)
178     {
179         case SC_HTYPE_MANAGER:
180         {
181             struct sc_manager_handle *manager = (struct sc_manager_handle *)hdr;
182             HeapFree(GetProcessHeap(), 0, manager);
183             break;
184         }
185         case SC_HTYPE_SERVICE:
186         {
187             struct sc_service_handle *service = (struct sc_service_handle *)hdr;
188             release_service(service->service_entry);
189             HeapFree(GetProcessHeap(), 0, service);
190             break;
191         }
192         default:
193             WINE_ERR("invalid handle type %d\n", hdr->type);
194             RpcRaiseException(ERROR_INVALID_HANDLE);
195     }
196 }
197 
198 DWORD svcctl_GetServiceDisplayNameW(
199     SC_RPC_HANDLE hSCManager,
200     LPCWSTR lpServiceName,
201     WCHAR *lpBuffer,
202     DWORD *cchBufSize)
203 {
204     struct sc_manager_handle *manager;
205     struct service_entry *entry;
206     DWORD err;
207 
208     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceName), *cchBufSize);
209 
210     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
211         return err;
212 
213     scmdatabase_lock_shared(manager->db);
214 
215     entry = scmdatabase_find_service(manager->db, lpServiceName);
216     if (entry != NULL)
217     {
218         LPCWSTR name;
219         int len;
220         service_lock_shared(entry);
221         name = get_display_name(entry);
222         len = strlenW(name);
223         if (len <= *cchBufSize)
224         {
225             err = ERROR_SUCCESS;
226             memcpy(lpBuffer, name, (len + 1)*sizeof(*name));
227         }
228         else
229             err = ERROR_INSUFFICIENT_BUFFER;
230         *cchBufSize = len;
231         service_unlock(entry);
232     }
233     else
234         err = ERROR_SERVICE_DOES_NOT_EXIST;
235 
236     scmdatabase_unlock(manager->db);
237 
238     if (err != ERROR_SUCCESS)
239         lpBuffer[0] = 0;
240 
241     return err;
242 }
243 
244 DWORD svcctl_GetServiceKeyNameW(
245     SC_RPC_HANDLE hSCManager,
246     LPCWSTR lpServiceDisplayName,
247     WCHAR *lpBuffer,
248     DWORD *cchBufSize)
249 {
250     struct service_entry *entry;
251     struct sc_manager_handle *manager;
252     DWORD err;
253 
254     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), *cchBufSize);
255 
256     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
257         return err;
258 
259     scmdatabase_lock_shared(manager->db);
260 
261     entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
262     if (entry != NULL)
263     {
264         int len;
265         service_lock_shared(entry);
266         len = strlenW(entry->name);
267         if (len <= *cchBufSize)
268         {
269             err = ERROR_SUCCESS;
270             memcpy(lpBuffer, entry->name, (len + 1)*sizeof(*entry->name));
271         }
272         else
273             err = ERROR_INSUFFICIENT_BUFFER;
274         *cchBufSize = len;
275         service_unlock(entry);
276     }
277     else
278         err = ERROR_SERVICE_DOES_NOT_EXIST;
279 
280     scmdatabase_unlock(manager->db);
281 
282     if (err != ERROR_SUCCESS)
283         lpBuffer[0] = 0;
284 
285     return err;
286 }
287 
288 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
289 {
290     struct sc_service_handle *service;
291 
292     if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
293     {
294         release_service(entry);
295         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
296     }
297 
298     service->hdr.type = SC_HTYPE_SERVICE;
299     service->hdr.access = dwDesiredAccess;
300     RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
301     service->service_entry = entry;
302     if (dwDesiredAccess & MAXIMUM_ALLOWED)
303         dwDesiredAccess |= SERVICE_ALL_ACCESS;
304 
305     *phService = &service->hdr;
306     return ERROR_SUCCESS;
307 }
308 
309 DWORD svcctl_OpenServiceW(
310     SC_RPC_HANDLE hSCManager,
311     LPCWSTR lpServiceName,
312     DWORD dwDesiredAccess,
313     SC_RPC_HANDLE *phService)
314 {
315     struct sc_manager_handle *manager;
316     struct service_entry *entry;
317     DWORD err;
318 
319     WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
320 
321     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
322         return err;
323     if (!validate_service_name(lpServiceName))
324         return ERROR_INVALID_NAME;
325 
326     scmdatabase_lock_shared(manager->db);
327     entry = scmdatabase_find_service(manager->db, lpServiceName);
328     if (entry != NULL)
329         InterlockedIncrement(&entry->ref_count);
330     scmdatabase_unlock(manager->db);
331 
332     if (entry == NULL)
333         return ERROR_SERVICE_DOES_NOT_EXIST;
334 
335     return create_handle_for_service(entry, dwDesiredAccess, phService);
336 }
337 
338 DWORD svcctl_CreateServiceW(
339     SC_RPC_HANDLE hSCManager,
340     LPCWSTR lpServiceName,
341     LPCWSTR lpDisplayName,
342     DWORD dwDesiredAccess,
343     DWORD dwServiceType,
344     DWORD dwStartType,
345     DWORD dwErrorControl,
346     LPCWSTR lpBinaryPathName,
347     LPCWSTR lpLoadOrderGroup,
348     DWORD *lpdwTagId,
349     const BYTE *lpDependencies,
350     DWORD dwDependenciesSize,
351     LPCWSTR lpServiceStartName,
352     const BYTE *lpPassword,
353     DWORD dwPasswordSize,
354     SC_RPC_HANDLE *phService)
355 {
356     struct sc_manager_handle *manager;
357     struct service_entry *entry;
358     DWORD err;
359 
360     WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
361 
362     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
363         return err;
364 
365     if (!validate_service_name(lpServiceName))
366         return ERROR_INVALID_NAME;
367     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
368         return ERROR_INVALID_PARAMETER;
369 
370     if (lpPassword)
371         WINE_FIXME("Don't know how to add a password\n");   /* I always get ERROR_GEN_FAILURE */
372     if (lpDependencies)
373         WINE_FIXME("Dependencies not supported yet\n");
374 
375     err = service_create(lpServiceName, &entry);
376     if (err != ERROR_SUCCESS)
377         return err;
378     entry->ref_count = 1;
379     entry->config.dwServiceType = entry->status.dwServiceType = dwServiceType;
380     entry->config.dwStartType = dwStartType;
381     entry->config.dwErrorControl = dwErrorControl;
382     entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
383     entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
384     entry->config.lpServiceStartName = strdupW(lpServiceStartName);
385     entry->config.lpDisplayName = strdupW(lpDisplayName);
386 
387     if (lpdwTagId)      /* TODO: In most situations a non-NULL TagId will generate an ERROR_INVALID_PARAMETER. */
388         entry->config.dwTagId = *lpdwTagId;
389     else
390         entry->config.dwTagId = 0;
391 
392     /* other fields NULL*/
393 
394     if (!validate_service_config(entry))
395     {
396         WINE_ERR("Invalid data while trying to create service\n");
397         free_service_entry(entry);
398         return ERROR_INVALID_PARAMETER;
399     }
400 
401     scmdatabase_lock_exclusive(manager->db);
402 
403     if (scmdatabase_find_service(manager->db, lpServiceName))
404     {
405         scmdatabase_unlock(manager->db);
406         free_service_entry(entry);
407         return ERROR_SERVICE_EXISTS;
408     }
409 
410     if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
411     {
412         scmdatabase_unlock(manager->db);
413         free_service_entry(entry);
414         return ERROR_DUPLICATE_SERVICE_NAME;
415     }
416 
417     err = scmdatabase_add_service(manager->db, entry);
418     if (err != ERROR_SUCCESS)
419     {
420         scmdatabase_unlock(manager->db);
421         free_service_entry(entry);
422         return err;
423     }
424     scmdatabase_unlock(manager->db);
425 
426     return create_handle_for_service(entry, dwDesiredAccess, phService);
427 }
428 
429 DWORD svcctl_DeleteService(
430     SC_RPC_HANDLE hService)
431 {
432     struct sc_service_handle *service;
433     DWORD err;
434 
435     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
436         return err;
437 
438     scmdatabase_lock_exclusive(service->service_entry->db);
439     service_lock_exclusive(service->service_entry);
440 
441     if (!is_marked_for_delete(service->service_entry))
442         err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
443     else
444         err = ERROR_SERVICE_MARKED_FOR_DELETE;
445 
446     service_unlock(service->service_entry);
447     scmdatabase_unlock(service->service_entry->db);
448 
449     return err;
450 }
451 
452 DWORD svcctl_QueryServiceConfigW(
453         SC_RPC_HANDLE hService,
454         QUERY_SERVICE_CONFIGW *config)
455 {
456     struct sc_service_handle *service;
457     DWORD err;
458 
459     WINE_TRACE("(%p)\n", config);
460 
461     if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
462         return err;
463 
464     service_lock_shared(service->service_entry);
465     config->dwServiceType = service->service_entry->config.dwServiceType;
466     config->dwStartType = service->service_entry->config.dwStartType;
467     config->dwErrorControl = service->service_entry->config.dwErrorControl;
468     config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
469     config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
470     config->dwTagId = service->service_entry->config.dwTagId;
471     config->lpDependencies = NULL; /* TODO */
472     config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
473     config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
474     service_unlock(service->service_entry);
475 
476     return ERROR_SUCCESS;
477 }
478 
479 DWORD svcctl_ChangeServiceConfigW(
480         SC_RPC_HANDLE hService,
481         DWORD dwServiceType,
482         DWORD dwStartType,
483         DWORD dwErrorControl,
484         LPCWSTR lpBinaryPathName,
485         LPCWSTR lpLoadOrderGroup,
486         DWORD *lpdwTagId,
487         const BYTE *lpDependencies,
488         DWORD dwDependenciesSize,
489         LPCWSTR lpServiceStartName,
490         const BYTE *lpPassword,
491         DWORD dwPasswordSize,
492         LPCWSTR lpDisplayName)
493 {
494     struct service_entry new_entry, *entry;
495     struct sc_service_handle *service;
496     DWORD err;
497 
498     WINE_TRACE("\n");
499 
500     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
501         return err;
502 
503     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
504         return ERROR_INVALID_PARAMETER;
505 
506     /* first check if the new configuration is correct */
507     service_lock_exclusive(service->service_entry);
508 
509     if (is_marked_for_delete(service->service_entry))
510     {
511         service_unlock(service->service_entry);
512         return ERROR_SERVICE_MARKED_FOR_DELETE;
513     }
514 
515     if (lpDisplayName != NULL &&
516         (entry = scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName)) &&
517         (entry != service->service_entry))
518     {
519         service_unlock(service->service_entry);
520         return ERROR_DUPLICATE_SERVICE_NAME;
521     }
522 
523     new_entry = *service->service_entry;
524 
525     if (dwServiceType != SERVICE_NO_CHANGE)
526         new_entry.config.dwServiceType = dwServiceType;
527 
528     if (dwStartType != SERVICE_NO_CHANGE)
529         new_entry.config.dwStartType = dwStartType;
530 
531     if (dwErrorControl != SERVICE_NO_CHANGE)
532         new_entry.config.dwErrorControl = dwErrorControl;
533 
534     if (lpBinaryPathName != NULL)
535         new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
536 
537     if (lpLoadOrderGroup != NULL)
538         new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
539 
540     if (lpdwTagId != NULL)
541         WINE_FIXME("Changing tag id not supported\n");
542 
543     if (lpDependencies != NULL)
544         WINE_FIXME("Changing dependencies not supported\n");
545 
546     if (lpServiceStartName != NULL)
547         new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
548 
549     if (lpPassword != NULL)
550         WINE_FIXME("Setting password not supported\n");
551 
552     if (lpDisplayName != NULL)
553         new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
554 
555     if (!validate_service_config(&new_entry))
556     {
557         WINE_ERR("The configuration after the change wouldn't be valid\n");
558         service_unlock(service->service_entry);
559         return ERROR_INVALID_PARAMETER;
560     }
561 
562     /* configuration OK. The strings needs to be duplicated */
563     if (lpBinaryPathName != NULL)
564         new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
565 
566     if (lpLoadOrderGroup != NULL)
567         new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
568 
569     if (lpServiceStartName != NULL)
570         new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
571 
572     if (lpDisplayName != NULL)
573         new_entry.config.lpDisplayName = strdupW(lpDisplayName);
574 
575     /* try to save to Registry, commit or rollback depending on success */
576     err = save_service_config(&new_entry);
577     if (ERROR_SUCCESS == err)
578     {
579         free_config_strings(&service->service_entry->config,&new_entry.config);
580         *service->service_entry = new_entry;
581     }
582     else free_config_strings(&new_entry.config,&service->service_entry->config);
583     service_unlock(service->service_entry);
584 
585     return err;
586 }
587 
588 DWORD svcctl_SetServiceStatus(
589     SC_RPC_HANDLE hServiceStatus,
590     LPSERVICE_STATUS lpServiceStatus)
591 {
592     struct sc_service_handle *service;
593     DWORD err;
594 
595     WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
596 
597     if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
598         return err;
599 
600     service_lock_exclusive(service->service_entry);
601     /* FIXME: be a bit more discriminant about what parts of the status we set
602      * and check that fields are valid */
603     service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
604     service->service_entry->status.dwCurrentState = lpServiceStatus->dwCurrentState;
605     service->service_entry->status.dwControlsAccepted = lpServiceStatus->dwControlsAccepted;
606     service->service_entry->status.dwWin32ExitCode = lpServiceStatus->dwWin32ExitCode;
607     service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
608     service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
609     service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
610     service_unlock(service->service_entry);
611 
612     if (service->service_entry->status_changed_event)
613         SetEvent(service->service_entry->status_changed_event);
614 
615     return ERROR_SUCCESS;
616 }
617 
618 DWORD svcctl_ChangeServiceConfig2W( SC_RPC_HANDLE hService, DWORD level, SERVICE_CONFIG2W *config )
619 {
620     struct sc_service_handle *service;
621     DWORD err;
622 
623     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
624         return err;
625 
626     switch (level)
627     {
628     case SERVICE_CONFIG_DESCRIPTION:
629         {
630             WCHAR *descr = NULL;
631 
632             if (config->descr.lpDescription[0])
633             {
634                 if (!(descr = strdupW( config->descr.lpDescription )))
635                     return ERROR_NOT_ENOUGH_MEMORY;
636             }
637 
638             WINE_TRACE( "changing service %p descr to %s\n", service, wine_dbgstr_w(descr) );
639             service_lock_exclusive( service->service_entry );
640             HeapFree( GetProcessHeap(), 0, service->service_entry->description );
641             service->service_entry->description = descr;
642             save_service_config( service->service_entry );
643             service_unlock( service->service_entry );
644         }
645         break;
646     case SERVICE_CONFIG_FAILURE_ACTIONS:
647         WINE_FIXME( "SERVICE_CONFIG_FAILURE_ACTIONS not implemented: period %u msg %s cmd %s\n",
648                     config->actions.dwResetPeriod,
649                     wine_dbgstr_w(config->actions.lpRebootMsg),
650                     wine_dbgstr_w(config->actions.lpCommand) );
651         break;
652     default:
653         WINE_FIXME("level %u not implemented\n", level);
654         err = ERROR_INVALID_LEVEL;
655         break;
656     }
657     return err;
658 }
659 
660 DWORD svcctl_QueryServiceConfig2W( SC_RPC_HANDLE hService, DWORD level,
661                                    BYTE *buffer, DWORD size, LPDWORD needed )
662 {
663     struct sc_service_handle *service;
664     DWORD err;
665 
666     memset(buffer, 0, size);
667 
668     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
669         return err;
670 
671     switch (level)
672     {
673     case SERVICE_CONFIG_DESCRIPTION:
674         {
675             SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
676 
677             service_lock_shared(service->service_entry);
678             *needed = sizeof(*descr);
679             if (service->service_entry->description)
680                 *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR);
681             if (size >= *needed)
682             {
683                 if (service->service_entry->description)
684                 {
685                     /* store a buffer offset instead of a pointer */
686                     descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer);
687                     strcpyW( (WCHAR *)(descr + 1), service->service_entry->description );
688                 }
689                 else descr->lpDescription = NULL;
690             }
691             else err = ERROR_INSUFFICIENT_BUFFER;
692             service_unlock(service->service_entry);
693         }
694         break;
695 
696     default:
697         WINE_FIXME("level %u not implemented\n", level);
698         err = ERROR_INVALID_LEVEL;
699         break;
700     }
701     return err;
702 }
703 
704 DWORD svcctl_QueryServiceStatusEx(
705     SC_RPC_HANDLE hService,
706     SC_STATUS_TYPE InfoLevel,
707     BYTE *lpBuffer,
708     DWORD cbBufSize,
709     LPDWORD pcbBytesNeeded)
710 {
711     struct sc_service_handle *service;
712     DWORD err;
713     LPSERVICE_STATUS_PROCESS pSvcStatusData;
714 
715     memset(lpBuffer, 0, cbBufSize);
716 
717     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
718         return err;
719 
720     if (InfoLevel != SC_STATUS_PROCESS_INFO)
721         return ERROR_INVALID_LEVEL;
722 
723     pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
724     if (pSvcStatusData == NULL)
725         return ERROR_INVALID_PARAMETER;
726 
727     if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
728     {
729         if( pcbBytesNeeded != NULL)
730             *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
731 
732         return ERROR_INSUFFICIENT_BUFFER;
733     }
734 
735     service_lock_shared(service->service_entry);
736 
737     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
738     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
739     pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
740     pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
741     pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
742     pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
743     pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
744     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
745     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
746 
747     service_unlock(service->service_entry);
748 
749     return ERROR_SUCCESS;
750 }
751 
752 /******************************************************************************
753  * service_accepts_control
754  */
755 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
756 {
757     DWORD a = service->status.dwControlsAccepted;
758 
759     switch (dwControl)
760     {
761     case SERVICE_CONTROL_INTERROGATE:
762         return TRUE;
763     case SERVICE_CONTROL_STOP:
764         if (a&SERVICE_ACCEPT_STOP)
765             return TRUE;
766         break;
767     case SERVICE_CONTROL_SHUTDOWN:
768         if (a&SERVICE_ACCEPT_SHUTDOWN)
769             return TRUE;
770         break;
771     case SERVICE_CONTROL_PAUSE:
772     case SERVICE_CONTROL_CONTINUE:
773         if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
774             return TRUE;
775         break;
776     case SERVICE_CONTROL_PARAMCHANGE:
777         if (a&SERVICE_ACCEPT_PARAMCHANGE)
778             return TRUE;
779         break;
780     case SERVICE_CONTROL_NETBINDADD:
781     case SERVICE_CONTROL_NETBINDREMOVE:
782     case SERVICE_CONTROL_NETBINDENABLE:
783     case SERVICE_CONTROL_NETBINDDISABLE:
784         if (a&SERVICE_ACCEPT_NETBINDCHANGE)
785             return TRUE;
786     case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
787         if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
788             return TRUE;
789         break;
790     case SERVICE_CONTROL_POWEREVENT:
791         if (a&SERVICE_ACCEPT_POWEREVENT)
792             return TRUE;
793         break;
794     case SERVICE_CONTROL_SESSIONCHANGE:
795         if (a&SERVICE_ACCEPT_SESSIONCHANGE)
796             return TRUE;
797         break;
798     }
799     return FALSE;
800 }
801 
802 /******************************************************************************
803  * service_send_control
804  */
805 static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
806 {
807     service_start_info *ssi;
808     DWORD len, count = 0;
809     BOOL r;
810 
811     /* calculate how much space we need to send the startup info */
812     len = strlenW(service->name) + 1;
813 
814     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
815     ssi->cmd = WINESERV_SENDCONTROL;
816     ssi->control = dwControl;
817     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
818     ssi->name_size = strlenW(service->name) + 1;
819     strcpyW( ssi->data, service->name );
820 
821     r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL);
822     if (!r || count != ssi->total_size)
823     {
824         WINE_ERR("service protocol error - failed to write pipe!\n");
825         return r;
826     }
827     r = ReadFile(pipe, result, sizeof *result, &count, NULL);
828     if (!r || count != sizeof *result)
829         WINE_ERR("service protocol error - failed to read pipe "
830             "r = %d  count = %d!\n", r, count);
831     return r;
832 }
833 
834 DWORD svcctl_StartServiceW(
835     SC_RPC_HANDLE hService,
836     DWORD dwNumServiceArgs,
837     LPCWSTR *lpServiceArgVectors)
838 {
839     struct sc_service_handle *service;
840     DWORD err;
841 
842     WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
843 
844     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
845         return err;
846 
847     err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
848 
849     return err;
850 }
851 
852 DWORD svcctl_ControlService(
853     SC_RPC_HANDLE hService,
854     DWORD dwControl,
855     SERVICE_STATUS *lpServiceStatus)
856 {
857     DWORD access_required;
858     struct sc_service_handle *service;
859     DWORD err;
860     BOOL ret;
861     HANDLE control_mutex;
862     HANDLE control_pipe;
863 
864     WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
865 
866     switch (dwControl)
867     {
868     case SERVICE_CONTROL_CONTINUE:
869     case SERVICE_CONTROL_NETBINDADD:
870     case SERVICE_CONTROL_NETBINDDISABLE:
871     case SERVICE_CONTROL_NETBINDENABLE:
872     case SERVICE_CONTROL_NETBINDREMOVE:
873     case SERVICE_CONTROL_PARAMCHANGE:
874     case SERVICE_CONTROL_PAUSE:
875         access_required = SERVICE_PAUSE_CONTINUE;
876         break;
877     case SERVICE_CONTROL_INTERROGATE:
878         access_required = SERVICE_INTERROGATE;
879         break;
880     case SERVICE_CONTROL_STOP:
881         access_required = SERVICE_STOP;
882         break;
883     default:
884         if (dwControl >= 128 && dwControl <= 255)
885             access_required = SERVICE_USER_DEFINED_CONTROL;
886         else
887             return ERROR_INVALID_PARAMETER;
888     }
889 
890     if ((err = validate_service_handle(hService, access_required, &service)) != 0)
891         return err;
892 
893     service_lock_exclusive(service->service_entry);
894 
895     if (lpServiceStatus)
896     {
897         lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
898         lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
899         lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
900         lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
901         lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
902         lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
903         lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
904     }
905 
906     if (!service_accepts_control(service->service_entry, dwControl))
907     {
908         service_unlock(service->service_entry);
909         return ERROR_INVALID_SERVICE_CONTROL;
910     }
911 
912     switch (service->service_entry->status.dwCurrentState)
913     {
914     case SERVICE_STOPPED:
915         service_unlock(service->service_entry);
916         return ERROR_SERVICE_NOT_ACTIVE;
917     case SERVICE_START_PENDING:
918         if (dwControl==SERVICE_CONTROL_STOP)
919             break;
920         /* fall thru */
921     case SERVICE_STOP_PENDING:
922         service_unlock(service->service_entry);
923         return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
924     }
925 
926     /* prevent races by caching these variables and clearing them on
927      * stop here instead of outside the services lock */
928     control_mutex = service->service_entry->control_mutex;
929     control_pipe = service->service_entry->control_pipe;
930     if (dwControl == SERVICE_CONTROL_STOP)
931     {
932         service->service_entry->control_mutex = NULL;
933         service->service_entry->control_pipe = INVALID_HANDLE_VALUE;
934     }
935 
936     service_unlock(service->service_entry);
937 
938     ret = WaitForSingleObject(control_mutex, 30000);
939     if (ret == WAIT_OBJECT_0)
940     {
941         DWORD result = ERROR_SUCCESS;
942 
943         ret = service_send_control(service->service_entry, control_pipe, dwControl, &result);
944 
945         if (dwControl == SERVICE_CONTROL_STOP)
946         {
947             CloseHandle(control_mutex);
948             CloseHandle(control_pipe);
949         }
950         else
951             ReleaseMutex(control_mutex);
952 
953         return result;
954     }
955     else
956     {
957         if (dwControl == SERVICE_CONTROL_STOP)
958         {
959             CloseHandle(control_mutex);
960             CloseHandle(control_pipe);
961         }
962         return ERROR_SERVICE_REQUEST_TIMEOUT;
963     }
964 }
965 
966 DWORD svcctl_CloseServiceHandle(
967     SC_RPC_HANDLE *handle)
968 {
969     WINE_TRACE("(&%p)\n", *handle);
970 
971     SC_RPC_HANDLE_destroy(*handle);
972     *handle = NULL;
973 
974     return ERROR_SUCCESS;
975 }
976 
977 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
978 {
979     struct sc_lock *lock = hLock;
980     scmdatabase_unlock_startup(lock->db);
981     HeapFree(GetProcessHeap(), 0, lock);
982 }
983 
984 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
985 {
986     SC_RPC_LOCK_destroy(hLock);
987 }
988 
989 DWORD svcctl_LockServiceDatabase(
990     SC_RPC_HANDLE hSCManager,
991     SC_RPC_LOCK *phLock)
992 {
993     struct sc_manager_handle *manager;
994     struct sc_lock *lock;
995     DWORD err;
996 
997     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
998 
999     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
1000         return err;
1001 
1002     err = scmdatabase_lock_startup(manager->db);
1003     if (err != ERROR_SUCCESS)
1004         return err;
1005 
1006     lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
1007     if (!lock)
1008     {
1009         scmdatabase_unlock_startup(manager->db);
1010         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
1011     }
1012 
1013     lock->db = manager->db;
1014     *phLock = lock;
1015 
1016     return ERROR_SUCCESS;
1017 }
1018 
1019 DWORD svcctl_UnlockServiceDatabase(
1020     SC_RPC_LOCK *phLock)
1021 {
1022     WINE_TRACE("(&%p)\n", *phLock);
1023 
1024     SC_RPC_LOCK_destroy(*phLock);
1025     *phLock = NULL;
1026 
1027     return ERROR_SUCCESS;
1028 }
1029 
1030 DWORD svcctl_QueryServiceObjectSecurity(
1031     void)
1032 {
1033     WINE_FIXME("\n");
1034     return ERROR_CALL_NOT_IMPLEMENTED;
1035 }
1036 
1037 DWORD svcctl_SetServiceObjectSecurity(
1038     void)
1039 {
1040     WINE_FIXME("\n");
1041     return ERROR_CALL_NOT_IMPLEMENTED;
1042 }
1043 
1044 DWORD svcctl_QueryServiceStatus(
1045     void)
1046 {
1047     WINE_FIXME("\n");
1048     return ERROR_CALL_NOT_IMPLEMENTED;
1049 }
1050 
1051 
1052 DWORD svcctl_NotifyBootConfigStatus(
1053     void)
1054 {
1055     WINE_FIXME("\n");
1056     return ERROR_CALL_NOT_IMPLEMENTED;
1057 }
1058 
1059 DWORD svcctl_SCSetServiceBitsW(
1060     void)
1061 {
1062     WINE_FIXME("\n");
1063     return ERROR_CALL_NOT_IMPLEMENTED;
1064 }
1065 
1066 
1067 DWORD svcctl_EnumDependentServicesW(
1068     void)
1069 {
1070     WINE_FIXME("\n");
1071     return ERROR_CALL_NOT_IMPLEMENTED;
1072 }
1073 
1074 DWORD svcctl_EnumServicesStatusW(
1075     void)
1076 {
1077     WINE_FIXME("\n");
1078     return ERROR_CALL_NOT_IMPLEMENTED;
1079 }
1080 
1081 
1082 DWORD svcctl_QueryServiceLockStatusW(
1083     void)
1084 {
1085     WINE_FIXME("\n");
1086     return ERROR_CALL_NOT_IMPLEMENTED;
1087 }
1088 
1089 DWORD svcctl_SCSetServiceBitsA(
1090     void)
1091 {
1092     WINE_FIXME("\n");
1093     return ERROR_CALL_NOT_IMPLEMENTED;
1094 }
1095 
1096 DWORD svcctl_ChangeServiceConfigA(
1097     void)
1098 {
1099     WINE_FIXME("\n");
1100     return ERROR_CALL_NOT_IMPLEMENTED;
1101 }
1102 
1103 DWORD svcctl_CreateServiceA(
1104     void)
1105 {
1106     WINE_FIXME("\n");
1107     return ERROR_CALL_NOT_IMPLEMENTED;
1108 }
1109 
1110 DWORD svcctl_EnumDependentServicesA(
1111     void)
1112 {
1113     WINE_FIXME("\n");
1114     return ERROR_CALL_NOT_IMPLEMENTED;
1115 }
1116 
1117 DWORD svcctl_EnumServicesStatusA(
1118     void)
1119 {
1120     WINE_FIXME("\n");
1121     return ERROR_CALL_NOT_IMPLEMENTED;
1122 }
1123 
1124 DWORD svcctl_OpenSCManagerA(
1125     void)
1126 {
1127     WINE_FIXME("\n");
1128     return ERROR_CALL_NOT_IMPLEMENTED;
1129 }
1130 
1131 DWORD svcctl_OpenServiceA(
1132     void)
1133 {
1134     WINE_FIXME("\n");
1135     return ERROR_CALL_NOT_IMPLEMENTED;
1136 }
1137 
1138 DWORD svcctl_QueryServiceConfigA(
1139     void)
1140 {
1141     WINE_FIXME("\n");
1142     return ERROR_CALL_NOT_IMPLEMENTED;
1143 }
1144 
1145 DWORD svcctl_QueryServiceLockStatusA(
1146     void)
1147 {
1148     WINE_FIXME("\n");
1149     return ERROR_CALL_NOT_IMPLEMENTED;
1150 }
1151 
1152 DWORD svcctl_StartServiceA(
1153     void)
1154 {
1155     WINE_FIXME("\n");
1156     return ERROR_CALL_NOT_IMPLEMENTED;
1157 }
1158 
1159 DWORD svcctl_GetServiceDisplayNameA(
1160     void)
1161 {
1162     WINE_FIXME("\n");
1163     return ERROR_CALL_NOT_IMPLEMENTED;
1164 }
1165 
1166 DWORD svcctl_GetServiceKeyNameA(
1167     void)
1168 {
1169     WINE_FIXME("\n");
1170     return ERROR_CALL_NOT_IMPLEMENTED;
1171 }
1172 
1173 DWORD svcctl_GetCurrentGroupStateW(
1174     void)
1175 {
1176     WINE_FIXME("\n");
1177     return ERROR_CALL_NOT_IMPLEMENTED;
1178 }
1179 
1180 DWORD svcctl_EnumServiceGroupW(
1181     void)
1182 {
1183     WINE_FIXME("\n");
1184     return ERROR_CALL_NOT_IMPLEMENTED;
1185 }
1186 
1187 DWORD svcctl_ChangeServiceConfig2A(
1188     void)
1189 {
1190     WINE_FIXME("\n");
1191     return ERROR_CALL_NOT_IMPLEMENTED;
1192 }
1193 
1194 DWORD svcctl_QueryServiceConfig2A(
1195     void)
1196 {
1197     WINE_FIXME("\n");
1198     return ERROR_CALL_NOT_IMPLEMENTED;
1199 }
1200 
1201 
1202 DWORD RPC_Init(void)
1203 {
1204     WCHAR transport[] = SVCCTL_TRANSPORT;
1205     WCHAR endpoint[] = SVCCTL_ENDPOINT;
1206     DWORD err;
1207 
1208     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1209     {
1210         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1211         return err;
1212     }
1213 
1214     if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1215     {
1216         WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1217         return err;
1218     }
1219 
1220     if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1221     {
1222         WINE_ERR("RpcServerListen failed with error %u\n", err);
1223         return err;
1224     }
1225     return ERROR_SUCCESS;
1226 }
1227 
1228 DWORD RPC_MainLoop(void)
1229 {
1230     DWORD err;
1231     HANDLE hExitEvent = __wine_make_process_system();
1232 
1233     SetEvent(g_hStartedEvent);
1234 
1235     WINE_TRACE("Entered main loop\n");
1236 
1237     do
1238     {
1239         err = WaitForSingleObjectEx(hExitEvent, INFINITE, TRUE);
1240         WINE_TRACE("Wait returned %d\n", err);
1241     } while (err != WAIT_OBJECT_0);
1242 
1243     WINE_TRACE("Object signaled - wine shutdown\n");
1244     CloseHandle(hExitEvent);
1245     return ERROR_SUCCESS;
1246 }
1247 
1248 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
1249 {
1250     SC_RPC_HANDLE_destroy(handle);
1251 }
1252 
1253 void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
1254 {
1255     return HeapAlloc(GetProcessHeap(), 0, len);
1256 }
1257 
1258 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
1259 {
1260     HeapFree(GetProcessHeap(), 0, ptr);
1261 }
1262 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.