├── images
├── enable.png
├── help1.png
├── help2.png
├── list1.png
├── list2.png
└── disable.png
├── .github
├── dependabot.yml
└── workflows
│ └── build.yml
├── src
├── StartupManager.Services
│ ├── Identity
│ │ ├── IWindowsIdentityService.cs
│ │ └── WindowsIdentityService.cs
│ ├── StartupManager.Services.csproj
│ ├── Schedulers
│ │ ├── ITaskSchedulerService.cs
│ │ └── TaskSchedulerService.cs
│ ├── Directories
│ │ ├── IDirectoryService.cs
│ │ └── DirectoryService.cs
│ ├── Startup
│ │ ├── IStartupQueryService.cs
│ │ └── StartupQueryService.cs
│ └── Registries
│ │ ├── IRegistryService.cs
│ │ └── RegistryService.cs
├── StartupManager.Models
│ ├── StartupManager.Models.csproj
│ ├── StartupState.cs
│ ├── Enums.cs
│ ├── ConsoleColorOutput.cs
│ ├── StartupProgram.cs
│ └── StartupList.cs
└── StartupManager
│ ├── Commands
│ ├── Remove
│ │ ├── RemoveCommand.cs
│ │ └── RemoveCommandHandler.cs
│ ├── StartupToggle
│ │ ├── EnableCommand.cs
│ │ ├── DisableCommand.cs
│ │ └── EnableDisableCommandHandler.cs
│ ├── SetPriority
│ │ ├── SetPriorityCommand.cs
│ │ └── SetPriorityCommandHandler.cs
│ ├── StartupList
│ │ ├── ListCommand.cs
│ │ └── ListCommandHandler.cs
│ ├── Add
│ │ ├── AddCommandHandler.cs
│ │ └── AddCommand.cs
│ └── CommandBuilder.cs
│ ├── ConsoleOutputters
│ ├── ConsoleStep.cs
│ ├── Tables
│ │ ├── Header.cs
│ │ ├── Table.cs
│ │ └── TableParser.cs
│ ├── ConsoleColorHelper.cs
│ ├── ConsoleOutputWriter.cs
│ ├── ListOutput
│ │ └── ListCommandOutputter.cs
│ └── ConsoleStepWizard.cs
│ ├── Program.cs
│ ├── StartupManager.csproj
│ └── UI
│ └── InteractiveMenu.cs
├── LICENSE.md
├── .vscode
├── launch.json
└── tasks.json
├── CHANGELOG.md
├── README.md
└── .gitignore
/images/enable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/enable.png
--------------------------------------------------------------------------------
/images/help1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/help1.png
--------------------------------------------------------------------------------
/images/help2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/help2.png
--------------------------------------------------------------------------------
/images/list1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/list1.png
--------------------------------------------------------------------------------
/images/list2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/list2.png
--------------------------------------------------------------------------------
/images/disable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Faustvii/StartupManager/HEAD/images/disable.png
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: "/src/StartupManager"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 |
--------------------------------------------------------------------------------
/src/StartupManager.Services/Identity/IWindowsIdentityService.cs:
--------------------------------------------------------------------------------
1 | namespace StartupManager.Services.Identity {
2 | public interface IWindowsIdentityService {
3 | string CurrentUser();
4 | bool IsElevated();
5 | }
6 | }
--------------------------------------------------------------------------------
/src/StartupManager.Models/StartupManager.Models.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | enable
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/StartupManager.Models/StartupState.cs:
--------------------------------------------------------------------------------
1 | namespace StartupManager.Models {
2 | public class StartupState {
3 | public string Name { get; set; }
4 | public bool Disabled { get; set; }
5 | public StartupState(string name, bool disabled) {
6 | Name = name;
7 | Disabled = disabled;
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/StartupManager.Models/Enums.cs:
--------------------------------------------------------------------------------
1 | namespace StartupManager.Models {
2 | public enum StateChange {
3 | Success,
4 | SameState,
5 | Unauthorized
6 | }
7 |
8 | public enum ProcessPriority {
9 | Idle = 0,
10 | BelowNormal = 1,
11 | Normal = 2,
12 | AboveNormal = 3,
13 | High = 4,
14 | Realtime = 5
15 | }
16 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/Remove/RemoveCommand.cs:
--------------------------------------------------------------------------------
1 | using StartupManager.ConsoleOutputters;
2 |
3 | namespace StartupManager.Commands.Remove {
4 | public static class RemoveCommand {
5 | public static void Run(string name, bool confirm) {
6 | var result = RemoveCommandHandler.Run(name, confirm);
7 | ConsoleOutputWriter.WriteToConsole(result);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/StartupToggle/EnableCommand.cs:
--------------------------------------------------------------------------------
1 | using StartupManager.ConsoleOutputters;
2 |
3 | namespace StartupManager.Commands.StartupToggle {
4 | public static class EnableCommand {
5 | public static void Run(string name) {
6 | var messages = EnableDisableCommandHandler.Run(name, true);
7 | ConsoleOutputWriter.WriteToConsole(messages);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/StartupToggle/DisableCommand.cs:
--------------------------------------------------------------------------------
1 | using StartupManager.ConsoleOutputters;
2 |
3 | namespace StartupManager.Commands.StartupToggle {
4 | public static class DisableCommand {
5 | public static void Run(string name) {
6 | var messages = EnableDisableCommandHandler.Run(name, false);
7 | ConsoleOutputWriter.WriteToConsole(messages);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/SetPriority/SetPriorityCommand.cs:
--------------------------------------------------------------------------------
1 | using StartupManager.ConsoleOutputters;
2 |
3 | namespace StartupManager.Commands.SetPriority {
4 | public static class SetPriorityCommand {
5 | public static void Run(string name, string priority) {
6 | var messages = SetPriorityCommandHandler.Run(name, priority);
7 | ConsoleOutputWriter.WriteToConsole(messages);
8 | }
9 | }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/ConsoleStep.cs:
--------------------------------------------------------------------------------
1 | namespace StartupManager.ConsoleOutputters {
2 | public class ConsoleStep {
3 | public string Id { get; set; }
4 | public string Message { get; set; }
5 | public object UserValue { get; set; }
6 | public ConsoleStep(string id, string message, object userValue) {
7 | this.Id = id;
8 | this.Message = message;
9 | this.UserValue = userValue;
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/StartupList/ListCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using StartupManager.ConsoleOutputters;
3 |
4 | namespace StartupManager.Commands.StartupList {
5 | public static class ListCommand {
6 | public static void Run(bool detailed) {
7 | var result = ListCommandHandler.Run();
8 | if (result.Messages.Any()) {
9 | ConsoleOutputWriter.WriteToConsole(result.Messages);
10 | }
11 | ConsoleOutputWriter.WriteToConsole(detailed, result.Programs);
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/Tables/Header.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace StartupManager.ConsoleOutputters.Tables {
4 | public class Header {
5 | public Header(string name, Func dataSelector, Func colorSelector) {
6 | Name = name;
7 | DataSelector = dataSelector;
8 | ColorSelector = colorSelector;
9 | }
10 | public string Name { get; set; }
11 | public Func ColorSelector { get; }
12 | public Func DataSelector { get; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/StartupManager/Program.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.Threading.Tasks;
3 | using StartupManager.Commands;
4 | using StartupManager.UI;
5 |
6 | namespace StartupManager {
7 | class Program {
8 | async static Task Main(string[] args) {
9 | // Show interactive UI if no arguments provided
10 | if (args == null || args.Length == 0) {
11 | InteractiveMenu.Show();
12 | return 0;
13 | }
14 |
15 | var rootCommand = CommandBuilder.GetRootCommand();
16 | return await rootCommand.InvokeAsync(args);
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/StartupManager.Services.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | netstandard2.1
14 | enable
15 | true
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/StartupManager/StartupManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net10.0
6 | win-x64
7 | true
8 | false
9 | 1.6.4
10 | enable
11 | default
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/StartupManager.Models/ConsoleColorOutput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace StartupManager.Models {
3 | public class ConsoleColorOutput {
4 | public readonly ConsoleColor? Color;
5 | public readonly WriteMode OutputMode;
6 | public readonly string Message;
7 |
8 | public ConsoleColorOutput(WriteMode outputMode, string message) {
9 | OutputMode = outputMode;
10 | Message = message;
11 | }
12 |
13 | public ConsoleColorOutput(WriteMode outputMode, string message, ConsoleColor color) {
14 | OutputMode = outputMode;
15 | Message = message;
16 | Color = color;
17 | }
18 | }
19 |
20 | public enum WriteMode {
21 | Write,
22 | Writeline
23 | }
24 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/ConsoleColorHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace StartupManager.ConsoleOutputters {
3 | internal static class ConsoleColorHelper {
4 | public static void ConsoleWriteLineColored(ConsoleColor color, params string[] data) {
5 | Console.ForegroundColor = color;
6 | foreach (var line in data) {
7 | Console.WriteLine(line);
8 | }
9 | Console.ResetColor();
10 | }
11 |
12 | public static void ConsoleWriteColored(ConsoleColor color, params string[] data) {
13 | Console.ForegroundColor = color;
14 | foreach (var line in data) {
15 | Console.Write(line);
16 | }
17 | Console.ResetColor();
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Schedulers/ITaskSchedulerService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Win32.TaskScheduler;
4 | using StartupManager.Models;
5 |
6 | namespace StartupManager.Services.Schedulers {
7 | public interface ITaskSchedulerService {
8 | Microsoft.Win32.TaskScheduler.Task AddProgramToStartup(StartupProgram program);
9 | void RemoveProgramFromStartup(string name);
10 | IEnumerable GetStartupPrograms(bool includeWindows);
11 | StartupList? GetStartupByName(string name);
12 | StartupList? GetStartupByPredicate(Func predicate);
13 | StateChange ToggleStartupState(StartupList program, bool enable);
14 | StateChange SetPriority(StartupList program, ProcessPriority priority);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Identity/WindowsIdentityService.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Principal;
2 |
3 | namespace StartupManager.Services.Identity {
4 | public class WindowsIdentityService : IWindowsIdentityService {
5 | public bool IsElevated() {
6 | using(WindowsIdentity identity = WindowsIdentity.GetCurrent()) {
7 | WindowsPrincipal principal = new WindowsPrincipal(identity);
8 | var isElevated = principal.IsInRole(WindowsBuiltInRole.Administrator);
9 | return isElevated;
10 | }
11 | }
12 |
13 | public string CurrentUser() {
14 | using(WindowsIdentity identity = WindowsIdentity.GetCurrent()) {
15 | WindowsPrincipal principal = new WindowsPrincipal(identity);
16 | return identity.Name;
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Directories/IDirectoryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.Models;
4 |
5 | namespace StartupManager.Services.Directories {
6 | public interface IDirectoryService {
7 | void RemoveProgramFromStartup(StartupList program);
8 | IEnumerable GetStartupPrograms();
9 | IEnumerable GetStartupPrograms(IEnumerable startupStates);
10 | StartupList? GetStartupByName(string name, IEnumerable startupStates);
11 | StartupList? GetStartupByName(string name);
12 | StartupList? GetStartupByPredicate(Func filePredicate, Func disabledPredicate, IEnumerable programStates);
13 | StartupList? GetStartupByPredicate(Func filePredicate, Func disabledPredicate);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Startup/IStartupQueryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.Models;
4 |
5 | namespace StartupManager.Services.Startup {
6 | public interface IStartupQueryService {
7 | StartupList? GetStartupByName(string name, IEnumerable startupStates);
8 | StartupList? GetStartupByName(string name);
9 | StartupList? GetStartupByIndex(int index, IEnumerable startupStates);
10 | StartupList? GetStartupByIndex(int index);
11 | StartupList? GetStartupByPredicate(Func registryPredicate, Func directoryPredicate, Func taskSchedulerPredicate, Func disabledPredicate, IEnumerable startupStates);
12 | StartupList? GetStartupByPredicate(Func registryPredicate, Func directoryPredicate, Func taskSchedulerPredicate, Func disabledPredicate);
13 | }
14 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Registries/IRegistryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.Models;
4 |
5 | namespace StartupManager.Services.Registries {
6 | public interface IRegistryService {
7 | void AddProgramToStartup(StartupProgram program);
8 | void RemoveProgramFromStartup(StartupList program);
9 | StateChange ToggleStartupState(StartupList program, bool enable);
10 | IEnumerable GetStartupPrograms(IEnumerable programStates);
11 | StartupList? GetStartupByName(string name, IEnumerable programStates);
12 | StartupList? GetStartupByName(string name);
13 | StartupList? GetStartupByPredicate(Func predicate, Func disabledPredicate, IEnumerable programStates);
14 | StartupList? GetStartupByPredicate(Func predicate, Func disabledPredicate);
15 | IEnumerable GetStartupPrograms();
16 | IEnumerable GetStartupProgramStates();
17 | }
18 | }
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2019
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/src/StartupManager.Models/StartupProgram.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace StartupManager.Models {
4 | public class StartupProgram {
5 | public string Name { get; set; }
6 | public FileInfo File { get; set; }
7 | public string Arguments { get; set; }
8 | public bool Administrator { get; set; }
9 | public bool AllUsers { get; set; }
10 | public ProcessPriority Priority { get; set; }
11 | public StartupProgram(string name, FileInfo file, string arguments, bool administrator, bool allUsers) {
12 | this.Name = name;
13 | this.File = file;
14 | this.Arguments = arguments;
15 | this.Administrator = administrator;
16 | this.AllUsers = allUsers;
17 | this.Priority = ProcessPriority.Normal;
18 | }
19 |
20 | public StartupProgram(string name, FileInfo file, string arguments, bool administrator, bool allUsers, ProcessPriority priority) {
21 | this.Name = name;
22 | this.File = file;
23 | this.Arguments = arguments;
24 | this.Administrator = administrator;
25 | this.AllUsers = allUsers;
26 | this.Priority = priority;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/src/StartupManager/bin/Debug/net5.0/win10-x64/StartupManager.exe",
14 | "args": ["l", "-d"],
15 | "cwd": "${workspaceFolder}/src/StartupManager",
16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17 | "console": "integratedTerminal",
18 | "internalConsoleOptions": "neverOpen",
19 | "stopAtEntry": false
20 | },
21 | {
22 | "name": ".NET Core Attach",
23 | "type": "coreclr",
24 | "request": "attach",
25 | "processId": "${command:pickProcess}"
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/src/StartupManager/StartupManager.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary",
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "--configuration",
23 | "Release",
24 | "${workspaceFolder}/src/StartupManager/StartupManager.csproj",
25 | "/property:GenerateFullPaths=true",
26 | "/consoleloggerparameters:NoSummary"
27 | ],
28 | "problemMatcher": "$msCompile"
29 | },
30 | {
31 | "label": "watch",
32 | "command": "dotnet",
33 | "type": "process",
34 | "args": [
35 | "watch",
36 | "--project",
37 | "${workspaceFolder}/src/StartupManager/StartupManager.csproj",
38 | "run",
39 | "l",
40 | "-d",
41 | ],
42 | "problemMatcher": "$msCompile"
43 | }
44 | ]
45 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/ConsoleOutputWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.ConsoleOutputters.ListOutput;
4 | using StartupManager.Models;
5 |
6 | namespace StartupManager.ConsoleOutputters {
7 | public static class ConsoleOutputWriter {
8 | public static void WriteToConsole(bool detailed, IEnumerable programs) {
9 | ListCommandOutputter.WriteToConsole(detailed, programs);
10 | }
11 |
12 | public static void WriteToConsole(IEnumerable messages) {
13 | foreach (var message in messages) {
14 | WriteToConsole(message);
15 | }
16 | }
17 |
18 | private static void WriteToConsole(ConsoleColorOutput message) {
19 | switch (message.OutputMode) {
20 | case WriteMode.Write:
21 | if (message.Color.HasValue) {
22 | ConsoleColorHelper.ConsoleWriteColored(message.Color.Value, message.Message);
23 | } else {
24 | Console.Write(message.Message);
25 | }
26 | break;
27 | case WriteMode.Writeline:
28 | if (message.Color.HasValue) {
29 | ConsoleColorHelper.ConsoleWriteLineColored(message.Color.Value, message.Message);
30 | } else {
31 | Console.WriteLine(message.Message);
32 | }
33 | break;
34 | }
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [ubuntu-latest]
11 |
12 | steps:
13 | - name: checkout
14 | uses: actions/checkout@master
15 |
16 | - name: Setup .NET Core
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: '10.0.x'
20 |
21 | - name: Build with dotnet
22 | run: dotnet build --configuration Release
23 | working-directory: ./src/StartupManager/
24 |
25 | publish:
26 | if: startsWith(github.ref, 'refs/tags/')
27 | runs-on: ubuntu-latest
28 |
29 | steps:
30 | - name: checkout
31 | uses: actions/checkout@master
32 |
33 | - name: Setup .NET Core
34 | uses: actions/setup-dotnet@v1
35 | with:
36 | dotnet-version: '10.0.x'
37 |
38 | - name: Publish with dotnet
39 | run: dotnet publish --configuration Release
40 | working-directory: ./src/StartupManager/
41 |
42 | - name: Archive Release
43 | uses: montudor/action-zip@v1.0.0
44 | with:
45 | args: zip -jr StartupManager ./src/StartupManager/bin/Release/net10.0/win-x64/publish/StartupManager.exe
46 |
47 | - name: Release
48 | uses: anton-yurchenko/git-release@v1.0.0
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | DRAFT_RELEASE: "true"
52 | CHANGELOG_FILE: "CHANGELOG.md"
53 | with:
54 | args: |
55 | ./StartupManager.zip
56 |
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/ListOutput/ListCommandOutputter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.ConsoleOutputters.Tables;
4 | using StartupManager.Models;
5 |
6 | namespace StartupManager.ConsoleOutputters.ListOutput {
7 | internal static class ListCommandOutputter {
8 | public static void WriteToConsole(bool detailed, IEnumerable programs) {
9 | System.Console.WriteLine("Applications starting with windows:");
10 | Output(programs, detailed);
11 | Console.ResetColor();
12 | }
13 |
14 | private static void Output(IEnumerable programs, bool detailed) {
15 | var tableHeaders = new List> {
16 | new Header("#", x => x.Index, x => ConsoleColor.Blue),
17 | new Header("Name", x => x.Name, x => ConsoleColor.Yellow),
18 | new Header("Admin", x => x.RequireAdministrator ? "[\u221A]" : string.Empty, x => ConsoleColor.Cyan),
19 | new Header("Enabled", x => x.Disabled ? "[x]" : "[\u221A]", x => x.Disabled ? ConsoleColor.Red : ConsoleColor.DarkCyan),
20 | new Header("Priority", x => x.Priority?.ToString() ?? "N/A", x => x.Priority.HasValue ? ConsoleColor.Magenta : ConsoleColor.DarkGray),
21 | };
22 |
23 | if (detailed) {
24 | tableHeaders.Add(new Header("Path", x => x.Path, x => ConsoleColor.Green));
25 | }
26 |
27 | var tableModel = new Table(programs, tableHeaders.ToArray());
28 |
29 | tableModel.OutputToConsoleColored();
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/Tables/Table.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace StartupManager.ConsoleOutputters.Tables {
6 | public class Table {
7 | public readonly string RawTableString;
8 | public Header[] Headers { get; set; }
9 | public T[] Models { get; set; }
10 |
11 | public Table(IEnumerable models, Header[] headers) {
12 | Models = models.ToArray();
13 | Headers = headers;
14 | RawTableString = models.ToStringTable(headers.Select(x => x.Name).ToArray(), headers.Select(x => x.DataSelector).ToArray());
15 | }
16 |
17 | public void OutputToConsoleColored() {
18 | var headerNames = Headers.Select(x => x.Name);
19 | var lines = RawTableString.Split(TableParser.LineSeperator, StringSplitOptions.RemoveEmptyEntries);
20 | for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) {
21 | var line = lines[lineIndex];
22 | if (headerNames.All(x => line.Contains(x))) { //Header line skip
23 | System.Console.WriteLine(line.Replace(TableParser.HeaderSeperator, string.Empty));
24 | System.Console.WriteLine();
25 | continue;
26 | }
27 |
28 | var model = Models[lineIndex - 1];
29 | var content = line.Split(TableParser.HeaderSeperator, StringSplitOptions.RemoveEmptyEntries);
30 | for (int i = 0; i < content.Length; i++) {
31 | Console.ForegroundColor = Headers[i].ColorSelector.Invoke(model);
32 | System.Console.Write(content[i]);
33 | }
34 | Console.ResetColor();
35 | System.Console.WriteLine();
36 | }
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/ConsoleStepWizard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace StartupManager.ConsoleOutputters {
7 | public static class ConsoleStepWizard {
8 | public static IEnumerable UserWizard(string message, IEnumerable steps) {
9 | Console.WriteLine(message);
10 | Console.WriteLine();
11 | foreach (var step in steps) {
12 | Console.Write(step.Message);
13 | step.UserValue = GetValueFromUser(step.UserValue, step.Message);
14 | }
15 | return steps;
16 | }
17 |
18 | private static object GetValueFromUser(object val, string message) {
19 | return val
20 | switch {
21 | FileInfo file => GetFileInfoFromUser(message),
22 | string stringVal => Console.ReadLine() ?? string.Empty,
23 | bool boolVal => PromptUserForBool("y", "n", message),
24 | _ => val
25 | };
26 | }
27 |
28 | public static FileInfo GetFileInfoFromUser(string message) {
29 | var input = (Console.ReadLine() ?? string.Empty).Replace("\"", string.Empty);
30 | var file = new FileInfo(input);
31 | while (!file.Exists) {
32 | Console.WriteLine();
33 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "That file doesn't seem to exist, please try again");
34 | Console.Write(message);
35 | input = (Console.ReadLine() ?? string.Empty).Replace("\"", string.Empty);;
36 | file = new FileInfo(input);
37 | }
38 | return file;
39 | }
40 |
41 | public static bool PromptUserForBool(string trueVal, string falseVal, string message) {
42 | var validOptions = new [] { trueVal, falseVal };
43 | var key = Console.ReadKey().Key.ToString();
44 | while (!validOptions.Any(x => x.Equals(key, StringComparison.OrdinalIgnoreCase))) {
45 | Console.WriteLine();
46 | Console.WriteLine($"Try again '{key}' is not a valid input");
47 | Console.Write(message);
48 | key = Console.ReadKey().Key.ToString();
49 | }
50 | Console.WriteLine();
51 | return key.Equals(trueVal, StringComparison.OrdinalIgnoreCase);
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/StartupList/ListCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using StartupManager.Models;
5 | using StartupManager.Services.Directories;
6 | using StartupManager.Services.Registries;
7 | using StartupManager.Services.Schedulers;
8 |
9 | namespace StartupManager.Commands.StartupList {
10 | public static class ListCommandHandler {
11 | private const string DisabledStartupFolderItems = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder";
12 | private const string UnauthorizedMessage = "Was unable to get all startup programs (Access denied), try running as administrator";
13 | private static string[] StartFolderSearchPatterns = new [] { "*.exe", "*.lnk", "*.ps1", "*.cmd" };
14 | private static IRegistryService RegistryService = new RegistryService();
15 | private static IDirectoryService DirectoryService = new DirectoryService();
16 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
17 |
18 | public static(IEnumerable Programs, IEnumerable Messages) Run() {
19 | var startupPrograms = new List();
20 | var messages = new List();
21 | try {
22 | var startupStates = RegistryService.GetStartupProgramStates();
23 |
24 | var registryStartups = RegistryService.GetStartupPrograms(startupStates);
25 | if (registryStartups != null)
26 | startupPrograms.AddRange(registryStartups);
27 |
28 | var shellStartups = DirectoryService.GetStartupPrograms(startupStates);
29 | if (shellStartups != null)
30 | startupPrograms.AddRange(shellStartups);
31 |
32 | var taskSchedulerStartups = TaskSchedulerService.GetStartupPrograms(false);
33 | if (taskSchedulerStartups != null)
34 | startupPrograms.AddRange(taskSchedulerStartups);
35 |
36 | } catch (UnauthorizedAccessException) {
37 | messages.Add(new ConsoleColorOutput(WriteMode.Writeline, UnauthorizedMessage, ConsoleColor.Red));
38 | }
39 |
40 | var sortedAndIndexedPrograms = startupPrograms
41 | .OrderBy(program => program.Name)
42 | .Select((program, index) => {
43 | program.Index = index + 1; // Assigning index starting from 1
44 | return program;
45 | })
46 | .ToList();
47 | return (sortedAndIndexedPrograms, messages);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/StartupManager.Models/StartupList.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace StartupManager.Models {
4 | public class StartupList {
5 | private string _name = string.Empty;
6 | public string Name { get => _name; set => _name = ParseName(value); }
7 | public string Path { get; set; }
8 | public bool RequireAdministrator { get; set; }
9 | public bool Disabled { get; set; }
10 | public bool AllUsers { get; set; }
11 | public string RegistryPath { get; set; }
12 | public string DisabledRegistryPath { get; set; }
13 | public string RegistryName { get; set; }
14 | public StartupType Type { get; set; }
15 | public int Index { get; set; }
16 | public ProcessPriority? Priority { get; set; }
17 |
18 | public StartupList(string name, [AllowNull] string path, bool requireAdministrator, bool disabled, StartupType type, bool allUsers, string registryPath, string disabledRegistryPath, string registryName) {
19 | Path = path ?? string.Empty;
20 | RequireAdministrator = requireAdministrator;
21 | Disabled = disabled;
22 | Type = type;
23 | AllUsers = allUsers;
24 | Name = name;
25 |
26 | RegistryPath = registryPath;
27 | DisabledRegistryPath = disabledRegistryPath;
28 | RegistryName = registryName;
29 | Priority = null;
30 | }
31 |
32 | public StartupList(string name, [AllowNull] string path, bool requireAdministrator, bool disabled, StartupType type, bool allUsers) {
33 | Path = path ?? string.Empty;
34 | RequireAdministrator = requireAdministrator;
35 | Disabled = disabled;
36 | Type = type;
37 | AllUsers = allUsers;
38 | Name = name;
39 |
40 | RegistryPath = string.Empty;
41 | DisabledRegistryPath = string.Empty;
42 | RegistryName = string.Empty;
43 | Priority = null;
44 | }
45 |
46 | public StartupList(string name, [AllowNull] string path, bool requireAdministrator, bool disabled, StartupType type, bool allUsers, ProcessPriority? priority) {
47 | Path = path ?? string.Empty;
48 | RequireAdministrator = requireAdministrator;
49 | Disabled = disabled;
50 | Type = type;
51 | AllUsers = allUsers;
52 | Name = name;
53 |
54 | RegistryPath = string.Empty;
55 | DisabledRegistryPath = string.Empty;
56 | RegistryName = string.Empty;
57 | Priority = priority;
58 | }
59 |
60 | private string ParseName(string name) {
61 | var parsedName = name == string.Empty ? "(Default)" : name;
62 | if (string.IsNullOrWhiteSpace(parsedName)) {
63 | parsedName = $"'{parsedName}'";
64 | }
65 |
66 | return parsedName;
67 | }
68 |
69 | public enum StartupType {
70 | Shortcut,
71 | Regedit,
72 | TaskScheduler
73 | }
74 | }
75 | } //00:00:00.0074059
--------------------------------------------------------------------------------
/src/StartupManager/Commands/Remove/RemoveCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using StartupManager.Commands.StartupList;
5 | using StartupManager.ConsoleOutputters;
6 | using StartupManager.Models;
7 | using StartupManager.Services.Directories;
8 | using StartupManager.Services.Identity;
9 | using StartupManager.Services.Registries;
10 | using StartupManager.Services.Schedulers;
11 |
12 | namespace StartupManager.Commands.Remove {
13 | public static class RemoveCommandHandler {
14 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
15 | private static IWindowsIdentityService WindowsIdentityService = new WindowsIdentityService();
16 | private static IDirectoryService DirectoryService = new DirectoryService();
17 | private static IRegistryService RegistryService = new RegistryService();
18 | public static IEnumerable Run(string name, bool confirm) {
19 | var program = ListCommandHandler.Run().Programs.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
20 | if (program == null) {
21 | return new [] {
22 | new ConsoleColorOutput(WriteMode.Write, "Could not find a program with name ", ConsoleColor.Red),
23 | new ConsoleColorOutput(WriteMode.Writeline, name, ConsoleColor.Yellow),
24 | };
25 | }
26 |
27 | if (program.RequireAdministrator && !WindowsIdentityService.IsElevated()) {
28 | return new [] {
29 | new ConsoleColorOutput(WriteMode.Write, $"To modify settings for ", ConsoleColor.Red),
30 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
31 | new ConsoleColorOutput(WriteMode.Writeline, " you need to run the command with administrator (sudo)", ConsoleColor.Red),
32 | };
33 | }
34 |
35 | if (!confirm) {
36 | var message = $"Are you sure you want to remove '{program.Name}' y/n: ";
37 | var messages = new [] {
38 | new ConsoleColorOutput(WriteMode.Write, message),
39 | };
40 | ConsoleOutputWriter.WriteToConsole(messages);
41 | var confirmation = ConsoleStepWizard.PromptUserForBool("y", "n", message);
42 | if (!confirmation) {
43 | return new List();
44 | }
45 | }
46 |
47 | switch (program.Type) {
48 | case Models.StartupList.StartupType.TaskScheduler:
49 | TaskSchedulerService.RemoveProgramFromStartup(program.Name);
50 | break;
51 | case Models.StartupList.StartupType.Shortcut:
52 | DirectoryService.RemoveProgramFromStartup(program);
53 | break;
54 | case Models.StartupList.StartupType.Regedit:
55 | RegistryService.RemoveProgramFromStartup(program);
56 | break;
57 | }
58 | return new [] {
59 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
60 | new ConsoleColorOutput(WriteMode.Writeline, " has been removed"),
61 | };
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/Add/AddCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using StartupManager.Commands.StartupList;
5 | using StartupManager.ConsoleOutputters;
6 | using StartupManager.Models;
7 | using StartupManager.Services.Identity;
8 | using StartupManager.Services.Registries;
9 | using StartupManager.Services.Schedulers;
10 |
11 | namespace StartupManager.Commands.Add {
12 | public static class AddCommandHandler {
13 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
14 | private static IWindowsIdentityService WindowsIdentityService = new WindowsIdentityService();
15 | private static IRegistryService RegistryService = new RegistryService();
16 | public static IEnumerable Run(StartupProgram program) {
17 | var existingPrograms = ListCommandHandler.Run().Programs.ToList();
18 | var programAlreadyStarts = existingPrograms.FirstOrDefault(x => x.Path.Contains(program.File.FullName, StringComparison.OrdinalIgnoreCase) && !x.Disabled);
19 | var nameInUseBy = existingPrograms.FirstOrDefault(x => x.Name.Equals(program.Name, StringComparison.OrdinalIgnoreCase));
20 |
21 | if (nameInUseBy != null) {
22 | return new [] {
23 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
24 | new ConsoleColorOutput(WriteMode.Writeline, " is already in use, please try again with a different name", ConsoleColor.Red),
25 | };
26 | }
27 |
28 | if (programAlreadyStarts != null) {
29 | var messages = new [] {
30 | new ConsoleColorOutput(WriteMode.Write, program.File.Name, ConsoleColor.Yellow),
31 | new ConsoleColorOutput(WriteMode.Writeline, " already starts with windows", ConsoleColor.Red),
32 | new ConsoleColorOutput(WriteMode.Write, "Want to add another instance of it to startup? y/n: "),
33 | };
34 | ConsoleOutputWriter.WriteToConsole(messages);
35 |
36 | var userWantsToContinue = ConsoleStepWizard.PromptUserForBool("y", "n", $"Want to add another? y/n: ");
37 | if (!userWantsToContinue) {
38 | return new List();
39 | }
40 | }
41 |
42 | if (program.AllUsers || program.Administrator) {
43 | if (!WindowsIdentityService.IsElevated()) {
44 | return new [] {
45 | new ConsoleColorOutput(WriteMode.Writeline, "This requires you run the command as administrator", ConsoleColor.Red)
46 | };
47 | }
48 | }
49 |
50 | //Current user only programs requires a schedule task to run as Administrator
51 | if (program.Administrator && !program.AllUsers) {
52 | TaskSchedulerService.AddProgramToStartup(program);
53 | } else {
54 | RegistryService.AddProgramToStartup(program);
55 | }
56 | return new [] {
57 | new ConsoleColorOutput(WriteMode.Write, "Added "),
58 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
59 | new ConsoleColorOutput(WriteMode.Writeline, " to startup")
60 | };
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/StartupManager/ConsoleOutputters/Tables/TableParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace StartupManager.ConsoleOutputters.Tables {
7 | public static class TableParser {
8 | public const string HeaderSeperator = "|-_-|";
9 | public const string LineSeperator = "|_-_|";
10 | public static string ToStringTable(this IEnumerable values, string[] columnHeaders, params Func[] valueSelectors) {
11 | return ToStringTable(values.ToArray(), columnHeaders, valueSelectors);
12 | }
13 |
14 | public static string ToStringTable(this T[] values, string[] columnHeaders, params Func[] valueSelectors) {
15 | if (valueSelectors == null) {
16 | throw new ArgumentNullException(nameof(valueSelectors));
17 | }
18 | if (columnHeaders.Length != valueSelectors.Length) {
19 | throw new ArgumentException($"{nameof(columnHeaders)} and {nameof(valueSelectors)} must have same length");
20 | }
21 |
22 | var arrValues = new string[values.Length + 1, valueSelectors.Length];
23 |
24 | // Fill headers
25 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) {
26 | arrValues[0, colIndex] = columnHeaders[colIndex];
27 | }
28 |
29 | // Fill table rows
30 | for (int rowIndex = 1; rowIndex < arrValues.GetLength(0); rowIndex++) {
31 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) {
32 | if (valueSelectors != null) {
33 | if (valueSelectors.Length >= colIndex) {
34 | var value = valueSelectors[colIndex]
35 | .Invoke(values[rowIndex - 1]).ToString() ?? string.Empty;
36 | arrValues[rowIndex, colIndex] = value;
37 | }
38 | }
39 | }
40 | }
41 |
42 | return ToStringTable(arrValues);
43 | }
44 |
45 | public static string ToStringTable(this string[, ] arrValues) {
46 | int[] maxColumnsWidth = GetMaxColumnsWidth(arrValues);
47 |
48 | var sb = new StringBuilder();
49 | for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++) {
50 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) {
51 | // Print cell
52 | string cell = arrValues[rowIndex, colIndex];
53 | cell = cell.PadRight(maxColumnsWidth[colIndex]);
54 | if (colIndex != 0)
55 | sb.Append($" {HeaderSeperator} ");
56 | sb.Append(cell);
57 | }
58 |
59 | // Print end of line
60 | sb.Append(LineSeperator);
61 | }
62 |
63 | return sb.ToString();
64 | }
65 |
66 | private static int[] GetMaxColumnsWidth(string[, ] arrValues) {
67 | var maxColumnsWidth = new int[arrValues.GetLength(1)];
68 | for (int colIndex = 0; colIndex < arrValues.GetLength(1); colIndex++) {
69 | for (int rowIndex = 0; rowIndex < arrValues.GetLength(0); rowIndex++) {
70 | int newLength = arrValues[rowIndex, colIndex].Length;
71 | int oldLength = maxColumnsWidth[colIndex];
72 |
73 | if (newLength > oldLength) {
74 | maxColumnsWidth[colIndex] = newLength;
75 | }
76 | }
77 | }
78 |
79 | return maxColumnsWidth;
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Startup/StartupQueryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Win32.TaskScheduler;
5 | using StartupManager.Models;
6 | using StartupManager.Services.Directories;
7 | using StartupManager.Services.Registries;
8 | using StartupManager.Services.Schedulers;
9 |
10 | namespace StartupManager.Services.Startup {
11 | public class StartupQueryService : IStartupQueryService {
12 | private static IRegistryService RegistryService = new RegistryService();
13 | private static IDirectoryService DirectoryService = new DirectoryService();
14 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
15 | public StartupList? GetStartupByName(string name, IEnumerable startupStates) {
16 | var program = RegistryService.GetStartupByName(name, startupStates);
17 | if (program == null) {
18 | program = DirectoryService.GetStartupByName(name, startupStates);
19 | }
20 | if (program == null) {
21 | program = TaskSchedulerService.GetStartupByName(name);
22 | }
23 |
24 | return program;
25 | }
26 |
27 | public StartupList? GetStartupByIndex(int index, IEnumerable startupStates) {
28 | var registryPrograms = RegistryService.GetStartupPrograms(startupStates);
29 | var directoryPrograms = DirectoryService.GetStartupPrograms(startupStates);
30 | var taskSchedulerPrograms = TaskSchedulerService.GetStartupPrograms(false);
31 | var allPrograms = new List();
32 | if (registryPrograms != null) {
33 | allPrograms.AddRange(registryPrograms);
34 | }
35 | if (directoryPrograms != null) {
36 | allPrograms.AddRange(directoryPrograms);
37 | }
38 | if (taskSchedulerPrograms != null) {
39 | allPrograms.AddRange(taskSchedulerPrograms);
40 | }
41 | allPrograms = allPrograms.OrderBy(program => program.Name).ToList();
42 | return allPrograms.ElementAtOrDefault(index - 1); // Index is 1-based in the command line
43 | }
44 |
45 | public StartupList? GetStartupByIndex(int index) {
46 | var startupStates = RegistryService.GetStartupProgramStates();
47 | return GetStartupByIndex(index, startupStates);
48 | }
49 |
50 | public StartupList? GetStartupByName(string name)
51 | {
52 | var startupStates = RegistryService.GetStartupProgramStates();
53 | return GetStartupByName(name, startupStates);
54 | }
55 |
56 | public StartupList? GetStartupByPredicate(Func registryPredicate, Func directoryPredicate, Func taskSchedulerPredicate, Func disabledPredicate, IEnumerable startupStates) {
57 | var program = RegistryService.GetStartupByPredicate(registryPredicate, disabledPredicate, startupStates);
58 | if (program == null) {
59 | program = DirectoryService.GetStartupByPredicate(directoryPredicate, disabledPredicate, startupStates);
60 | }
61 | if (program == null) {
62 | program = TaskSchedulerService.GetStartupByPredicate(taskSchedulerPredicate);
63 | }
64 | return program;
65 | }
66 |
67 | public StartupList? GetStartupByPredicate(Func registryPredicate, Func directoryPredicate, Func taskSchedulerPredicate, Func disabledPredicate) {
68 | var startupStates = RegistryService.GetStartupProgramStates();
69 | return GetStartupByPredicate(registryPredicate, directoryPredicate, taskSchedulerPredicate, disabledPredicate, startupStates);
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/SetPriority/SetPriorityCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.Models;
4 | using StartupManager.Services.Registries;
5 | using StartupManager.Services.Schedulers;
6 | using StartupManager.Services.Startup;
7 |
8 | namespace StartupManager.Commands.SetPriority {
9 | public static class SetPriorityCommandHandler {
10 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
11 | private static IStartupQueryService StartupQueryService = new StartupQueryService();
12 | private static IRegistryService RegistryService = new RegistryService();
13 |
14 | public static IEnumerable Run(string name, string priority) {
15 | var consoleMessages = new List();
16 |
17 | if (!Enum.TryParse(priority, true, out var priorityValue)) {
18 | return new[] {
19 | new ConsoleColorOutput(WriteMode.Write, "Invalid priority: ", ConsoleColor.Red),
20 | new ConsoleColorOutput(WriteMode.Writeline, priority, ConsoleColor.Yellow),
21 | new ConsoleColorOutput(WriteMode.Writeline, "Valid priorities are: Idle, BelowNormal, Normal, AboveNormal, High, Realtime", ConsoleColor.Red)
22 | };
23 | }
24 |
25 | var startupStates = RegistryService.GetStartupProgramStates();
26 | Models.StartupList? program = null;
27 | if (int.TryParse(name, out int index)) {
28 | program = StartupQueryService.GetStartupByIndex(index);
29 | }
30 | program ??= StartupQueryService.GetStartupByName(name, startupStates);
31 |
32 | if (program == null) {
33 | return WriteProgramNotFoundConsoleOutput(name);
34 | }
35 |
36 | if (program.Type != Models.StartupList.StartupType.TaskScheduler) {
37 | return new[] {
38 | new ConsoleColorOutput(WriteMode.Write, "Priority can only be set for Task Scheduler startup programs. ", ConsoleColor.Red),
39 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
40 | new ConsoleColorOutput(WriteMode.Writeline, $" is a {program.Type} startup program.", ConsoleColor.Red)
41 | };
42 | }
43 |
44 | var result = TaskSchedulerService.SetPriority(program, priorityValue);
45 | switch (result) {
46 | case StateChange.SameState:
47 | return WriteSamePriorityConsoleOutput(program, priorityValue);
48 | case StateChange.Success:
49 | return WritePrioritySetConsoleOutput(program, priorityValue);
50 | case StateChange.Unauthorized:
51 | return WriteRequireAdministratorConsoleOutput(program);
52 | }
53 | return new List();
54 | }
55 |
56 | private static IEnumerable WriteProgramNotFoundConsoleOutput(string name) {
57 | return new[] {
58 | new ConsoleColorOutput(WriteMode.Write, "Could not find a program with name/index ", ConsoleColor.Red),
59 | new ConsoleColorOutput(WriteMode.Writeline, name, ConsoleColor.Yellow),
60 | };
61 | }
62 |
63 | private static IEnumerable WriteSamePriorityConsoleOutput(Models.StartupList program, ProcessPriority priority) {
64 | return new[] {
65 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
66 | new ConsoleColorOutput(WriteMode.Writeline, $" already has priority set to {priority}"),
67 | };
68 | }
69 |
70 | private static IEnumerable WriteRequireAdministratorConsoleOutput(Models.StartupList program) {
71 | return new[] {
72 | new ConsoleColorOutput(WriteMode.Write, $"To modify priority for ", ConsoleColor.Red),
73 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
74 | new ConsoleColorOutput(WriteMode.Writeline, " you need to run the command with administrator (sudo)", ConsoleColor.Red),
75 | };
76 | }
77 |
78 | private static IEnumerable WritePrioritySetConsoleOutput(Models.StartupList program, ProcessPriority priority) {
79 | return new[] {
80 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
81 | new ConsoleColorOutput(WriteMode.Writeline, $" priority has been set to {priority}"),
82 | };
83 | }
84 | }
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/src/StartupManager/Commands/CommandBuilder.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Invocation;
3 | using System.IO;
4 | using StartupManager.Commands.Add;
5 | using StartupManager.Commands.Remove;
6 | using StartupManager.Commands.SetPriority;
7 | using StartupManager.Commands.StartupList;
8 | using StartupManager.Commands.StartupToggle;
9 |
10 | namespace StartupManager.Commands {
11 | public static class CommandBuilder {
12 | public static RootCommand GetRootCommand() => new RootCommand {
13 | GetStartupListCommand(),
14 | GetDisableStartupCommand(),
15 | GetEnableStartupCommand(),
16 | GetAddStartupCommand(),
17 | GetRemoveStartupCommand(),
18 | GetSetPriorityCommand()
19 | };
20 |
21 | private static Command GetStartupListCommand() {
22 | var listCommand = new Command("list") {
23 | Description = "Lists the current startup programs"
24 | };
25 |
26 | listCommand.AddAlias("l");
27 |
28 | var detailedOption = new Option("--detailed", "Shows additional output about the startup programs");
29 | detailedOption.AddAlias("-d");
30 | listCommand.AddOption(detailedOption);
31 |
32 | listCommand.Handler = CommandHandler.Create(ListCommand.Run);
33 |
34 | return listCommand;
35 | }
36 |
37 | private static Command GetAddStartupCommand() {
38 | var addCommand = new Command("add") {
39 | Description = "Adds a program to startup with windows",
40 | };
41 |
42 | addCommand.AddAlias("a");
43 |
44 | addCommand.AddArgument(new Argument("name", null) { Arity = ArgumentArity.ZeroOrOne });
45 | addCommand.AddArgument(new Argument("path", null) { Arity = ArgumentArity.ZeroOrOne });
46 | addCommand.AddArgument(new Argument("arguments", null) { Arity = ArgumentArity.ZeroOrOne });
47 | addCommand.AddArgument(new Argument("admin", null) { Arity = ArgumentArity.ZeroOrOne });
48 | addCommand.AddArgument(new Argument("allUsers", null) { Arity = ArgumentArity.ZeroOrOne });
49 | addCommand.AddArgument(new Argument("priority", null) { Arity = ArgumentArity.ZeroOrOne });
50 |
51 | addCommand.Handler = CommandHandler.Create(AddCommand.Run);
52 |
53 | return addCommand;
54 | }
55 |
56 | private static Command GetRemoveStartupCommand() {
57 | var removeCommand = new Command("remove") {
58 | Description = "Removes the specified program from startup"
59 | };
60 |
61 | removeCommand.AddAlias("r");
62 |
63 | removeCommand.AddArgument(new Argument("name"));
64 |
65 | var skipConfirmation = new Option("--confirm", "Skips the confirmation prompt");
66 | skipConfirmation.AddAlias("-c");
67 | removeCommand.AddOption(skipConfirmation);
68 |
69 | removeCommand.Handler = CommandHandler.Create(RemoveCommand.Run);
70 |
71 | return removeCommand;
72 | }
73 |
74 | private static Command GetDisableStartupCommand() {
75 | var disableCommand = new Command("disable") {
76 | Description = "Disables one of the current startup programs",
77 | };
78 |
79 | disableCommand.AddAlias("d");
80 |
81 | disableCommand.AddArgument(new Argument("name", description: "Name or index of the program to disable"));
82 |
83 | disableCommand.Handler = CommandHandler.Create(DisableCommand.Run);
84 |
85 | return disableCommand;
86 | }
87 |
88 | private static Command GetEnableStartupCommand() {
89 | var enableCommand = new Command("enable") {
90 | Description = "Enables one of the current startup programs"
91 | };
92 |
93 | enableCommand.AddAlias("e");
94 |
95 | enableCommand.AddArgument(new Argument("name", description: "Name or index of the program to enable"));
96 |
97 | enableCommand.Handler = CommandHandler.Create(EnableCommand.Run);
98 |
99 | return enableCommand;
100 | }
101 |
102 | private static Command GetSetPriorityCommand() {
103 | var setPriorityCommand = new Command("set-priority") {
104 | Description = "Sets the priority of a Task Scheduler startup program"
105 | };
106 |
107 | setPriorityCommand.AddAlias("p");
108 |
109 | setPriorityCommand.AddArgument(new Argument("name", description: "Name or index of the program to set priority for"));
110 | setPriorityCommand.AddArgument(new Argument("priority", description: "Priority level: Idle, BelowNormal, Normal, AboveNormal, High, or Realtime"));
111 |
112 | setPriorityCommand.Handler = CommandHandler.Create(SetPriorityCommand.Run);
113 |
114 | return setPriorityCommand;
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/src/StartupManager/Commands/StartupToggle/EnableDisableCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using StartupManager.Models;
4 | using StartupManager.Services.Directories;
5 | using StartupManager.Services.Registries;
6 | using StartupManager.Services.Schedulers;
7 | using StartupManager.Services.Startup;
8 |
9 | namespace StartupManager.Commands.StartupToggle {
10 | public static class EnableDisableCommandHandler {
11 | private static IRegistryService RegistryService = new RegistryService();
12 | private static IDirectoryService DirectoryService = new DirectoryService();
13 | private static ITaskSchedulerService TaskSchedulerService = new TaskSchedulerService();
14 | private static IStartupQueryService StartupQueryService = new StartupQueryService();
15 | public static IEnumerable Run(string name, bool enable) {
16 | var consoleMessages = new List();
17 | var toggleText = enable ? "enabled" : "disabled";
18 | var startupStates = RegistryService.GetStartupProgramStates();
19 | Models.StartupList? program = null;
20 | if (int.TryParse(name, out int index)) {
21 | program = StartupQueryService.GetStartupByIndex(index);
22 | }
23 | program ??= StartupQueryService.GetStartupByName(name, startupStates);
24 |
25 | if (program == null)
26 | {
27 | return WriteProgramNotFoundConsoleOutput(name);
28 | }
29 |
30 | switch (program.Type) {
31 | case Models.StartupList.StartupType.TaskScheduler:
32 | consoleMessages.AddRange(ToggleThroughTaskScheduler(enable, toggleText, program));
33 | break;
34 | case Models.StartupList.StartupType.Regedit:
35 | case Models.StartupList.StartupType.Shortcut:
36 | consoleMessages.AddRange(ToggleThroughRegedit(enable, toggleText, program));
37 | break;
38 | }
39 | return consoleMessages;
40 | }
41 |
42 | private static IEnumerable ToggleThroughTaskScheduler(bool enable, string toggleText, Models.StartupList program) {
43 | var result = TaskSchedulerService.ToggleStartupState(program, enable);
44 | switch (result) {
45 | case StateChange.SameState:
46 | return WriteSameStateConsoleOutput(toggleText, program);
47 | case StateChange.Success:
48 | return WriteToggledConsoleOutput(toggleText, program);
49 | case StateChange.Unauthorized:
50 | return WriteRequireAdministratorConsoleOutput(program);
51 | }
52 | return new List();
53 | }
54 |
55 | private static IEnumerable ToggleThroughRegedit(bool enable, string toggleText, Models.StartupList program) {
56 | var result = RegistryService.ToggleStartupState(program, enable);
57 | switch (result) {
58 | case Models.StateChange.SameState:
59 | return WriteSameStateConsoleOutput(toggleText, program);
60 | case Models.StateChange.Success:
61 | return WriteToggledConsoleOutput(toggleText, program);
62 | case Models.StateChange.Unauthorized:
63 | return WriteRequireAdministratorConsoleOutput(program);
64 | }
65 | return new List();
66 | }
67 |
68 | private static IEnumerable WriteProgramNotFoundConsoleOutput(string name) {
69 | return new [] {
70 | new ConsoleColorOutput(WriteMode.Write, "Could not find a program with name/index ", ConsoleColor.Red),
71 | new ConsoleColorOutput(WriteMode.Writeline, name, ConsoleColor.Yellow),
72 | };
73 | }
74 |
75 | private static IEnumerable WriteSameStateConsoleOutput(string toggleText, Models.StartupList program) {
76 | return new [] {
77 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
78 | new ConsoleColorOutput(WriteMode.Writeline, $" is already {toggleText}"),
79 | };
80 | }
81 |
82 | private static IEnumerable WriteRequireAdministratorConsoleOutput(Models.StartupList program) {
83 | return new [] {
84 | new ConsoleColorOutput(WriteMode.Write, $"To modify settings for ", ConsoleColor.Red),
85 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
86 | new ConsoleColorOutput(WriteMode.Writeline, " you need to run the command with administrator (sudo)", ConsoleColor.Red),
87 | };
88 | }
89 |
90 | private static IEnumerable WriteToggledConsoleOutput(string toggleText, Models.StartupList program) {
91 | return new [] {
92 | new ConsoleColorOutput(WriteMode.Write, program.Name, ConsoleColor.Yellow),
93 | new ConsoleColorOutput(WriteMode.Writeline, $" has been {toggleText}"),
94 | };
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Directories/DirectoryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using StartupManager.Models;
6 | using StartupManager.Services.Registries;
7 |
8 | namespace StartupManager.Services.Directories {
9 | public class DirectoryService : IDirectoryService {
10 | private const string DisabledStartupFolderItems = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder";
11 | private static string[] ExecuteableSearchPatterns = new [] { "*.exe", "*.lnk", "*.ps1", "*.cmd" };
12 | private static IRegistryService RegistryService = new RegistryService();
13 | private static string CurrentUserStartup = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
14 | private static string AllUsersStartup = Environment.GetFolderPath(Environment.SpecialFolder.CommonStartup);
15 | public StartupList? GetStartupByPredicate(Func filePredicate, Func disabledPredicate, IEnumerable startupStates) {
16 | var files = GetFiles(CurrentUserStartup, ExecuteableSearchPatterns);
17 | var path = files.FirstOrDefault(filePredicate);
18 | if (path == null) {
19 | files = GetFiles(AllUsersStartup, ExecuteableSearchPatterns);
20 | path = files.FirstOrDefault(filePredicate);
21 | if (path == null) {
22 | return null;
23 | }
24 | var fileName = Path.GetFileName(path);
25 | var parsedName = Path.GetFileNameWithoutExtension(path);
26 | var disabled = startupStates.Any(disabledPredicate);
27 |
28 | return new StartupList(parsedName, path, requireAdministrator : true, disabled, StartupList.StartupType.Shortcut, allUsers : true, string.Empty, DisabledStartupFolderItems, fileName);
29 | } else {
30 | var parsedName = Path.GetFileNameWithoutExtension(path);
31 | var fileName = Path.GetFileName(path);
32 | var disabled = startupStates.Any(disabledPredicate);
33 | return new StartupList(parsedName, path, requireAdministrator : false, disabled, StartupList.StartupType.Shortcut, allUsers : false, string.Empty, DisabledStartupFolderItems, fileName);
34 | }
35 | }
36 |
37 | public StartupList? GetStartupByPredicate(Func predicate, Func disabledPredicate) {
38 | var startupStates = RegistryService.GetStartupProgramStates();
39 | return GetStartupByPredicate(predicate, disabledPredicate, startupStates);
40 | }
41 |
42 | public StartupList? GetStartupByName(string name, IEnumerable startupStates) {
43 | Func namePredicate = x => Path.GetFileNameWithoutExtension(x).Equals(name, StringComparison.OrdinalIgnoreCase);
44 | Func disabledPredicate = x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && x.Disabled;
45 | return GetStartupByPredicate(namePredicate, disabledPredicate, startupStates);
46 | }
47 |
48 | public StartupList? GetStartupByName(string name) {
49 | var startupStates = RegistryService.GetStartupProgramStates();
50 | return GetStartupByName(name, startupStates);
51 | }
52 |
53 | public IEnumerable GetStartupPrograms() {
54 | var startupStates = RegistryService.GetStartupProgramStates();
55 | return GetStartupPrograms(startupStates);
56 | }
57 |
58 | public IEnumerable GetStartupPrograms(IEnumerable startupStates) {
59 | var programs = new List();
60 |
61 | var currentUserStartups = GetShellStartup(allUsers: false, CurrentUserStartup, startupStates);
62 | programs.AddRange(currentUserStartups);
63 |
64 | var allUserStartups = GetShellStartup(allUsers: true, AllUsersStartup, startupStates);
65 | programs.AddRange(allUserStartups);
66 |
67 | return programs;
68 | }
69 |
70 | private static IEnumerable GetShellStartup(bool allUsers, string path, IEnumerable startupStates) {
71 | var currentStartups = GetFiles(path, ExecuteableSearchPatterns).Select(name => {
72 | var fileName = Path.GetFileName(name);
73 | var disabled = startupStates.Any(x => x.Name.Equals(fileName, StringComparison.Ordinal) && x.Disabled);
74 |
75 | return new StartupList(Path.GetFileNameWithoutExtension(name), name, requireAdministrator : allUsers, disabled, StartupList.StartupType.Shortcut, allUsers : allUsers, DisabledStartupFolderItems, string.Empty, fileName);
76 | });
77 | return currentStartups.ToList();
78 | }
79 |
80 | public static IEnumerable GetFiles(string path, string[] searchPatterns, SearchOption searchOption = SearchOption.TopDirectoryOnly) {
81 | if (string.IsNullOrWhiteSpace(path))
82 | return new string[0];
83 |
84 | return searchPatterns.SelectMany(searchPattern => Directory.EnumerateFiles(path, searchPattern, searchOption));
85 | }
86 |
87 | public void RemoveProgramFromStartup(StartupList program) {
88 | File.Delete(program.Path);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # Changelog
3 |
4 | All notable changes to this project will be documented in this file.
5 |
6 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
7 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8 |
9 | ## [Unreleased]
10 |
11 | ## [1.6.4] - 2025-12-05
12 |
13 | ### Changed
14 |
15 | - Updated to .NET 10
16 |
17 | ### Added
18 |
19 | - Added priority support for startup programs (ProcessPriority property) implemented by [@AliRezaBeigy](https://github.com/AliRezaBeigy)
20 |
21 | ## [1.6.3] - 2025-08-10
22 |
23 | ### Added
24 |
25 | - #6 Ability to disable/enable by index
26 |
27 | ### Changed
28 |
29 | - Updated to .NET 9
30 |
31 | ## [1.6.2] - 2021-04-12
32 |
33 | ### Changed
34 |
35 | - Updated to .NET 5
36 | - Updated TaskScheduler dependency
37 | - Updated Microsoft.Win32.Registry dependency
38 |
39 | ### Fixed
40 |
41 | - #21 Scoop installation issue due to older .NET should be resolved
42 | - Fixed potential null pointer warnings.
43 |
44 | ## [1.6.1] - 2020-07-18
45 |
46 | ### Fixed
47 |
48 | - #15 Fixed an issue with DirectoryService trying to search an empty path.
49 |
50 | ### Changed
51 |
52 | - Updated TaskScheduler dependency
53 |
54 | ## [1.6.0] - 2020-06-12
55 |
56 | No new functionality was added in this release, but almost all packages were upgraded, which has resulted in a minor performance increase.
57 |
58 | Before
59 |
60 | ```text
61 | Avg: 559.7063ms
62 | Min: 553.4852ms
63 | Max: 575.3427ms
64 | ```
65 |
66 | After
67 |
68 | ```text
69 | Avg: 456.1703ms
70 | Min: 450.5132ms
71 | Max: 461.5471ms
72 | ```
73 |
74 | Tested on my own machine, results may vary.
75 |
76 | ### Changed
77 |
78 | - Updated to .NET Core 3.1
79 | - Updated TaskScheduler dependency
80 | - Updated Registry dependency
81 | - Updated System.Commandline framework
82 |
83 | ## [1.5.0] - 2019-11-10
84 |
85 | ### Added
86 |
87 | - Made the list output on average twice as fast previous versions (On test machines).
88 | - Enable/Disable commands have also gotten a performance boost.
89 | - Remove command has also gotten a small performance boost.
90 | - Add command has also gotten a small performance boost.
91 |
92 | ### Fixed
93 |
94 | - In some cases an exception would be thrown when trying to Enable/Disable a program, this is no longer the case.
95 | - It was possible to add a new startup item with the same name as an existing one in some cases.
96 | - The remove command wasn't able to remove shortcut based Startup programs.
97 |
98 | ## [1.4.1] - 2019-11-09
99 |
100 | ### Fixed
101 |
102 | - Version number wasn't aligned for the version command.
103 |
104 | ## [1.4.0] - 2019-11-08
105 |
106 | ### Added
107 |
108 | - #2 It's now possible to remove programs from starting with windows.
109 |
110 | ### Fixed
111 |
112 | - List output had too many newlines as seperator between header.
113 | - #7 Documentation has been updated
114 |
115 | ## [1.3.0] - 2019-11-05
116 |
117 | It's now finally possible to add programs to startup
118 |
119 | ### Added
120 |
121 | - #1 Added a startup command
122 | - It's possible to add startup tasks for the current user to run as Administrator as well.
123 | - List command can now also see Startup tasks in Task Scheduler.
124 | - It's now possible to enable/disable startup tasks from Task Scheduler.
125 |
126 | ## [1.2.1] - 2019-11-02
127 |
128 | No new application features, but streamlined build, publish and release process.
129 |
130 | ### Added
131 |
132 | - Github actions
133 | - Build
134 | - Publish
135 | - Release
136 |
137 | ## [1.2.0] - 2019-10-29
138 |
139 | ### Added
140 |
141 | - #5 Added a different enabled/disabled message when the state wouldn't change
142 |
143 | ### Changed
144 |
145 | - #4 Changed default list output
146 |
147 | ### Fixed
148 |
149 | - #3 Fixed a bug with "empty" entries in the list output.
150 |
151 | ## [1.1.0] - 2019-10-27
152 |
153 | ### Fixed
154 |
155 | - List command did not find all programs
156 | - It has been updated to look in more locations now.
157 |
158 | ## [1.0.0] - 2019-10-26
159 |
160 | ### Added
161 |
162 | - List command
163 | - It's now possible to list the programs starting with windows.
164 | - Enable/Disable command
165 | - It's now possible to enable/disable existing programs starting with windows.
166 |
167 | [Unreleased]: https://github.com/Faustvii/StartupManager/compare/1.6.3...HEAD
168 | [1.6.3]: https://github.com/Faustvii/StartupManager/compare/1.6.2...1.6.3
169 | [1.6.2]: https://github.com/Faustvii/StartupManager/compare/1.6.1...1.6.2
170 | [1.6.1]: https://github.com/Faustvii/StartupManager/compare/1.6.0...1.6.1
171 | [1.6.0]: https://github.com/Faustvii/StartupManager/compare/1.5.0...1.6.0
172 | [1.5.0]: https://github.com/Faustvii/StartupManager/compare/1.4.1...1.5.0
173 | [1.4.1]: https://github.com/Faustvii/StartupManager/compare/1.4.0...1.4.1
174 | [1.4.0]: https://github.com/Faustvii/StartupManager/compare/1.3.0...1.4.0
175 | [1.3.0]: https://github.com/Faustvii/StartupManager/compare/1.2.1...1.3.0
176 | [1.2.1]: https://github.com/Faustvii/StartupManager/compare/1.2.0...1.2.1
177 | [1.2.0]: https://github.com/Faustvii/StartupManager/compare/1.1.0...1.2.0
178 | [1.1.0]: https://github.com/Faustvii/StartupManager/compare/1.0.0...1.1.0
179 | [1.0.0]: https://github.com/Faustvii/StartupManager/releases/tag/1.0.0
180 |
181 |
--------------------------------------------------------------------------------
/src/StartupManager/Commands/Add/AddCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using StartupManager.ConsoleOutputters;
6 | using StartupManager.Models;
7 |
8 | namespace StartupManager.Commands.Add {
9 | public static class AddCommand {
10 | private static string NameId = "Name";
11 | private static string PathId = "Path";
12 | private static string ArgumentsId = "Arguments";
13 | private static string AdministratorId = "Administrator";
14 | private static string AllUserId = "All Users";
15 | private static string PriorityId = "Priority";
16 |
17 | public static void Run(string? name, FileInfo? path, string? arguments, bool? admin, bool? allUsers, string? priority) {
18 | if (name == null || (path == null || !path.Exists) || arguments == null || admin == null || allUsers == null || priority == null) {
19 | var steps = GetWizardSteps(name, path, arguments, admin, allUsers, priority);
20 | steps = ConsoleStepWizard.UserWizard("Let's guide you through settings up a new startup program", steps);
21 | System.Console.WriteLine();
22 |
23 | var startupProgram = ParseUserInfo(steps, name, path, arguments, admin, allUsers, priority);
24 | ValidateInformationWithUser(startupProgram);
25 | System.Console.WriteLine();
26 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Green, "Does this look correct? y/n: ");
27 |
28 | var correct = ConsoleStepWizard.PromptUserForBool("y", "n", "Does this look correct? y/n: ");
29 | if (correct) {
30 | ExecuteHandler(startupProgram);
31 | } else {
32 | System.Console.WriteLine("Sorry to hear that, please try again");
33 | }
34 | } else {
35 | var startupProgram = ParseUserInfo(new List(), name, path, arguments, admin, allUsers, priority);
36 | ExecuteHandler(startupProgram);
37 | }
38 | }
39 |
40 | private static void ExecuteHandler(StartupProgram startupProgram) {
41 | var messages = AddCommandHandler.Run(startupProgram);
42 | ConsoleOutputWriter.WriteToConsole(messages);
43 | }
44 |
45 | private static void ValidateInformationWithUser(StartupProgram data) {
46 | Console.Write($"{NameId}: ");
47 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.Name}");
48 | Console.Write($"{PathId}: ");
49 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.File}");
50 | Console.Write($"{ArgumentsId}: ");
51 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.Arguments}");
52 | Console.Write($"{AdministratorId}: ");
53 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.Administrator}");
54 | Console.Write($"{AllUserId}: ");
55 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.AllUsers}");
56 | Console.Write($"{PriorityId}: ");
57 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, $"{data.Priority}");
58 | }
59 |
60 | private static IEnumerable GetWizardSteps(string? name, FileInfo? file, string? arguments, bool? admin, bool? allUsers, string? priority) {
61 | var steps = new List();
62 | if (name == null || string.IsNullOrWhiteSpace(name)) {
63 | steps.Add(new ConsoleStep(NameId, "What's the name of the program?: ", string.Empty));
64 | }
65 | if (file == null || !file.Exists) {
66 | steps.Add(new ConsoleStep(PathId, "What's the path to the program?: ", new FileInfo("program")));
67 | }
68 | if (arguments == null) {
69 | steps.Add(new ConsoleStep(ArgumentsId, "What's the arguments for the program?: ", string.Empty));
70 | }
71 | if (admin == null) {
72 | steps.Add(new ConsoleStep(AdministratorId, "Do you want to run this program as an Administrator? y/n: ", false));
73 | }
74 | if (allUsers == null) {
75 | steps.Add(new ConsoleStep(AllUserId, "Do you want to run this program for all users? y/n: ", false));
76 | }
77 | if (priority == null) {
78 | steps.Add(new ConsoleStep(PriorityId, "What priority should this program run at? (Idle/BelowNormal/Normal/AboveNormal/High/Realtime) [Normal]: ", "Normal"));
79 | }
80 | return steps;
81 | }
82 |
83 | private static StartupProgram ParseUserInfo(IEnumerable steps, string? name, FileInfo? file, string? arguments, bool? admin, bool? allUsers, string? priority) {
84 | var nameVal = name ?? (string) steps.Single(x => x.Id == NameId).UserValue;
85 | var fileVal = file ?? (FileInfo) steps.Single(x => x.Id == PathId).UserValue;
86 | if (!fileVal.Exists) {
87 | fileVal = (FileInfo) steps.Single(x => x.Id == PathId).UserValue;
88 | }
89 | var argumentsVal = arguments ?? (string) steps.Single(x => x.Id == ArgumentsId).UserValue;
90 | var adminVal = admin ?? (bool) steps.Single(x => x.Id == AdministratorId).UserValue;
91 | var allUserVal = allUsers ?? (bool) steps.Single(x => x.Id == AllUserId).UserValue;
92 | var priorityStr = priority ?? (string) steps.Single(x => x.Id == PriorityId).UserValue;
93 | var priorityVal = ParsePriority(priorityStr);
94 | return new StartupProgram(nameVal, fileVal, argumentsVal, adminVal, allUserVal, priorityVal);
95 | }
96 |
97 | private static ProcessPriority ParsePriority(string priority) {
98 | if (Enum.TryParse(priority, true, out var result)) {
99 | return result;
100 | }
101 | return ProcessPriority.Normal;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StartupManager
2 |
3 | [](https://www.codefactor.io/repository/github/faustvii/startupmanager)
4 | 
5 |
6 | A tool to manage startup programs on windows through a CLI interface.
7 |
8 | - [StartupManager](#startupmanager)
9 | - [Scoop](#scoop)
10 | - [Commands](#commands)
11 | - [Help](#help)
12 | - [Version](#version)
13 | - [List](#list)
14 | - [Columns explanation](#columns-explanation)
15 | - [Enable](#enable)
16 | - [Disable](#disable)
17 | - [Add](#add)
18 | - [Examples of usage](#examples-of-usage)
19 | - [Remove](#remove)
20 |
21 | ## Scoop
22 |
23 | You can install the application through [Scoop](https://github.com/lukesampson/scoop) if you have my bucket installed.
24 |
25 | I recommend installing through scoop to have the app accessible on your "path"
26 |
27 | `scoop bucket add Faustvii 'https://github.com/Faustvii/scoop-bucket.git'`
28 |
29 | `scoop install StartupManager`
30 |
31 | ## Commands
32 |
33 | There are currently six functional commands `list`, `enable`, `disable`, `add`, `remove`, `set-priority`
34 |
35 | ### Help
36 |
37 | Examples of usage `StartupManager.exe --help`
38 |
39 | ```text
40 | Usage:
41 | StartupManager [options] [command]
42 |
43 | Options:
44 | --version Display version information
45 |
46 | Commands:
47 | l, list Lists the current startup programs
48 | d, disable Disables one of the current startup programs
49 | e, enable Enables one of the current startup programs
50 | a, add Adds a program to startup with windows
51 | r, remove Removes the specified program from startup
52 | p, set-priority Sets the priority of a Task Scheduler startup program
53 | ```
54 |
55 | The help command can also be used on any commands;
56 |
57 | Examples of usage `StartupManager.exe list --help`
58 |
59 | ```text
60 | list:
61 | Lists the current startup programs
62 |
63 | Usage:
64 | StartupManager list [options]
65 |
66 | Options:
67 | -d, --detailed Shows additional output about the startup programs
68 | ```
69 |
70 | ### Version
71 |
72 | Displays the current version of the application
73 |
74 | Examples of usage `StartupManager.exe --version`
75 |
76 | ```text
77 | 1.6.3
78 | ```
79 |
80 | ### List
81 |
82 | Will display a list of applications that starts with windows.
83 |
84 | It's possible to use the `--detailed`/`-d` option to get a table showing the path and arguments for the entries as well.
85 |
86 | Examples of usage `StartupManager.exe list` or `StartupManager.exe l`
87 |
88 | ```text
89 | Applications starting with windows:
90 | Name Admin Enabled
91 |
92 | f.lux [√]
93 | Steam [√]
94 | Discord [√]
95 | SecurityHealth [√] [√]
96 | ```
97 |
98 | Examples of usage `StartupManager.exe list --detailed` or `StartupManager.exe l -d`
99 |
100 | ```text
101 | Applications starting with windows:
102 | # Name Admin Enabled Path
103 |
104 | 1 f.lux [√] "C:\Users\Faust\Scoop\apps\flux\current\flux.exe" /noshow
105 | 2 Steam [√] "C:\Users\Faust\Scoop\apps\steam\current\steam.exe" -silent
106 | 3 Discord [√] C:\Users\Faust\scoop\apps\discord\current\Discord.exe --start-minimized
107 | 4 SecurityHealth [√] [√] C:\Windows\system32\SecurityHealthSystray.exe
108 | ```
109 |
110 | #### Columns explanation
111 |
112 | - `#`
113 | - Numeric index used for referencing items in other commands (e.g., disabling or enabling).
114 | - `Name` is either the
115 | - Registry key name
116 | - Shortcut filename without extension
117 | - Task scheduler name
118 | - `Admin`
119 | - Shows if you need to have administrator priviliges to modify it
120 | - `Enabled`
121 | - Shows whether the startup item is currently enabled ([√]) or disabled (blank).
122 | - `Path`
123 | - Shows the path and potential arguments to the application
124 |
125 | ### Enable
126 |
127 | Examples of usage `StartupManager.exe enable Steam` or `StartupManager.exe e Steam` or `StartupManager.exe e 2` (Assuming index 2 is steam)
128 |
129 | ```text
130 | Steam is already enabled
131 | ```
132 |
133 | Or
134 |
135 | ```text
136 | Steam has been enabled
137 | ```
138 |
139 | ### Disable
140 |
141 | Examples of usage `StartupManager.exe disable Steam` or `StartupManager.exe d Steam` or `StartupManager.exe d 2` (Assuming index 2 is steam)
142 |
143 | ```text
144 | Steam has been disabled
145 | ```
146 |
147 | Or
148 |
149 | ```text
150 | Steam is already disabled
151 | ```
152 |
153 | ### Add
154 |
155 | The add command has a "wizard" that will guide you through the required steps, if you do not supply all the needed arguments when using the command.
156 |
157 | #### Examples of usage
158 |
159 | `StartupManager add MyWelcomeApp "C:\new folder\test.bat" "Hello to you sir!" false false` would output
160 |
161 | ```text
162 | Added MyWelcomeApp to startup
163 | ```
164 |
165 | `StartupManager add [Name] [Path] [Arguments] [RunAsAdministrator] [ForAllUsers]`
166 |
167 | `StartupManager add MyWelcomeApp` or `StartupManager a MyWelcomeApp` would output this (Notice it doesn't ask for the name because it was supplied already)
168 |
169 | ```text
170 | PS C:\> StartupManager add MyWelcomeApp
171 | Let's guide you through settings up a new startup program
172 |
173 | What's the path to the program?: C:\new folder\test.bat
174 | What's the arguments for the program?: "Hello to you sir!"
175 | Do you want to run this program as an Administrator? y/n: n
176 | Do you want to run this program for all users? y/n: n
177 |
178 | Name: MyWelcomeApp
179 | Path: C:\new folder\test.bat
180 | Arguments: "Hello to you sir!"
181 | Administrator: False
182 | All Users: False
183 |
184 | Does this look correct? y/n: y
185 | Added MyWelcomeApp to startup
186 | ```
187 |
188 | ### Remove
189 |
190 | This command will remove a program from starting with windows by deleting the registry entry, task or shortcut.
191 |
192 | Examples of usage
193 |
194 | `StartupManager remove MyWelcomeApp` would output
195 |
196 | ```text
197 | Are you sure you want to delete 'MyWelcomeApp' y/n: y
198 | MyWelcomeApp has been removed
199 | ```
200 |
201 | It's possible to skip the confirmation by adding the option `--confirm` or `-c`
202 |
203 | `StartupManager remove MyWelcomeApp --confirm` or `StartupManager r MyWelcomeApp -c` would output
204 |
205 | ```text
206 | MyWelcomeApp has been removed
207 | ```
208 |
209 | ### Set-Priority
210 |
211 | This command allows you to set the priority level for Task Scheduler startup programs. The priority levels available are: Idle, BelowNormal, Normal, AboveNormal, High, or Realtime.
212 |
213 | Examples of usage
214 |
215 | `StartupManager set-priority MyWelcomeApp Normal` or `StartupManager p MyWelcomeApp Normal` would output
216 |
217 | ```text
218 | Priority for MyWelcomeApp has been set to Normal
219 | ```
220 |
221 | You can also use the index from the list command:
222 |
223 | `StartupManager p 1 High` would set the priority of the program at index 1 to High.
224 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 |
33 | # Visual Studio 2015/2017 cache/options directory
34 | .vs/
35 | # Uncomment if you have tasks that create the project's static files in wwwroot
36 | #wwwroot/
37 |
38 | # Visual Studio 2017 auto generated files
39 | Generated\ Files/
40 |
41 | # MSTest test Results
42 | [Tt]est[Rr]esult*/
43 | [Bb]uild[Ll]og.*
44 |
45 | # NUnit
46 | *.VisualState.xml
47 | TestResult.xml
48 | nunit-*.xml
49 |
50 | # Build Results of an ATL Project
51 | [Dd]ebugPS/
52 | [Rr]eleasePS/
53 | dlldata.c
54 |
55 | # Benchmark Results
56 | BenchmarkDotNet.Artifacts/
57 |
58 | # .NET Core
59 | project.lock.json
60 | project.fragment.lock.json
61 | artifacts/
62 |
63 | # StyleCop
64 | StyleCopReport.xml
65 |
66 | # Files built by Visual Studio
67 | *_i.c
68 | *_p.c
69 | *_h.h
70 | *.ilk
71 | *.meta
72 | *.obj
73 | *.iobj
74 | *.pch
75 | *.pdb
76 | *.ipdb
77 | *.pgc
78 | *.pgd
79 | *.rsp
80 | *.sbr
81 | *.tlb
82 | *.tli
83 | *.tlh
84 | *.tmp
85 | *.tmp_proj
86 | *_wpftmp.csproj
87 | *.log
88 | *.vspscc
89 | *.vssscc
90 | .builds
91 | *.pidb
92 | *.svclog
93 | *.scc
94 |
95 | # Chutzpah Test files
96 | _Chutzpah*
97 |
98 | # Visual C++ cache files
99 | ipch/
100 | *.aps
101 | *.ncb
102 | *.opendb
103 | *.opensdf
104 | *.sdf
105 | *.cachefile
106 | *.VC.db
107 | *.VC.VC.opendb
108 |
109 | # Visual Studio profiler
110 | *.psess
111 | *.vsp
112 | *.vspx
113 | *.sap
114 |
115 | # Visual Studio Trace Files
116 | *.e2e
117 |
118 | # TFS 2012 Local Workspace
119 | $tf/
120 |
121 | # Guidance Automation Toolkit
122 | *.gpState
123 |
124 | # ReSharper is a .NET coding add-in
125 | _ReSharper*/
126 | *.[Rr]e[Ss]harper
127 | *.DotSettings.user
128 |
129 | # JustCode is a .NET coding add-in
130 | .JustCode
131 |
132 | # TeamCity is a build add-in
133 | _TeamCity*
134 |
135 | # DotCover is a Code Coverage Tool
136 | *.dotCover
137 |
138 | # AxoCover is a Code Coverage Tool
139 | .axoCover/*
140 | !.axoCover/settings.json
141 |
142 | # Visual Studio code coverage results
143 | *.coverage
144 | *.coveragexml
145 |
146 | # NCrunch
147 | _NCrunch_*
148 | .*crunch*.local.xml
149 | nCrunchTemp_*
150 |
151 | # MightyMoose
152 | *.mm.*
153 | AutoTest.Net/
154 |
155 | # Web workbench (sass)
156 | .sass-cache/
157 |
158 | # Installshield output folder
159 | [Ee]xpress/
160 |
161 | # DocProject is a documentation generator add-in
162 | DocProject/buildhelp/
163 | DocProject/Help/*.HxT
164 | DocProject/Help/*.HxC
165 | DocProject/Help/*.hhc
166 | DocProject/Help/*.hhk
167 | DocProject/Help/*.hhp
168 | DocProject/Help/Html2
169 | DocProject/Help/html
170 |
171 | # Click-Once directory
172 | publish/
173 |
174 | # Publish Web Output
175 | *.[Pp]ublish.xml
176 | *.azurePubxml
177 | # Note: Comment the next line if you want to checkin your web deploy settings,
178 | # but database connection strings (with potential passwords) will be unencrypted
179 | *.pubxml
180 | *.publishproj
181 |
182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
183 | # checkin your Azure Web App publish settings, but sensitive information contained
184 | # in these scripts will be unencrypted
185 | PublishScripts/
186 |
187 | # NuGet Packages
188 | *.nupkg
189 | # NuGet Symbol Packages
190 | *.snupkg
191 | # The packages folder can be ignored because of Package Restore
192 | **/[Pp]ackages/*
193 | # except build/, which is used as an MSBuild target.
194 | !**/[Pp]ackages/build/
195 | # Uncomment if necessary however generally it will be regenerated when needed
196 | #!**/[Pp]ackages/repositories.config
197 | # NuGet v3's project.json files produces more ignorable files
198 | *.nuget.props
199 | *.nuget.targets
200 |
201 | # Microsoft Azure Build Output
202 | csx/
203 | *.build.csdef
204 |
205 | # Microsoft Azure Emulator
206 | ecf/
207 | rcf/
208 |
209 | # Windows Store app package directories and files
210 | AppPackages/
211 | BundleArtifacts/
212 | Package.StoreAssociation.xml
213 | _pkginfo.txt
214 | *.appx
215 | *.appxbundle
216 | *.appxupload
217 |
218 | # Visual Studio cache files
219 | # files ending in .cache can be ignored
220 | *.[Cc]ache
221 | # but keep track of directories ending in .cache
222 | !?*.[Cc]ache/
223 |
224 | # Others
225 | ClientBin/
226 | ~$*
227 | *~
228 | *.dbmdl
229 | *.dbproj.schemaview
230 | *.jfm
231 | *.pfx
232 | *.publishsettings
233 | orleans.codegen.cs
234 |
235 | # Including strong name files can present a security risk
236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
237 | #*.snk
238 |
239 | # Since there are multiple workflows, uncomment next line to ignore bower_components
240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
241 | #bower_components/
242 |
243 | # RIA/Silverlight projects
244 | Generated_Code/
245 |
246 | # Backup & report files from converting an old project file
247 | # to a newer Visual Studio version. Backup files are not needed,
248 | # because we have git ;-)
249 | _UpgradeReport_Files/
250 | Backup*/
251 | UpgradeLog*.XML
252 | UpgradeLog*.htm
253 | ServiceFabricBackup/
254 | *.rptproj.bak
255 |
256 | # SQL Server files
257 | *.mdf
258 | *.ldf
259 | *.ndf
260 |
261 | # Business Intelligence projects
262 | *.rdl.data
263 | *.bim.layout
264 | *.bim_*.settings
265 | *.rptproj.rsuser
266 | *- [Bb]ackup.rdl
267 | *- [Bb]ackup ([0-9]).rdl
268 | *- [Bb]ackup ([0-9][0-9]).rdl
269 |
270 | # Microsoft Fakes
271 | FakesAssemblies/
272 |
273 | # GhostDoc plugin setting file
274 | *.GhostDoc.xml
275 |
276 | # Node.js Tools for Visual Studio
277 | .ntvs_analysis.dat
278 | node_modules/
279 |
280 | # Visual Studio 6 build log
281 | *.plg
282 |
283 | # Visual Studio 6 workspace options file
284 | *.opt
285 |
286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
287 | *.vbw
288 |
289 | # Visual Studio LightSwitch build output
290 | **/*.HTMLClient/GeneratedArtifacts
291 | **/*.DesktopClient/GeneratedArtifacts
292 | **/*.DesktopClient/ModelManifest.xml
293 | **/*.Server/GeneratedArtifacts
294 | **/*.Server/ModelManifest.xml
295 | _Pvt_Extensions
296 |
297 | # Paket dependency manager
298 | .paket/paket.exe
299 | paket-files/
300 |
301 | # FAKE - F# Make
302 | .fake/
303 |
304 | # CodeRush personal settings
305 | .cr/personal
306 |
307 | # Python Tools for Visual Studio (PTVS)
308 | __pycache__/
309 | *.pyc
310 |
311 | # Cake - Uncomment if you are using it
312 | # tools/**
313 | # !tools/packages.config
314 |
315 | # Tabs Studio
316 | *.tss
317 |
318 | # Telerik's JustMock configuration file
319 | *.jmconfig
320 |
321 | # BizTalk build output
322 | *.btp.cs
323 | *.btm.cs
324 | *.odx.cs
325 | *.xsd.cs
326 |
327 | # OpenCover UI analysis results
328 | OpenCover/
329 |
330 | # Azure Stream Analytics local run output
331 | ASALocalRun/
332 |
333 | # MSBuild Binary and Structured Log
334 | *.binlog
335 |
336 | # NVidia Nsight GPU debugger configuration file
337 | *.nvuser
338 |
339 | # MFractors (Xamarin productivity tool) working folder
340 | .mfractor/
341 |
342 | # Local History for Visual Studio
343 | .localhistory/
344 |
345 | # BeatPulse healthcheck temp database
346 | healthchecksdb
347 |
348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
349 | MigrationBackup/
350 |
--------------------------------------------------------------------------------
/src/StartupManager/UI/InteractiveMenu.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using StartupManager.Commands.Add;
4 | using StartupManager.Commands.Remove;
5 | using StartupManager.Commands.SetPriority;
6 | using StartupManager.Commands.StartupList;
7 | using StartupManager.Commands.StartupToggle;
8 | using StartupManager.ConsoleOutputters;
9 |
10 | namespace StartupManager.UI {
11 | public static class InteractiveMenu {
12 | public static void Show() {
13 | while (true) {
14 | Console.Clear();
15 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan,
16 | "╔════════════════════════════════════════════════════════════╗",
17 | "║ Startup Manager - Interactive Menu ║",
18 | "╚════════════════════════════════════════════════════════════╝",
19 | ""
20 | );
21 |
22 | // Display the startup list
23 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Yellow, "Current Startup Programs:");
24 | Console.WriteLine();
25 | ListCommand.Run(false);
26 | Console.WriteLine();
27 |
28 | // Display menu options
29 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, "Menu Options:");
30 | Console.WriteLine(" 1. Add a new startup program");
31 | Console.WriteLine(" 2. Remove a startup program");
32 | Console.WriteLine(" 3. Enable a startup program");
33 | Console.WriteLine(" 4. Disable a startup program");
34 | Console.WriteLine(" 5. Set priority for a startup program");
35 | Console.WriteLine(" 6. Refresh list");
36 | Console.WriteLine(" 7. Show detailed list");
37 | Console.WriteLine(" 0. Exit");
38 | Console.WriteLine();
39 |
40 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Cyan, "Enter your choice: ");
41 | var choice = Console.ReadLine()?.Trim();
42 |
43 | if (string.IsNullOrEmpty(choice)) {
44 | continue;
45 | }
46 |
47 | switch (choice) {
48 | case "1":
49 | HandleAdd();
50 | break;
51 | case "2":
52 | HandleRemove();
53 | break;
54 | case "3":
55 | HandleEnable();
56 | break;
57 | case "4":
58 | HandleDisable();
59 | break;
60 | case "5":
61 | HandleSetPriority();
62 | break;
63 | case "6":
64 | // Just refresh by continuing the loop
65 | break;
66 | case "7":
67 | HandleDetailedList();
68 | break;
69 | case "0":
70 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Green, "Goodbye!");
71 | return;
72 | default:
73 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Invalid choice. Please try again.");
74 | Console.WriteLine("Press any key to continue...");
75 | Console.ReadKey();
76 | break;
77 | }
78 | }
79 | }
80 |
81 | private static void HandleAdd() {
82 | Console.Clear();
83 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Add New Startup Program");
84 | Console.WriteLine();
85 | AddCommand.Run(null, null, null, null, null, null);
86 | Console.WriteLine();
87 | Console.WriteLine("Press any key to continue...");
88 | Console.ReadKey();
89 | }
90 |
91 | private static void HandleRemove() {
92 | Console.Clear();
93 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Remove Startup Program");
94 | Console.WriteLine();
95 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Yellow, "Enter the name or index of the program to remove: ");
96 | var name = Console.ReadLine()?.Trim();
97 | if (!string.IsNullOrEmpty(name)) {
98 | RemoveCommand.Run(name, true);
99 | } else {
100 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Name or index cannot be empty.");
101 | }
102 | Console.WriteLine();
103 | Console.WriteLine("Press any key to continue...");
104 | Console.ReadKey();
105 | }
106 |
107 | private static void HandleEnable() {
108 | Console.Clear();
109 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Enable Startup Program");
110 | Console.WriteLine();
111 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Yellow, "Enter the name or index of the program to enable: ");
112 | var name = Console.ReadLine()?.Trim();
113 | if (!string.IsNullOrEmpty(name)) {
114 | EnableCommand.Run(name);
115 | } else {
116 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Name or index cannot be empty.");
117 | }
118 | Console.WriteLine();
119 | Console.WriteLine("Press any key to continue...");
120 | Console.ReadKey();
121 | }
122 |
123 | private static void HandleDisable() {
124 | Console.Clear();
125 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Disable Startup Program");
126 | Console.WriteLine();
127 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Yellow, "Enter the name or index of the program to disable: ");
128 | var name = Console.ReadLine()?.Trim();
129 | if (!string.IsNullOrEmpty(name)) {
130 | DisableCommand.Run(name);
131 | } else {
132 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Name or index cannot be empty.");
133 | }
134 | Console.WriteLine();
135 | Console.WriteLine("Press any key to continue...");
136 | Console.ReadKey();
137 | }
138 |
139 | private static void HandleSetPriority() {
140 | Console.Clear();
141 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Set Priority for Startup Program");
142 | Console.WriteLine();
143 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Yellow, "Enter the name or index of the program: ");
144 | var name = Console.ReadLine()?.Trim();
145 | if (string.IsNullOrEmpty(name)) {
146 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Name or index cannot be empty.");
147 | Console.WriteLine();
148 | Console.WriteLine("Press any key to continue...");
149 | Console.ReadKey();
150 | return;
151 | }
152 |
153 | ConsoleColorHelper.ConsoleWriteColored(ConsoleColor.Yellow, "Enter priority (Idle, BelowNormal, Normal, AboveNormal, High, Realtime): ");
154 | var priority = Console.ReadLine()?.Trim();
155 | if (!string.IsNullOrEmpty(priority)) {
156 | SetPriorityCommand.Run(name, priority);
157 | } else {
158 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Red, "Priority cannot be empty.");
159 | }
160 | Console.WriteLine();
161 | Console.WriteLine("Press any key to continue...");
162 | Console.ReadKey();
163 | }
164 |
165 | private static void HandleDetailedList() {
166 | Console.Clear();
167 | ConsoleColorHelper.ConsoleWriteLineColored(ConsoleColor.Cyan, "Detailed Startup Programs List");
168 | Console.WriteLine();
169 | ListCommand.Run(true);
170 | Console.WriteLine();
171 | Console.WriteLine("Press any key to continue...");
172 | Console.ReadKey();
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/StartupManager.Services/Schedulers/TaskSchedulerService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Win32.TaskScheduler;
5 | using StartupManager.Models;
6 | using StartupManager.Services.Identity;
7 |
8 | namespace StartupManager.Services.Schedulers {
9 | public class TaskSchedulerService : ITaskSchedulerService {
10 | private static IWindowsIdentityService WindowsIdentityService = new WindowsIdentityService();
11 | private const string TaskSchedulerFolder = "StartupManager";
12 | public StartupList? GetStartupByPredicate(Func predicate) {
13 | var startupList = GetStartupList(predicate, GetFolderPredicate(false));
14 | if (startupList == null) {
15 | return null;
16 | }
17 | return startupList;
18 | }
19 | public StartupList? GetStartupByName(string name) {
20 | Func namePredicate = x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Logon);
21 | return GetStartupByPredicate(namePredicate);
22 | }
23 |
24 | public IEnumerable GetStartupPrograms(bool includeWindows) {
25 | return GetAllTasks(includeWindows).Select(x => Convert(x)).ToList();
26 | }
27 |
28 | public void RemoveProgramFromStartup(string name) {
29 | using(var taskService = new TaskService()) {
30 | var task = taskService.FindTask(name);
31 | taskService.RootFolder.DeleteTask(task.Path);
32 | }
33 | }
34 |
35 | public StateChange ToggleStartupState(StartupList program, bool enable) {
36 | try {
37 | using(var taskService = new TaskService()) {
38 | Func namePredicate = x => x.Name.Equals(program.Name, StringComparison.OrdinalIgnoreCase) && x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Logon);
39 | var task = GetTaskFromFolder(TaskService.Instance.RootFolder, namePredicate, GetFolderPredicate(false)) !;
40 |
41 | var isAlreadyTheRequestState = task.Enabled == enable;
42 | if (isAlreadyTheRequestState) {
43 | return StateChange.SameState;
44 | } else {
45 | task.Enabled = enable;
46 | return StateChange.Success;
47 | }
48 | }
49 | } catch (UnauthorizedAccessException) {
50 | return StateChange.Unauthorized;
51 | }
52 | }
53 |
54 | public StateChange SetPriority(StartupList program, ProcessPriority priority) {
55 | try {
56 | using(var taskService = new TaskService()) {
57 | Func namePredicate = x => x.Name.Equals(program.Name, StringComparison.OrdinalIgnoreCase) && x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Logon);
58 | var task = GetTaskFromFolder(TaskService.Instance.RootFolder, namePredicate, GetFolderPredicate(false)) !;
59 |
60 | var currentPriority = ConvertPriorityFromTask(task.Definition.Settings.Priority);
61 | if (currentPriority == priority) {
62 | return StateChange.SameState;
63 | } else {
64 | var taskDef = task.Definition;
65 | taskDef.Settings.Priority = ConvertPriority(priority);
66 | taskService.RootFolder.RegisterTaskDefinition(task.Path, taskDef, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken);
67 | return StateChange.Success;
68 | }
69 | }
70 | } catch (UnauthorizedAccessException) {
71 | return StateChange.Unauthorized;
72 | }
73 | }
74 |
75 | public Task AddProgramToStartup(StartupProgram program) {
76 | using(var taskService = new TaskService()) {
77 | var currentUser = WindowsIdentityService.CurrentUser();
78 |
79 | var taskDef = taskService.NewTask();
80 | taskDef.RegistrationInfo.Author = $"StartupManager";
81 | taskDef.RegistrationInfo.Description = $"Runs {program.Name} ({program.File.FullName}) at logon of user {currentUser}";
82 | taskDef.Principal.RunLevel = TaskRunLevel.Highest;
83 | taskDef.Settings.ExecutionTimeLimit = TimeSpan.Zero;
84 | taskDef.Settings.StartWhenAvailable = true;
85 | taskDef.Settings.StopIfGoingOnBatteries = false;
86 | taskDef.Settings.Priority = ConvertPriority(program.Priority);
87 |
88 | var action = taskDef.Actions.Add(program.File.FullName, program.Arguments, program.File.DirectoryName);
89 |
90 | var logonTrigger = (LogonTrigger) taskDef.Triggers.AddNew(TaskTriggerType.Logon);
91 | logonTrigger.UserId = currentUser;
92 |
93 | return taskService.RootFolder.RegisterTaskDefinition($"{TaskSchedulerFolder}\\{program.Name}", taskDef);
94 | }
95 | }
96 |
97 | private StartupList? GetStartupList(Func taskPredicate, Func folderPredicate) {
98 | var task = GetTaskFromFolder(TaskService.Instance.RootFolder, taskPredicate, folderPredicate);
99 | if (task != null) {
100 | return Convert(task);
101 | }
102 | return null;
103 | }
104 |
105 | private Task? GetTaskFromFolder(TaskFolder folder, Func taskPredicate, Func folderPredicate) {
106 | Task? task = folder.Tasks.FirstOrDefault(taskPredicate);
107 | if (task == null) {
108 | foreach (var subFolder in folder.SubFolders.Where(folderPredicate)) {
109 | task = GetTaskFromFolder(subFolder, taskPredicate, folderPredicate);
110 | if (task != null) {
111 | return task;
112 | }
113 | }
114 | }
115 | return task;
116 | }
117 |
118 | private static IEnumerable GetAllTasks(bool includeWindows) {
119 | var predicate = GetFolderPredicate(includeWindows);
120 |
121 | return GetFolderTasks(TaskService.Instance.RootFolder, predicate);
122 | }
123 |
124 | private static IEnumerable GetFolderTasks(TaskFolder folder, Func predicate) {
125 | var tasks = new List();
126 | tasks.AddRange(folder.Tasks.Where(x => x.Definition.Triggers.Any(x => x.TriggerType == TaskTriggerType.Logon)));
127 |
128 | foreach (TaskFolder subFolder in folder.SubFolders.Where(predicate)) {
129 | tasks.AddRange(GetFolderTasks(subFolder, predicate));
130 | }
131 |
132 | return tasks;
133 | }
134 |
135 | private static Func GetFolderPredicate(bool includeWindows) {
136 | if (includeWindows) {
137 | return x => true;
138 | } else {
139 | return x => !x.Name.Contains("Microsoft", StringComparison.OrdinalIgnoreCase);
140 | }
141 | }
142 |
143 | private static StartupList Convert(Task task) {
144 | return new StartupList(task.Name,
145 | path : string.Join($" | ", task.Definition.Actions.Select(a => GetPathFromAction(a))),
146 | requireAdministrator : true,
147 | disabled: !task.Enabled,
148 | type : StartupList.StartupType.TaskScheduler,
149 | allUsers : task.Definition.Triggers.Where(t => t.TriggerType == TaskTriggerType.Logon).All(x => ((LogonTrigger) x).UserId != null),
150 | priority : ConvertPriorityFromTask(task.Definition.Settings.Priority));
151 | }
152 |
153 | public static List GetStartupTaskScheduler(bool includeWindows) {
154 |
155 | var tasks = GetAllTasks(includeWindows);
156 | return tasks.Select(x => Convert(x))
157 | .ToList();
158 | }
159 |
160 | private static string GetPathFromAction(Microsoft.Win32.TaskScheduler.Action action) {
161 | switch (action) {
162 | case ExecAction act:
163 | return $"{act.Path} {act.Arguments}";
164 | case EmailAction act:
165 | return $"Email To: {act.To} From: {act.From} Subject: {act.Subject}";
166 | case ComHandlerAction act:
167 | return $"{act.ActionType} - {act}";
168 | case ShowMessageAction act:
169 | return $"{act.ActionType} - {act}";
170 | default:
171 | return $"Unknown action type: '{action.ActionType}'";
172 | }
173 | }
174 |
175 | private static System.Diagnostics.ProcessPriorityClass ConvertPriority(ProcessPriority priority) {
176 | return priority switch {
177 | ProcessPriority.Idle => System.Diagnostics.ProcessPriorityClass.Idle,
178 | ProcessPriority.BelowNormal => System.Diagnostics.ProcessPriorityClass.BelowNormal,
179 | ProcessPriority.Normal => System.Diagnostics.ProcessPriorityClass.Normal,
180 | ProcessPriority.AboveNormal => System.Diagnostics.ProcessPriorityClass.AboveNormal,
181 | ProcessPriority.High => System.Diagnostics.ProcessPriorityClass.High,
182 | ProcessPriority.Realtime => System.Diagnostics.ProcessPriorityClass.RealTime,
183 | _ => System.Diagnostics.ProcessPriorityClass.Normal
184 | };
185 | }
186 |
187 | private static ProcessPriority? ConvertPriorityFromTask(System.Diagnostics.ProcessPriorityClass taskPriority) {
188 | return taskPriority switch {
189 | System.Diagnostics.ProcessPriorityClass.Idle => ProcessPriority.Idle,
190 | System.Diagnostics.ProcessPriorityClass.BelowNormal => ProcessPriority.BelowNormal,
191 | System.Diagnostics.ProcessPriorityClass.Normal => ProcessPriority.Normal,
192 | System.Diagnostics.ProcessPriorityClass.AboveNormal => ProcessPriority.AboveNormal,
193 | System.Diagnostics.ProcessPriorityClass.High => ProcessPriority.High,
194 | System.Diagnostics.ProcessPriorityClass.RealTime => ProcessPriority.Realtime,
195 | _ => null
196 | };
197 | }
198 | }
199 | }
--------------------------------------------------------------------------------
/src/StartupManager.Services/Registries/RegistryService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Linq;
5 | using Microsoft.Win32;
6 | using StartupManager.Models;
7 |
8 | namespace StartupManager.Services.Registries {
9 | public class RegistryService : IRegistryService {
10 | private static string[] StartupRegistryPaths = new [] {
11 | @"Software\Microsoft\Windows\CurrentVersion\Run",
12 | @"Software\Microsoft\Windows\CurrentVersion\RunOnce",
13 | @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run",
14 | @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run",
15 | @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\RunOnce"
16 | };
17 | private static string StartupRegistryPath => @"Software\Microsoft\Windows\CurrentVersion\Run";
18 |
19 | private const string DisabledStartupRegistryItems = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run";
20 | private const string DisabledStartupFolderItems = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder";
21 | private static byte[] EnabledBytes => new byte[] { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
22 |
23 | public void AddProgramToStartup(StartupProgram program) {
24 | using(var key = GetWriteRegistryKey(StartupRegistryPath, program.AllUsers)) {
25 | key.SetValue(program.Name, $"\"{program.File.FullName}\" {program.Arguments}");
26 | }
27 | }
28 |
29 | public void RemoveProgramFromStartup(StartupList program) {
30 | using(var key = GetWriteRegistryKey(program.RegistryPath, program.AllUsers)) {
31 | key.DeleteValue(program.RegistryName);
32 | }
33 | }
34 |
35 | public StateChange ToggleStartupState(StartupList program, bool enable) {
36 | try {
37 | using(var reg = GetWriteRegistryKey(program.DisabledRegistryPath, program.AllUsers)) {
38 | byte[] bytes;
39 | if (enable) {
40 | bytes = EnabledBytes;
41 | } else {
42 | bytes = MakeDisabledBytes();
43 | }
44 |
45 | var currentValue = (byte[]?) reg.GetValue(program.RegistryName);
46 |
47 | if (currentValue == null) {
48 | reg.SetValue(program.RegistryName, bytes);
49 | return StateChange.Success;
50 | }
51 |
52 | var isAlreadyTheRequestState = new ReadOnlySpan(bytes.Take(4).ToArray()).SequenceEqual(currentValue.Take(4).ToArray());
53 |
54 | if (isAlreadyTheRequestState) {
55 | return StateChange.SameState;
56 | } else {
57 | reg.SetValue(program.RegistryName, bytes);
58 | return StateChange.Success;
59 | }
60 | }
61 | } catch (UnauthorizedAccessException) {
62 | return StateChange.Unauthorized;
63 | }
64 | }
65 |
66 | public IEnumerable GetStartupPrograms(IEnumerable programStates) {
67 | var startupPrograms = new List();
68 | var startupStates = programStates;
69 | foreach (var path in StartupRegistryPaths) {
70 | var currentUser = GetStartups(path, allUsers : false, startupStates);
71 | if (currentUser != null) {
72 | startupPrograms.AddRange(currentUser);
73 | }
74 | var allUsers = GetStartups(path, allUsers : true, startupStates);
75 | if (allUsers != null) {
76 | startupPrograms.AddRange(allUsers);
77 | }
78 | }
79 | return startupPrograms;
80 | }
81 |
82 | public IEnumerable GetStartupPrograms() {
83 | var startupStates = GetStartupProgramStates();
84 | return GetStartupPrograms(startupStates);
85 | }
86 |
87 | public StartupList? GetStartupByName(string name, IEnumerable programStates) {
88 | Func namePredicate = x => x.Equals(name, StringComparison.OrdinalIgnoreCase);
89 | Func disabledPredicate = x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && x.Disabled;
90 | return GetStartupByPredicate(namePredicate, disabledPredicate, programStates);
91 | }
92 |
93 | public StartupList? GetStartupByName(string name) {
94 | var startupStates = GetStartupProgramStates();
95 | return GetStartupByName(name, startupStates);
96 | }
97 |
98 | public StartupList? GetStartupByPredicate(Func predicate, Func disabledPredicate, IEnumerable programStates) {
99 | var program = FindStartupList(predicate, disabledPredicate, false, programStates);
100 | if (program == null) {
101 | program = FindStartupList(predicate, disabledPredicate, true, programStates);
102 | }
103 | return program;
104 | }
105 |
106 | public StartupList? GetStartupByPredicate(Func predicate, Func disabledPredicate) {
107 | var startupStates = GetStartupProgramStates();
108 | return GetStartupByPredicate(predicate, disabledPredicate, startupStates);
109 | }
110 |
111 | private StartupList? FindStartupList(Func predicate, Func disabledPredicate, bool allUsers, IEnumerable startupStates) {
112 | var startupRegistryKeys = GetReadRegistryKeys(allUsers, StartupRegistryPaths);
113 | foreach (var registry in startupRegistryKeys) {
114 | using(registry) {
115 | if (registry == null)
116 | continue;
117 |
118 | var startupValues = registry.GetValueNames();
119 | var name = startupValues.FirstOrDefault(predicate);
120 | if (name != null) {
121 | var path = registry?.GetValue(name)?.ToString();
122 | var disabled = startupStates.Any(disabledPredicate);
123 | return new StartupList(name, path, requireAdministrator : allUsers, disabled, StartupList.StartupType.Regedit, allUsers : allUsers, StartupRegistryPaths.First(x => registry?.Name.Contains(x) == true), DisabledStartupRegistryItems, name);
124 | }
125 | }
126 | }
127 | return null;
128 | }
129 |
130 | public IEnumerable GetStartupProgramStates() {
131 | var startupStates = new List();
132 | using(var currentUserDisabledReg = GetReadRegistryKey(DisabledStartupRegistryItems, allUsers : false))
133 | using(var allUserDisabledReg = GetReadRegistryKey(DisabledStartupRegistryItems, allUsers : true))
134 | using(var currentUserShellDisabledReg = GetReadRegistryKey(DisabledStartupFolderItems, allUsers : false))
135 | using(var allUserShellDisabledReg = GetReadRegistryKey(DisabledStartupFolderItems, allUsers : true)) {
136 | var currentUsers = currentUserDisabledReg?.GetValueNames().Select(x => GetStartupState(currentUserDisabledReg, x));
137 | var allUsers = allUserDisabledReg?.GetValueNames().Select(x => GetStartupState(allUserDisabledReg, x));
138 | var currentUsersShell = currentUserShellDisabledReg?.GetValueNames().Select(x => GetStartupState(currentUserShellDisabledReg, x));
139 | var allUsersShell = allUserShellDisabledReg?.GetValueNames().Select(x => GetStartupState(allUserShellDisabledReg, x));
140 | if (currentUsers != null) {
141 | startupStates.AddRange(currentUsers);
142 | }
143 | if (currentUsersShell != null) {
144 | startupStates.AddRange(currentUsersShell);
145 | }
146 | if (allUsers != null) {
147 | startupStates.AddRange(allUsers);
148 | }
149 | if (allUsersShell != null) {
150 | startupStates.AddRange(allUsersShell);
151 | }
152 | }
153 | return startupStates;
154 | }
155 |
156 | private IEnumerable GetStartups(string registryPath, bool allUsers, IEnumerable startupStates) {
157 | var programs = new List();
158 | var startupRegistryKeys = GetReadRegistryKeys(allUsers, registryPath);
159 | foreach (var registry in startupRegistryKeys) {
160 | using(registry) {
161 | if (registry == null)
162 | continue;
163 |
164 | var startupValues = registry.GetValueNames();
165 | var startupPrograms = startupValues.Select(name => {
166 | var path = registry?.GetValue(name)?.ToString();
167 | var disabled = startupStates.Any(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && x.Disabled);
168 |
169 | return new StartupList(name, path, requireAdministrator : allUsers, disabled, StartupList.StartupType.Regedit, allUsers : allUsers, StartupRegistryPaths.First(x => registry?.Name.Contains(x) == true), DisabledStartupRegistryItems, name);
170 | }).Where(x => !string.IsNullOrWhiteSpace(x.Path)).ToList();
171 | programs.AddRange(startupPrograms);
172 | }
173 | }
174 | return programs;
175 | }
176 |
177 | private static RegistryKey GetWriteRegistryKey(string registryPath, bool allUsers) {
178 | if (allUsers) {
179 | return Registry.LocalMachine.CreateSubKey(registryPath);
180 | } else {
181 | return Registry.CurrentUser.CreateSubKey(registryPath);
182 | }
183 | }
184 |
185 | private static RegistryKey? GetReadRegistryKey(string registryPath, bool allUsers) {
186 | if (allUsers) {
187 | return Registry.LocalMachine.OpenSubKey(registryPath);
188 | } else {
189 | return Registry.CurrentUser.OpenSubKey(registryPath);
190 | }
191 | }
192 |
193 | private static void DeleteRegistryKey(StartupList program) {
194 | using(var key = GetWriteRegistryKey(program.RegistryPath, program.AllUsers)) {
195 | key.DeleteValue(program.RegistryName);
196 | }
197 | }
198 |
199 | private static IEnumerable GetReadRegistryKeys(bool allUsers, params string[] registryKeys) {
200 | if (allUsers) {
201 | return registryKeys.Select(x => Registry.LocalMachine.OpenSubKey(x));
202 | } else {
203 | return registryKeys.Select(x => Registry.CurrentUser.OpenSubKey(x));
204 | }
205 | }
206 |
207 | private StartupState GetStartupState(RegistryKey disabledReg, string name) {
208 | var bytes = disabledReg.GetValue(name) as byte[];
209 | var disabled = CheckIfDisabled(bytes);
210 | return new StartupState(name, disabled);
211 | }
212 |
213 | private static bool CheckIfDisabled([AllowNull] byte[] bytes) {
214 | var disabled = false;
215 | if (bytes != null) {
216 | //If the last 8 bytes are not 0 then it's disabled
217 | disabled = bytes.Skip(4).Any(x => x != 0x00);
218 | }
219 |
220 | return disabled;
221 | }
222 |
223 | private static byte[] MakeDisabledBytes() {
224 | var startBytes = new byte[] { 0x03, 0x00, 0x00, 0x00 };
225 | var now = DateTime.Now.Ticks;
226 | var timeBytes = BitConverter.GetBytes(now);
227 | var bytes = startBytes.Concat(timeBytes).ToArray();
228 | return bytes;
229 | }
230 | }
231 | }
--------------------------------------------------------------------------------