/// <summary> /// This method starts the service. /// </summary> static void Main(string[] args) { //normally Main is one line (and no args above) for services: // To run more than one service you have to add them here //ServiceBase.Run(new ServiceBase[] { new iedusm() }); //but let's implement self-install as per <https://stackoverflow.com/questions/2072288/installing-windows-service-programmatically>: if (System.Environment.UserInteractive) { Console.Error.WriteLine("This program would normally be run as a service"); if (args.Length > 0) { switch (args[0]) { case "-install": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location }); //starting it as per codemonkey from <https://stackoverflow.com/questions/1036713/automatically-start-a-windows-service-on-install>: //results in access denied (same if done manually, unless "Log on as" is changed from LocalService to Local System //serviceInstaller //using (ServiceController sc = new ServiceController(serviceInstaller.ServiceName)) //using (ServiceController sc = new ServiceController("iedusm")) //{ // sc.Start(); //} break; } case "-uninstall": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location }); break; } case "-detach_managed_software": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); IEduSM.uninstall_services(false); break; } case "-delete_managed_software": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); IEduSM.uninstall_services(true); break; } case "-install_managed_software": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); IEduSM.update_software(); break; } case "-update_managed_software": { Console.Error.WriteLine(" but found " + args[0] + " option so continuing with that..."); IEduSM.update_software(); break; } default: { Console.Error.WriteLine(" ERROR: unknown option '" + args[0] + "'"); break; } } } else { //make sure other program are install (such as if someone right-clicked this to run as Administrator) //Console.Error.WriteLine(" or be run with -install, -uninstall -services_install or -services_uninstall option."); Console.Error.WriteLine(" so taking defensive measures..."); IEduSM.update_software(); } } else { Console.Error.WriteLine("Running service..."); ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new IEduSM() }; ServiceBase.Run(ServicesToRun); } } //end Main
private static void update_software(string[] names) { if (names != null) { if (names.Length > 0) { Console.Error.WriteLine("update_software will check for already-installed versions in (list may have dups since forces check for x86 in environment variables):"); string[] destination_dir_paths = IEdu.get_software_destination_possible_folder_paths(names[0]); for (int j = 0; j < destination_dir_paths.Length; j++) { Console.Error.WriteLine(" - " + destination_dir_paths[j].Replace(char.ToString(Path.DirectorySeparatorChar) + names[0], "")); } Console.Error.WriteLine(); for (int i = 0; i < names.Length; i++) { bool copyfiles_enable = false; string s_name = names[i]; try { string source_file_path = IEdu.get_software_source_file_path(s_name); if (source_file_path == null) { string msg = "A source for " + s_name + " could not be found in:"; string[] sources = IEdu.get_software_source_expected_paths(s_name); for (int l = 0; l < sources.Length; l++) { msg += "\n - " + sources[l]; } throw new ApplicationException(msg); } string destination_file_path = IEdu.get_software_destination_file_path(s_name, false); //TODO: also update bool update_enable = true; //TODO: only enable if version changed if (destination_file_path == null) { Console.Error.WriteLine("update_software did not find the program " + s_name + ", so installing..."); string destination_folder_path = IEdu.get_software_destination_folder_path(s_name, true); if (source_file_path != null && File.Exists(source_file_path)) { //setup (copy files to destination) //guaranteed to not to return null when 2nd param is true if (!Directory.Exists(destination_folder_path)) { Directory.CreateDirectory(destination_folder_path); } copyfiles_enable = true; } else { Console.Error.WriteLine("ERROR: The missing service was not copied to " + destination_folder_path + " because the installer service could not find the install source in any of the following locations:"); string[] possible_sources = IEdu.get_software_source_expected_paths(s_name); for (int k = 0; k < possible_sources.Length; k++) { Console.Error.WriteLine(" - " + possible_sources[k]); } Console.Error.WriteLine(); } } else { if (update_enable) { IEduSM.detach_service(IEdu.get_software_destination_file_path(s_name, false)); copyfiles_enable = true; } } if (copyfiles_enable) { //we are already assured by get_service_path that the following doesn't exist since fell through to github: string new_s_path = IEdu.get_software_destination_file_path(s_name, true); Console.Error.WriteLine("Copying '" + s_name + "' from GitHub build folder to '" + new_s_path + "'"); try { if (File.Exists(new_s_path)) { File.Delete(new_s_path); } } catch (Exception exn) { Console.Error.WriteLine("Could not finish deleting old version of " + s_name + ": " + exn.ToString()); } if (!File.Exists(new_s_path)) { File.Copy(source_file_path, new_s_path); if (!File.Exists(new_s_path)) { Console.Error.WriteLine("Could not copy '" + source_file_path + "' to destination directory '" + IEdu.get_software_destination_folder_path(s_name, true) + "'. You must run this as Administrator."); } else { destination_file_path = IEdu.get_software_destination_file_path(s_name, false); } } else { Console.Error.WriteLine("Could not install " + s_name + " since old version couldn't be deleted (maybe you didn't have permission to delete the file, or didn't have permission to detach (\"uninstall\") a service [or stop services which would have to be done first if running])!"); } } if (destination_file_path != null) { if (copyfiles_enable) { //already copied files, but now need to install service SINCE they were (updated/added). //NOTE: get_service_path assures that s_path exists in this case // (if it was in GitHub folder, install to ProgramFiles was already tried, // and s_path changed to Program Files*\<s_name>\<s_name>.exe). // see <https://docs.microsoft.com/en-us/dotnet/framework/windows-services/walkthrough-creating-a-windows-service-application-in-the-component-designer#code-snippet-1> // via <https://stackoverflow.com/questions/2072288/installing-windows-service-programmatically>: //detach_software(new string[] {s_name}); //detach_service(s_path); //already done if existed0 ManagedInstallerClass.InstallHelper(new string[] { destination_file_path }); //Starting it as per codemonkey from <https://stackoverflow.com/questions/1036713/automatically-start-a-windows-service-on-install>: // results in access denied (same if done manually, unless "Log on as" is changed from LocalService to Local System //serviceInstaller //ServiceProcessInstaller serviceProcessInstaller1 = new ServiceProcessInstaller(); //serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; //serviceProcessInstaller1.Installers //using (ServiceController sc = new ServiceController(s_name)) { // sc.ServiceType = ServiceType.InteractiveProcess;//TODO: is this correct? // sc.Start(); //} // and doesn't have a way to set account, so set in IEduInstaller.cs which inherits from Installer and don't call it manually /* * // --below won't work either since Commit is called by install utilities which know the state of the service (but don't need overload since can start it manually) * Installer installer; * * * installer = new Installer(); * ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller(); * serviceProcessInstaller.Account = ServiceAccount.LocalSystem; // Or whatever account you want * ServiceInstaller si = new ServiceInstaller(); * si.DelayedAutoStart = true; * si.DisplayName = s_name; * si.Description = s_name; * si.DisplayName = s_name; * //si.HelpText * //si.Installers * //si.Parent //do set since not child * si.ServiceName = s_name; * //si.ServicesDependedOn //do not set since not parent either * //si.Site // I dont' know what this does * si.StartType = ServiceStartMode.Automatic; * * //var serviceInstaller = new ServiceInstaller * //{ * // DisplayName = "Insert the display name here", * // StartType = ServiceStartMode.Automatic, // Or whatever startup type you want * // Description = "Insert a description for your service here", * // ServiceName = "Insert the service name here" * //}; * installer.Installers.Add(serviceProcessInstaller); // why was this _serviceProcessInstaller fre0n? * installer.Installers.Add(si); * installer.Commit(); */ Console.WriteLine("Trying to start service manually..."); ServiceController sc = new ServiceController(s_name); // using (ServiceController sc = new ServiceController(serviceInstaller.ServiceName)) //sc.ServiceType = ServiceType.InteractiveProcess; // TODO: why is this readonly and should it be changed somehow? sc.Start(); //or use IEduPServiceInstaller (see its cs file) based on fre0n's answer from <https://stackoverflow.com/questions/2253051/credentials-when-installing-windows-service/2253140#2253140>: } else { Console.Error.WriteLine("Update for " + s_name + " was not enabled/needed."); } } else { Console.Error.WriteLine("ERROR: Path to " + s_name + " could not be detected."); } } catch (Exception exn) { string var_msg = (names[i] != null)?("'" + names[i] + "'"):"null"; Console.Error.WriteLine("Could not finish update_software named " + var_msg + ":" + exn.ToString()); } } //end for i<names.Length } else { Console.Error.WriteLine("ERROR: update_software got empty names array"); } } else { Console.Error.WriteLine("ERROR: update_software got null names array"); } }