/// <devdoc> /// <para>Provides the main entry point for an executable that /// contains multiple associated services. Loads the specified services into memory so they can be /// started.</para> /// </devdoc> public static unsafe void Run(ServiceBase[] services) { if (services == null || services.Length == 0) { throw new ArgumentException(SR.NoServices); } IntPtr entriesPointer = Marshal.AllocHGlobal(checked ((services.Length + 1) * sizeof(SERVICE_TABLE_ENTRY))); Span <SERVICE_TABLE_ENTRY> entries = new Span <SERVICE_TABLE_ENTRY>((void *)entriesPointer, services.Length + 1); entries.Clear(); try { bool multipleServices = services.Length > 1; // The members of the last entry in the table must have NULL values to designate the end of the table. // Leave the last element in the entries span to be zeroed out. for (int index = 0; index < services.Length; ++index) { ServiceBase service = services[index]; service.Initialize(multipleServices); // This method allocates on unmanaged heap; Make sure that the contents are freed after use. entries[index] = service.GetEntry(); } // While the service is running, this function will never return. It will return when the service // is stopped. // After it returns, SCM might terminate the process at any time // (so subsequent code is not guaranteed to run). bool res = StartServiceCtrlDispatcher(entriesPointer); foreach (ServiceBase service in services) { if (service._startFailedException != null) { // Propagate exceptions throw during OnStart. // Note that this same exception is also thrown from ServiceMainCallback // (so SCM can see it as well). service._startFailedException.Throw(); } } string errorMessage = string.Empty; if (!res) { errorMessage = new Win32Exception().Message; Console.WriteLine(SR.CantStartFromCommandLine); } foreach (ServiceBase service in services) { service.Dispose(); if (!res) { service.WriteLogEntry(SR.Format(SR.StartFailed, errorMessage), EventLogEntryType.Error); } } } finally { // Free the pointer to the name of the service on the unmanaged heap. for (int i = 0; i < entries.Length; i++) { Marshal.FreeHGlobal(entries[i].name); } // Free the unmanaged array containing the entries. Marshal.FreeHGlobal(entriesPointer); } }