master
h 706 lines 27.2 KB
Raw
1 ///////////////////////////////////////////////////////////////////////////////
2 // //
3 // Copyright (c) Microsoft Corporation. All rights reserved. //
4 // comservicehelper.h //
5 // //
6 // Provides a template class to handle a Service entry point. //
7 // //
8 ///////////////////////////////////////////////////////////////////////////////
9
10 #pragma once
11
12 #include <sddl.h>
13 #include <ctxtcall.h>
14 #include <wil\result.h>
15 #include <wil\resource.h>
16
17 namespace Windows { namespace Internal {
18
19 struct ServerDescriptor final
20 {
21 const wchar_t* ServerName = nullptr;
22 };
23
24 template <typename TBase>
25 struct ModuleServerDescriptor
26 {
27 constexpr static const ServerDescriptor Create()
28 {
29 constexpr const ServerDescriptor serverDescriptor = {TBase::ServerName};
30 return serverDescriptor;
31 }
32 };
33
34 struct DefaultServerDescriptor final
35 {
36 };
37
38 class ServiceModuleBase
39 {
40 public:
41 ServiceModuleBase()
42 {
43 }
44
45 ~ServiceModuleBase()
46 {
47 }
48
49 template <typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor = DefaultServerDescriptor>
50 HRESULT Initialize(_In_ boolean ownProcess, _In_ boolean addRefModule, _In_ boolean hasDedicatedThread = true, _In_ HANDLE stopEvent = nullptr)
51 {
52 auto uninitializeOnFailure = wil::scope_exit([&]() { Uninitialize(); });
53
54 if (hasDedicatedThread)
55 {
56 // If the ServiceModule is being initialized on its own dedicated thread (i.e. the thread hangs around until it's
57 // time to call SvcModuleBase::Uninitialize) then initialize COM for this thread.
58 m_hrMtaInitialized = Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);
59 }
60 else
61 {
62 // Otherwise, take a reference on the MTA apartment.
63 m_hrMtaInitialized = CoIncrementMTAUsage(&m_mtaUsageCookie);
64 }
65 RETURN_IF_FAILED(m_hrMtaInitialized);
66
67 __if_exists(TServerDescriptor::Create)
68 {
69 m_serverDescriptor = TServerDescriptor::Create();
70 }
71
72 if (ownProcess)
73 {
74 RETURN_IF_FAILED(InitializeSecurity<TSecurityPolicy>());
75 }
76
77 // Tell COM how to mask fatal exceptions.
78 if (ownProcess)
79 {
80 wil::com_ptr<IGlobalOptions> pIGLB;
81 RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIGLB)));
82 RETURN_IF_FAILED(pIGLB->Set(COMGLB_EXCEPTION_HANDLING, TExceptionPolicy));
83 }
84
85 // SubInitialize must be called before IncrementObjectCount or the ContextCallback.
86 // The ContextCallback will register the COM objects, and as soon as that happens, incoming activations may arrive
87 // which will call IncrementObjectCount.
88 RETURN_IF_FAILED(SubInitialize());
89
90 // Add the extra module reference to prevent shutdown before the ContextCallback because once we register the COM objects,
91 // an object may be released and drop the module reference count to zero if the extra reference isn't added yet.
92 if (addRefModule)
93 {
94 IncrementObjectCount();
95 m_addedModuleReference = true;
96 }
97
98 RETURN_IF_FAILED(CoCreateInstance(CLSID_ContextSwitcher, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_icc)));
99
100 RETURN_IF_FAILED(m_icc->ContextCallback(
101 &Windows::Internal::ServiceModuleBase::ConnectCallbackThunk, reinterpret_cast<ComCallData*>(this), IID_IContextCallback, 5, nullptr));
102
103 uninitializeOnFailure.release();
104 return S_OK;
105 }
106
107 HRESULT Uninitialize()
108 {
109 if (m_icc)
110 {
111 m_icc->ContextCallback(
112 &ServiceModuleBase::DisconnectCallbackThunk, reinterpret_cast<ComCallData*>(this), IID_IContextCallback, 5, nullptr);
113 m_icc = nullptr;
114 }
115
116 if (m_addedModuleReference)
117 {
118 DecrementObjectCount();
119 m_addedModuleReference = false;
120 }
121
122 if (SUCCEEDED(m_hrMtaInitialized))
123 {
124 if (m_mtaUsageCookie)
125 {
126 m_mtaUsageCookie.reset();
127 }
128 else
129 {
130 Windows::Foundation::Uninitialize();
131 }
132
133 m_hrMtaInitialized = E_FAIL;
134 }
135 return S_OK;
136 }
137
138 virtual HRESULT ConnectCallback() = 0;
139
140 virtual HRESULT DisconnectCallback() = 0;
141
142 STDMETHOD_(ULONG, IncrementObjectCount()) = 0;
143
144 STDMETHOD_(ULONG, DecrementObjectCount()) = 0;
145
146 static HRESULT __stdcall ConnectCallbackThunk(_In_ ComCallData* pv)
147 {
148 ServiceModuleBase* pThis = reinterpret_cast<ServiceModuleBase*>(pv);
149 return pThis->ConnectCallback();
150 }
151
152 static HRESULT __stdcall DisconnectCallbackThunk(_In_ ComCallData* pv)
153 {
154 ServiceModuleBase* pThis = reinterpret_cast<ServiceModuleBase*>(pv);
155 return pThis->DisconnectCallback();
156 }
157
158 public:
159 //
160 // These are not fully-fledged policy objects, but they all rely on SDDL instead.
161 //
162 // Useful references:
163 //
164 // Access Control Lists for COM
165 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms693364(v=vs.85).aspx
166 //
167 // Security Descriptor String Format
168 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx
169 //
170 // ACE Strings
171 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa374928(v=vs.85).aspx
172 //
173 struct SecurityPolicyEveryoneLocal
174 {
175 static LPCWSTR GetSDDLText()
176 {
177 //
178 // The current one explicitly allows Everyone and App Packages for local clients only.
179 //
180 // O: = Owner
181 // PS = principal self
182 // G: = Group
183 // BU = Built-in users
184 // D: = DACL
185 // A = access allowed
186 // 0B = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL
187 // AC = App Packages
188 // WD = everyone
189 // S: = SACL
190 // ML = Mandatory Label
191 // NX = NO_EXECUTE_UP
192 // LW = Low Integrity
193 //
194 return L"O:PSG:BUD:(A;;0xB;;;AC)(A;;0xB;;;WD)S:(ML;;NX;;;LW)";
195 }
196 };
197
198 struct SecurityPolicyEveryoneLocalAndRemote
199 {
200 static LPCWSTR GetSDDLText()
201 {
202 //
203 // The current one explicitly allows Everyone and App Packages for local and remote clients.
204 //
205 // O: = Owner
206 // PS = principal self
207 // G: = Group
208 // BU = Built-in users
209 // D: = DACL
210 // A = access allowed
211 // 1F = COM_RIGHTS_EXECUTE | COM_RIGHTS_EXECUTE_LOCAL | COM_RIGHTS_ACTIVATE_LOCAL | COM_RIGHTS_EXECUTE_REMOTE | COM_RIGHTS_ACTIVATE_REMOTE
212 // AC = App Packages
213 // WD = everyone
214 // S: = SACL
215 // ML = Mandatory Label
216 // NX = NO_EXECUTE_UP
217 // LW = Low Integrity
218 //
219 return L"O:PSG:BUD:(A;;0x1F;;;AC)(A;;0x1F;;;WD)S:(ML;;NX;;;LW)";
220 }
221 };
222
223 protected:
224 // _module is a reference, so the compiler can't generate these. Hide them.
225 ServiceModuleBase(const ServiceModuleBase&);
226 ServiceModuleBase& operator=(const ServiceModuleBase&);
227
228 // Used by derived classes to initialize any necessary state
229 virtual HRESULT SubInitialize()
230 {
231 return S_OK;
232 }
233
234 template <typename TSecurityPolicy>
235 HRESULT InitializeSecurity()
236 {
237 PACL pDacl = nullptr, pSacl = nullptr;
238 PSID pOwner = nullptr, pPrimaryGroup = nullptr;
239 PSECURITY_DESCRIPTOR pSDRelative = nullptr, pSDAbsolute = nullptr;
240 DWORD cbSDAbsolute = 0, cbDacl = 0, cbSacl = 0, cbOwner = 0, cbPrimaryGroup = 0;
241
242 auto cleanup = wil::scope_exit([&] {
243 HeapFree(GetProcessHeap(), 0, pDacl);
244 HeapFree(GetProcessHeap(), 0, pSacl);
245 HeapFree(GetProcessHeap(), 0, pOwner);
246 HeapFree(GetProcessHeap(), 0, pPrimaryGroup);
247 HeapFree(GetProcessHeap(), 0, pSDAbsolute);
248 HeapFree(GetProcessHeap(), 0, pSDRelative);
249 });
250
251 // The following call returns a self-relative security descriptor...
252 RETURN_IF_WIN32_BOOL_FALSE(ConvertStringSecurityDescriptorToSecurityDescriptor(
253 TSecurityPolicy::GetSDDLText(), SDDL_REVISION_1, &pSDRelative, nullptr));
254
255 // ...before we pass it to CoInitializeSecurity, we need to make it absolute. We call MakeAbsoluteSD once to find out how large our buffers need to be...
256 RETURN_LAST_ERROR_IF(
257 MakeAbsoluteSD(pSDRelative, nullptr, &cbSDAbsolute, nullptr, &cbDacl, nullptr, &cbSacl, nullptr, &cbOwner, nullptr, &cbPrimaryGroup) ||
258 ERROR_INSUFFICIENT_BUFFER != GetLastError());
259
260 // Then we allocate the buffers...
261 pSDAbsolute = reinterpret_cast<PSECURITY_DESCRIPTOR>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSDAbsolute));
262 RETURN_IF_NULL_ALLOC(pSDAbsolute);
263
264 pDacl = reinterpret_cast<PACL>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDacl));
265 RETURN_IF_NULL_ALLOC(pDacl);
266
267 pSacl = reinterpret_cast<PACL>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbSacl));
268 RETURN_IF_NULL_ALLOC(pSacl);
269
270 pOwner = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbOwner));
271 RETURN_IF_NULL_ALLOC(pOwner);
272
273 pPrimaryGroup = reinterpret_cast<PSID>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbPrimaryGroup));
274 RETURN_IF_NULL_ALLOC(pPrimaryGroup);
275
276 // ...then we call MakeAbsoluteSD again with the buffers we just allocated
277 RETURN_IF_WIN32_BOOL_FALSE(MakeAbsoluteSD(
278 pSDRelative, pSDAbsolute, &cbSDAbsolute, pDacl, &cbDacl, pSacl, &cbSacl, pOwner, &cbOwner, pPrimaryGroup, &cbPrimaryGroup));
279
280 // ...and now we can call CoInitializeSecurity
281 RETURN_IF_FAILED(CoInitializeSecurity(
282 pSDAbsolute, -1, nullptr, nullptr, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, nullptr));
283
284 return S_OK;
285 }
286
287 // Declare before any COM member variables in this object
288 wil::unique_mta_usage_cookie m_mtaUsageCookie;
289
290 // Result of initializing the COM apartment
291 HRESULT m_hrMtaInitialized = E_FAIL;
292
293 // Track whether we added an extra module reference
294 bool m_addedModuleReference = false;
295
296 // COM callback object to support unloading shared-process services
297 wil::com_ptr<IContextCallback> m_icc;
298
299 // COM Server descriptor
300 ServerDescriptor m_serverDescriptor{};
301 };
302
303 class ServiceModule : public ServiceModuleBase, public Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>
304 {
305 public:
306 STDMETHOD_(ULONG, IncrementObjectCount()) override
307 {
308 return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>::IncrementObjectCount();
309 }
310
311 STDMETHOD_(ULONG, DecrementObjectCount()) override
312 {
313 return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc, ServiceModule>::DecrementObjectCount();
314 }
315
316 HRESULT ConnectCallback() override
317 {
318 return __super::RegisterObjects(m_serverDescriptor.ServerName);
319 }
320
321 HRESULT DisconnectCallback() override
322 {
323 __super::UnregisterObjects(m_serverDescriptor.ServerName);
324 return CoDisconnectContext(INFINITE);
325 }
326 };
327
328 enum LastObjectReleaseBehavior
329 {
330 ShutdownAfterLastObjectReleased = 1,
331 ContinueRunningWithNoObjects = 2,
332 };
333
334 template <
335 typename TBase,
336 LastObjectReleaseBehavior TLastObjectReleaseBehavior = ShutdownAfterLastObjectReleased,
337 typename TSecurityPolicy = ServiceModule::SecurityPolicyEveryoneLocal,
338 GLOBALOPT_EH_VALUES TExceptionPolicy = COMGLB_EXCEPTION_DONOT_HANDLE_ANY,
339 typename TServerDescriptor = DefaultServerDescriptor>
340 class Service
341 {
342 public:
343 Service()
344 {
345 _serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
346 _serviceStatus.dwCurrentState = SERVICE_RUNNING;
347 _serviceStatus.dwWin32ExitCode = NO_ERROR;
348 }
349
350 ~Service()
351 {
352 __if_exists(TBase::OnLowPowerModeChanged)
353 {
354 if (_powerHandle != nullptr)
355 {
356 PowerSettingUnregisterNotification(_powerHandle);
357 _powerHandle = nullptr;
358 }
359 }
360
361 if (_stopEvent != nullptr)
362 {
363 CloseHandle(_stopEvent);
364 _stopEvent = nullptr;
365 }
366 }
367
368 // Runs the main function for a service that lives in its own process.
369 static HRESULT ProcessMain()
370 {
371 const SERVICE_TABLE_ENTRY DispatchTable[] = {
372 {const_cast<LPWSTR>(L""),
373 (LPSERVICE_MAIN_FUNCTION)&Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy>::SvcMain},
374 {nullptr, nullptr}};
375
376 RETURN_IF_WIN32_BOOL_FALSE(StartServiceCtrlDispatcher(DispatchTable));
377
378 return s_LastServiceMainHR;
379 }
380
381 // Runs the service itself. Only necessary when ProcessMain isn't used.
382 static void ServiceMainSharedProcess()
383 {
384 TBase instance;
385 s_LastServiceMainHR = instance.RunServiceMain(false);
386 }
387
388 HRESULT RunServiceMain(_In_ boolean fOwnProcess)
389 {
390 __if_exists(TBase::ServiceStopped)
391 {
392 _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_STOP;
393 }
394
395 __if_exists(TBase::OnSystemShutdown)
396 {
397 _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
398 }
399
400 __if_exists(TBase::OnSessionChanged)
401 {
402 _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
403 }
404
405 ServiceModuleBase* pModule = nullptr;
406 HRESULT hr = [&]() {
407 // The service handle need not be closed.
408 _serviceStatusHandle = RegisterServiceCtrlHandlerEx(TBase::GetName(), &Service::HandlerExStatic, this);
409 RETURN_LAST_ERROR_IF(_serviceStatusHandle == 0);
410
411 _stopEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
412 RETURN_LAST_ERROR_IF(_stopEvent == nullptr);
413
414 __if_exists(TBase::OnServiceStarting)
415 {
416 RETURN_IF_FAILED(reinterpret_cast<TBase*>(this)->OnServiceStarting());
417 }
418
419 if (fOwnProcess)
420 {
421 pModule = &(ServiceModule::Create(this, GetModuleCallback<TLastObjectReleaseBehavior>()));
422 }
423 else
424 {
425 RETURN_HR(E_NOTIMPL);
426 }
427
428 constexpr bool addModuleReference = (TLastObjectReleaseBehavior == ContinueRunningWithNoObjects);
429
430 RETURN_IF_FAILED((pModule->Initialize<TSecurityPolicy, TExceptionPolicy, TServerDescriptor>(
431 fOwnProcess, addModuleReference, true /*hasDedicatedThread*/, _stopEvent)));
432
433 RETURN_IF_FAILED(hr = reinterpret_cast<TBase*>(this)->ServiceStarted());
434 auto serviceStopped = wil::scope_exit([&] {
435 __if_exists(TBase::ServiceStopped)
436 {
437 reinterpret_cast<TBase*>(this)->ServiceStopped();
438 }
439 });
440
441 __if_exists(TBase::OnLowPowerModeChanged)
442 {
443 RETURN_IF_WIN32_ERROR(PowerSettingRegisterNotification(
444 &GUID_LOW_POWER_EPOCH_PRV, DEVICE_NOTIFY_SERVICE_HANDLE, _serviceStatusHandle, &_powerHandle));
445 }
446
447 __if_exists(TBase::OnLowPowerModeChanged)
448 {
449 _serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT;
450 }
451
452 ReportCurrentStatus();
453 WaitForSingleObject(_stopEvent, INFINITE);
454
455 // The service is stopping now.
456 serviceStopped.reset();
457
458 __if_exists(TBase::OnLowPowerModeChanged)
459 {
460 if (_powerHandle != nullptr)
461 {
462 PowerSettingUnregisterNotification(_powerHandle);
463 _powerHandle = nullptr;
464 }
465 }
466
467 return S_OK;
468 }();
469
470 //
471 // See http://blogs.msdn.com/b/oldnewthing/archive/2006/11/03/942851.aspx for
472 // a discussion on why this is lossy.
473 //
474 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
475 {
476 _serviceStatus.dwWin32ExitCode = HRESULT_CODE(hr);
477 }
478 else
479 {
480 if (FAILED(hr))
481 {
482 _serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
483 }
484 _serviceStatus.dwServiceSpecificExitCode = hr;
485 }
486
487 // Unregister the COM objects if the service module was created.
488 if (pModule != nullptr)
489 {
490 pModule->Uninitialize();
491 }
492
493 _serviceStatus.dwCurrentState = SERVICE_STOPPED;
494 ReportCurrentStatus();
495
496 RETURN_IF_FAILED(hr);
497
498 return S_OK;
499 }
500
501 // Returns the service status handle for this service.
502 SERVICE_STATUS_HANDLE GetServiceStatusHandle() const
503 {
504 return _serviceStatusHandle;
505 }
506
507 protected:
508 // Reports the current status information.
509 void ReportCurrentStatus()
510 {
511 SetServiceStatus(_serviceStatusHandle, &_serviceStatus);
512 }
513
514 // Gets a mutable reference to the current status information.
515 LPSERVICE_STATUS GetServiceStatusReference()
516 {
517 return &_serviceStatus;
518 }
519
520 // Asynchronously stops this service, typically in response to a SERVICE_CONTROL_STOP request.
521 void StopAsync()
522 {
523 if (_serviceStatus.dwCurrentState != SERVICE_STOP_PENDING && _serviceStatus.dwCurrentState != SERVICE_STOPPED)
524 {
525 _serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
526 ReportCurrentStatus();
527 }
528 SetEvent(_stopEvent);
529 }
530
531 // Asynchronously stops this service, typically in response to an async initialization issue
532 void StopAsync(HRESULT hr)
533 {
534 if (hr != S_OK)
535 {
536 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
537 {
538 _serviceStatus.dwWin32ExitCode = HRESULT_CODE(hr);
539 }
540 else
541 {
542 if (FAILED(hr))
543 {
544 _serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
545 }
546 _serviceStatus.dwServiceSpecificExitCode = hr;
547 }
548 }
549
550 StopAsync();
551 }
552
553 private:
554 static void SvcMain(DWORD, LPWSTR*)
555 {
556 TBase instance;
557 s_LastServiceMainHR = instance.RunServiceMain(true);
558 }
559
560 static DWORD WINAPI HandlerExStatic(_In_ DWORD dwControl, _In_ DWORD dwEventType, _In_ LPVOID lpEventData, _In_ LPVOID lpContext)
561 {
562 Service* self = reinterpret_cast<Service*>(lpContext);
563 return self->HandlerEx(dwControl, dwEventType, lpEventData);
564 }
565
566 DWORD WINAPI HandlerEx(_In_ DWORD dwControl, _In_ DWORD dwEventType, _In_ LPVOID lpEventData)
567 {
568 // Unreferenced when OnLowPowerModeChanged isn't defined, but this won't hurt.
569 UNREFERENCED_PARAMETER(dwEventType);
570 UNREFERENCED_PARAMETER(lpEventData);
571
572 DWORD dwResult = ERROR_CALL_NOT_IMPLEMENTED;
573
574 __if_exists(TBase::OnHandlerEx)
575 {
576 dwResult = reinterpret_cast<TBase*>(this)->OnHandlerEx(dwControl, dwEventType, lpEventData);
577 }
578
579 // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683241(v=vs.85).aspx for codes.
580 if (dwControl == SERVICE_CONTROL_STOP)
581 {
582 StopAsync();
583 }
584
585 // Provide first-class support for lower power mode when OnLowPowerModeChanged is available.
586 // Additional support can be implemented by overriding OnHandlerEx.
587 __if_exists(TBase::OnLowPowerModeChanged)
588 {
589 if (dwControl == SERVICE_CONTROL_POWEREVENT)
590 {
591 PPOWERBROADCAST_SETTING powerSetting;
592 switch (dwEventType)
593 {
594 case PBT_POWERSETTINGCHANGE:
595 powerSetting = static_cast<PPOWERBROADCAST_SETTING>(lpEventData);
596 if (!memcmp(&powerSetting->PowerSetting, &GUID_LOW_POWER_EPOCH_PRV, sizeof(powerSetting->PowerSetting)) &&
597 powerSetting->DataLength == sizeof(ULONG))
598 {
599 switch (*reinterpret_cast<ULONG*>(powerSetting->Data))
600 {
601 case 0:
602 // Exiting lower power mode change.
603 reinterpret_cast<TBase*>(this)->OnLowPowerModeChanged(false);
604 dwResult = NO_ERROR;
605 break;
606
607 case 1:
608 // Entering lower power mode change.
609 reinterpret_cast<TBase*>(this)->OnLowPowerModeChanged(true);
610 dwResult = NO_ERROR;
611 break;
612 }
613 }
614 break;
615 case PBT_APMPOWERSTATUSCHANGE:
616 case PBT_APMRESUMEAUTOMATIC:
617 case PBT_APMSUSPEND:
618 default:
619 break;
620 }
621 }
622 }
623
624 __if_exists(TBase::OnSessionChanged)
625 {
626 if (dwControl == SERVICE_CONTROL_SESSIONCHANGE)
627 {
628 PWTSSESSION_NOTIFICATION sessionNotification;
629 sessionNotification = static_cast<PWTSSESSION_NOTIFICATION>(lpEventData);
630 reinterpret_cast<TBase*>(this)->OnSessionChanged(dwEventType, sessionNotification->dwSessionId);
631 dwResult = NO_ERROR;
632 }
633 }
634
635 // Provide first-class support for system shutdown when OnSystemShutdown is available.
636 __if_exists(TBase::OnSystemShutdown)
637 {
638 if (dwControl == SERVICE_CONTROL_SHUTDOWN)
639 {
640 //
641 // If a service accepts this control code, it must stop
642 // after it performs its cleanup tasks and return NO_ERROR.
643 // After the SCM sends this control code, it will not send other
644 // control codes to the service.
645 //
646 // We stop asynchronously to have the same codepath as system
647 // stop requests.
648 //
649 reinterpret_cast<TBase*>(this)->OnSystemShutdown();
650 StopAsync();
651 dwResult = NO_ERROR;
652 }
653 }
654
655 return (dwControl == SERVICE_CONTROL_STOP || dwControl == SERVICE_CONTROL_INTERROGATE) ? NO_ERROR : dwResult;
656 }
657
658 typedef void (Service::*CallbackFn)();
659
660 template <LastObjectReleaseBehavior>
661 CallbackFn GetModuleCallback();
662
663 template <>
664 CallbackFn GetModuleCallback<ShutdownAfterLastObjectReleased>()
665 {
666 return &Service::StopAsync;
667 }
668
669 template <>
670 CallbackFn GetModuleCallback<ContinueRunningWithNoObjects>()
671 {
672 return &Service::DummyNoOpCallback;
673 }
674
675 void DummyNoOpCallback()
676 {
677 }
678
679 // HRESULT of the last service main call. Only used when ProcessMain is called.
680 static HRESULT s_LastServiceMainHR;
681
682 // Don't force all callers to adjust project include paths for a single constant.
683 static const GUID GUID_LOW_POWER_EPOCH_PRV;
684
685 // A handle to the power registration for low power epoch.
686 HPOWERNOTIFY _powerHandle = nullptr;
687
688 // Handle to identify this service instance.
689 SERVICE_STATUS_HANDLE _serviceStatusHandle = nullptr;
690
691 // Structure used to report service status updates.
692 SERVICE_STATUS _serviceStatus{};
693
694 // Event object to signal that the service should stop.
695 HANDLE _stopEvent = nullptr;
696 };
697
698 template <typename TBase, LastObjectReleaseBehavior TLastObjectReleaseBehavior, typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor>
699 __declspec(selectany) HRESULT Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy, TServerDescriptor>::s_LastServiceMainHR;
700
701 template <typename TBase, LastObjectReleaseBehavior TLastObjectReleaseBehavior, typename TSecurityPolicy, GLOBALOPT_EH_VALUES TExceptionPolicy, typename TServerDescriptor>
702 __declspec(selectany)
703 const GUID Service<TBase, TLastObjectReleaseBehavior, TSecurityPolicy, TExceptionPolicy, TServerDescriptor>::GUID_LOW_POWER_EPOCH_PRV = {
704 0xe1233993, 0xeaa4, 0x470f, {0x9d, 0xe7, 0xa3, 0x51, 0xc1, 0xb6, 0xfb, 0x71}};
705
706 }} // namespace Windows::Internal