I'm a novice, self taught C# developer who's working on my first major program. The program I'm building is a system startup manager that is supposed to replace the stock startup manager that ships with Windows.
You can find instructions on how to use it from the README.txt file, and to get an idea of how it looks, look into "SystemStartupProgram.PNG".
This program was originally made for private use but I figured I might as well let others use it (for free) when it's finished.
The basic functionality of my program is this; it reads a user input, searches for the program name from all directories on the pc and then writes the name of the program and its path to "utilities.txt", and adds both to a list called ProgramNames
. Another way to add a program is to use the explorer for which there is a seperate button. Using this method instead, it won't look for a directory since it is already given by the explorer. It then adds the program in the same way.
Upon user login, it will read the program names / paths from "utilities.txt" and adds those to the list ProgramNames
. When the user wants to launch programs through a button, it will go through the list of programs saved and launch each program separately.
To have an idea of what the program looks like visually, please refer to "SystemStartupProgram.PNG" found on Github, linked earlier.
Note: Apparently I could replace the AppendingThreads
with Task.Run(--)
. I'm planning on making a seperate Windows service application for it, instead of running the .exe itself on startup. (This program would just be a GUI for the service version).
Also, I downloaded Puma Scan and it didn't find any security flaws. Should I trust its judgement?
I would love to get feedback on two things:
- How's the quality of the code, and are there ways to improve it? If so, how?
- Can you find any weak points that may cause trouble to end users? (Security flaws, things that could slow down the end users' computer, etc)
Parts of my program are missing comments, sorry about those parts.
namespace SystemStartupProgram
{
public partial class StartupManager : Form
{
// Utilities
Thread addToListThread = null;
StreamWriter writer;
StreamReader reader;
private Thread appendingThread = null;
private Thread setTextThread = null;
private Boolean shouldChange = true;
String defaultDirectory = "";
// List contains names of all saved programs
public static List<String> ProgramNames = new List<String>();
public StartupManager()
{
InitializeComponent(); // Creates application
// Set program's directory to path, where it's utilities are located
var AppDataDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
String subFolderDirectory = Path.Combine(AppDataDirectory, "StartupProgram");
if (Directory.Exists(subFolderDirectory))
{
Directory.SetCurrentDirectory(subFolderDirectory);
}
else
{
Directory.CreateDirectory(subFolderDirectory);
Directory.SetCurrentDirectory(subFolderDirectory);
}
SetDefaultDirectory(subFolderDirectory);
if (File.Exists("utilities.txt"))
{
CheckForEmptyFileAndShowPrograms();
SetStartupBox();
SetStartUp();
StartAppsOnStartup();
if (File.Exists("folder.ico"))
{
this.Icon = Icon.ExtractAssociatedIcon(subFolderDirectory + "\\folder.ico");
}
}
else
{
this.appendingThread = new Thread(() => AppendText("Text file could not be found! Creating new file.\n"));
appendingThread.Start();
using (writer = new StreamWriter("utilities.txt"))
{
if (LaunchCheckBox.Enabled == true)
{
writer.WriteLine("false;startup;");
}
}
}
}
/// <summary>
/// All functions are located here
/// </summary>
#region All functions
/// <summary>
/// Reads file and saves all currently saved programs and their indexes
/// to a String which is returns
/// </summary>
private static String ReadProgramInfoFromFile()
{
String line;
int count = 0;
StringBuilder savedPrograms = new StringBuilder();
Boolean isFirstIT = true;
try
{
StreamReader reader = new StreamReader("utilities.txt");
while ((line = reader.ReadLine()) != null)
{
if (line.EndsWith(".exe") && !line.ToLower().StartsWith("c:\\"))
{
if (isFirstIT) // On first cycle, add this text
{
savedPrograms.Append("Currently saved programs: \n\n");
isFirstIT = false;
}
savedPrograms.Append(count + ": " + line + "\n");
ProgramNames.Add(line);
count++;
}
else if (line.ToLower().StartsWith("c:\\"))
{
savedPrograms.Append("Path: " + line + "\n\n");
ProgramNames.Add(line);
}
}
savedPrograms.Append("---------------------------------------------------" +
"-----------------------------------------------------------------\n\n");
reader.Close();
}
catch { }
return savedPrograms.ToString();
}
// Disables all controls
private void DisableAllControls()
{
LaunchButton.Enabled = false;
DeleteAllButton.Enabled = false;
ClearButton.Enabled = false;
ShowSavedButton.Enabled = false;
SetButton.Enabled = false;
DeleteButton.Enabled = false;
ExplorerButton.Enabled = false;
InputTextBar.Enabled = false;
DeleteTextBox.Enabled = false;
LaunchCheckBox.Enabled = false;
}
// Enables all controls
private void EnableAllControls()
{
SetControlPropertyThreadSafe(LaunchButton, "Enabled", true);
SetControlPropertyThreadSafe(DeleteAllButton, "Enabled", true);
SetControlPropertyThreadSafe(ClearButton, "Enabled", true);
SetControlPropertyThreadSafe(ShowSavedButton, "Enabled", true);
SetControlPropertyThreadSafe(SetButton, "Enabled", true);
SetControlPropertyThreadSafe(DeleteButton, "Enabled", true);
SetControlPropertyThreadSafe(ExplorerButton, "Enabled", true);
SetControlPropertyThreadSafe(InputTextBar, "Enabled", true);
SetControlPropertyThreadSafe(DeleteTextBox, "Enabled", true);
SetControlPropertyThreadSafe(LaunchCheckBox, "Enabled", true);
}
// Same as AddToList(String, String) but instead locates file directory
// of the specified program first.
public void AddToList(String name)
{
if (String.IsNullOrWhiteSpace(name))
{
this.appendingThread = new Thread(() => AppendText("Please specify a file name first!"));
appendingThread.Start();
return;
}
if (!ContainsName(name))
{
writer = File.AppendText("utilities.txt");
if (name.EndsWith(".exe") && !name.Contains("*"))
{
this.appendingThread = new Thread(() => AppendText("Locating file directory...\n"));
appendingThread.Start();
List<String> paths = GetDirectory(@"C:\\", name);
if (paths.Count() > 0)
{
String _path = paths[0];
if (_path.ToLower().Equals((defaultDirectory + "\\systemstartupprogram.exe").ToLower()))
{
this.appendingThread = new Thread(() => AppendText("Can't add this program!"));
appendingThread.Start();
return;
}
name = _path.Substring(_path.LastIndexOf("\\") +1);
if (_path.Contains("\\\\"))
{
_path = _path.Remove(_path.IndexOf("\\\\"), 1);
}
ProgramNames.Add(name);
ProgramNames.Add(_path);
writer.WriteLine(name);
writer.WriteLine(_path);
writer.Close();
this.appendingThread = new Thread(() => AppendText("Program added: " + name));
appendingThread.Start();
Thread.Sleep(100);
this.appendingThread = new Thread(() => AppendText("Path : "
+ _path + "\n"));
appendingThread.Start();
DeleteProgramFromRegistry(name, _path);
if (ProgramNames.Count > 10)
{
appendingThread = new Thread(() => AppendText("Warning: more than 5 programs set to start! (May cause system to slow down on startup)"));
appendingThread.Start();
}
}
else
{
this.appendingThread = new Thread(() => AppendText("Program not found!\n"));
appendingThread.Start();
}
}
else
{
if (name.Contains("*"))
{
this.appendingThread = new Thread(() => AppendText("Name cannot contain asterisks!\n"));
appendingThread.Start();
}
else if (!name.EndsWith(".exe"))
{
this.appendingThread = new Thread(() => AppendText("File must end in '.exe' !\n"));
appendingThread.Start();
}
}
writer.Close();
}
else
{
this.appendingThread = new Thread(() => AppendText("This file is already saved!\n"));
appendingThread.Start();
}
EnableAllControls();
}
// Adds program to list 'ProgramNames' and writes it to file.
public void AddToList(String name, String path)
{
// Make sure name isn't empty or nothing but spaces
if (String.IsNullOrWhiteSpace(name))
{
this.appendingThread = new Thread(() => AppendText("Please specify a file name first!"));
appendingThread.Start();
return;
}
// Check if program is already saved
if (!ContainsName(name))
{
writer = File.AppendText("utilities.txt");
if (name.EndsWith(".exe") && path.ToLower().StartsWith("c:\\"))
{
name = path.Substring(path.LastIndexOf("\\") + 1);
ProgramNames.Add(name);
ProgramNames.Add(path);
writer.WriteLine(name);
writer.WriteLine(path);
writer.Close();
this.appendingThread = new Thread(() => AppendText("Program added: " + name));
appendingThread.Start();
this.appendingThread = new Thread(() => AppendText("Path for '" + name + "': " + path + "\n"));
appendingThread.Start();
DeleteProgramFromRegistry(name, path);
if (ProgramNames.Count > 5)
{
appendingThread = new Thread(() => AppendText("Warning: more than 5 programs set to start! (May cause system to slow down on startup)"));
appendingThread.Start();
}
}
else if (!name.EndsWith(".exe"))
{
this.appendingThread = new Thread(() => AppendText("File must end in '.exe' !\n"));
appendingThread.Start();
}
else if (!path.ToLower().StartsWith("c:\\"))
{
this.appendingThread = new Thread(() => AppendText("Path invalid!\n"));
appendingThread.Start();
}
}
else
{
this.appendingThread = new Thread(() => AppendText("This program is already saved!\n"));
appendingThread.Start();
}
}
// Checks if given string is already written in file
private Boolean ContainsName(String name)
{
using (StreamReader reader = new StreamReader("utilities.txt"))
{
String contents = reader.ReadToEnd();
if (contents.Contains(name))
{
return true;
}
}
return false;
}
/// <summary>
/// Launches all saved applications in order by getting name & path from ProgramNames.
/// </summary>
private async void LaunchSavedPrograms()
{
String path = "";
String name = "";
// Make sure list isn't empty
if (ProgramNames.Count > 0)
{
// Go through every application in the list
for (int i = 0; i < ProgramNames.Count(); i += 2)
{
String tempname = ProgramNames[i].ToLower();
tempname = Regex.Replace(tempname, @"\s+", "");
String temppath = ProgramNames[i + 1].ToLower();
temppath = Regex.Replace(temppath, @"\s+", "");
// Make sure file is a .exe
if (tempname.EndsWith(".exe")) {
name = ProgramNames[i];
}
// Make sure path is a path and ends in program name (= path is program's path)
if (temppath.StartsWith("c:\\") && temppath.EndsWith(tempname)){
path = ProgramNames[i + 1];
}
if (tempname.Equals("microsoftedge.exe"))
{
Process.Start("microsoft-edge:");
}
try
{
// Start program
var psi = new ProcessStartInfo(name);
Directory.SetCurrentDirectory(path.Substring(0, path.LastIndexOf("\\")));
Process.Start(psi);
this.appendingThread = new Thread(() => AppendText("Program started: " + name + "\n" +
"Program path: " + path + "\n"));
appendingThread.Start();
await Task.Delay(300);
}
catch (Win32Exception ex)
{
this.appendingThread = new Thread(() => AppendText("Error starting program: " + ex.Message + "\n" +
"Failed program: " + tempname + ".\nProgram path: " + temppath + "\n"));
appendingThread.Start();
await Task.Delay(300);
}
}
// After starting all apps, set current directory back to default
Directory.SetCurrentDirectory(defaultDirectory);
this.appendingThread = new Thread(() => AppendText("Loading complete.\n"));
appendingThread.Start();
}
}
// Gets the path of specified application (f.ex 'notepad.exe' output = 'c:/windows/notepad.exe')
private List<String> GetDirectory(string path, string pattern)
{
var files = new List<String>();
try
{
files.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly));
foreach (var directory in Directory.GetDirectories(path))
{
files.AddRange(GetDirectory(directory, pattern));
}
}
catch (UnauthorizedAccessException) { }
return files;
}
// Checks to see if file is empty and if not, sets TextArea text to program names / directories
private void CheckForEmptyFileAndShowPrograms()
{
if (File.Exists("utilities.txt"))
{
String _true = "true;startup;";
String _false = "false;startup;";
// Reads file and determines if file is empty or not, sets text accordingly
using (StreamReader reader = new StreamReader("utilities.txt"))
{
String contents = reader.ReadToEnd();
if (contents.Contains(_true))
{
contents = contents.Remove(contents.IndexOf(_true), _true.Length);
}
else if (contents.Contains(_false))
{
contents = contents.Remove(contents.IndexOf(_false), _false.Length);
}
if (String.IsNullOrWhiteSpace(contents))
{
setTextThread = new Thread(() => SetText("No programs saved!\n"));
setTextThread.Start();
}
else
{
setTextThread = new Thread(() => SetText(ReadProgramInfoFromFile()));
setTextThread.Start();
}
}
}
else
{
appendingThread = new Thread(() => AppendText("Not found"));
appendingThread.Start();
}
}
// Sets the value for default directory
private void SetDefaultDirectory(String directory)
{
defaultDirectory = directory;
}
// Writes a regedit or deletes it according to state of check box
private void SetStartUp()
{
RegistryKey rk = Registry.CurrentUser.OpenSubKey
("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
if (LaunchCheckBox.Checked)
{
rk.SetValue("SystemStartupProgram", (defaultDirectory + "\\SystemStartupProgram.exe"));
}
else
{
rk.DeleteValue("SystemStartupProgram", false);
}
}
// Reads file and determines if startup box should be enabled or not
private void SetStartupBox()
{
Boolean shouldCheck = false;
using (reader = new StreamReader("utilities.txt"))
{
String line = reader.ReadLine();
if (line != null)
{
if (line.EndsWith(";startup;"))
{
if (line.StartsWith("true"))
{
shouldCheck = true;
}
else if (line.StartsWith("false"))
{
shouldCheck = false;
}
}
}
}
if (shouldCheck)
{
shouldChange = false;
LaunchCheckBox.Checked = true;
}
else
{
shouldChange = false;
LaunchCheckBox.Checked = false;
}
}
/// <summary>
/// Functions to properly append text to TextArea
/// </summary>
#region Safe appending/setting functions
private void AppendText(string text)
{
if (this.TextArea.InvokeRequired)
{
argReturner ar = new argReturner(AppendText);
this.Invoke(ar, new object[] { text });
}
else
{
this.TextArea.AppendText(text + "\n");
}
}
private void SetText(string text)
{
if (this.TextArea.InvokeRequired)
{
argReturner ar = new argReturner(SetText);
this.Invoke(ar, new Object[] { text });
}
else
{
this.TextArea.Text = text;
}
}
private void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.SetProperty,
null, control, new object[] { propertyValue });
}
}
private void ThreadProcSafe(String text)
{
this.AppendText(text);
}
private void ThreadProcSage(string text)
{
this.SetText(text);
}
delegate void argReturner(string text);
delegate void SetControlPropertyThreadSafeDelegate(
Control control, string propertyName, object propertyValue);
#endregion Safe appending/setting functions
/// <summary>
/// Region ends
/// </summary>
// Launch saved applications upon startup
private async void StartAppsOnStartup()
{
await Task.Delay(100);
LaunchButton.PerformClick();
}
/// <summary>
/// Handles program deletion. Checks if line contains desired string, in which case
/// don't write it to new file. In other cases, write the current line to new file.
/// After writing, delete old file and rename new file to old file's name.
/// </summary>
private void DeletePrograms()
{
String line = "";
String tempLine = "";
String program = DeleteTextBox.Text;
program = program.ToLower();
program = Regex.Replace(program, @"\s+", "");
DeleteTextBox.Text = "";
try
{
StreamReader reader = new StreamReader("utilities.txt");
writer = new StreamWriter("temp.txt");
while ((line = reader.ReadLine()) != null)
{
tempLine = line.ToLower();
tempLine = Regex.Replace(tempLine, @"\s+", "");
if (!tempLine.Equals(program))
{
writer.WriteLine(line);
}
else if (tempLine.Equals(program))
{
// If the next line is the directory for program, do nothing
if ((line = reader.ReadLine()).ToLower().StartsWith("c:\\"))
{
}
}
}
writer.Close();
reader.Close();
// Renaming & deleting
File.Delete("utilities.txt");
System.IO.File.Move("temp.txt", "utilities.txt");
}
catch (Exception ex)
{
this.appendingThread = new Thread(() => AppendText("Error: " + ex.Message));
appendingThread.Start();
}
// Resets programNames list
ProgramNames = new List<String>();
// Refresh saved applications
ShowSavedButton.PerformClick();
}
// Opens file explorer, once file is selected, add it to program list
private void SelectFileWBrowser()
{
OpenFileDialog browser = new OpenFileDialog
{
Title = "Select files to use",
InitialDirectory = @"c:\\",
Filter = "All files (*.*)|*.*|All files (*.*)|*.*",
FilterIndex = 2,
RestoreDirectory = true
};
if (browser.ShowDialog() == DialogResult.OK)
{
String path = browser.FileName;
String name = path.Substring(path.LastIndexOf("\\") +1, path.Length - path.LastIndexOf("\\") -1);
var thread = new Thread(() => AddToList(name, path));
thread.Start();
}
}
private void DeleteProgramFromRegistry(String program, String programPath)
{
if (program.EndsWith(".exe"))
{
program = program.Substring(0, program.IndexOf(".exe"));
}
programPath = programPath.ToLower();
programPath = Regex.Replace(programPath, @"\s+", "");
if (programPath.Contains("\\\\"))
{
programPath = programPath.Remove(programPath.IndexOf("\\"), 1);
}
RegistryKey rk = Registry.CurrentUser.OpenSubKey
("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
foreach (var k in rk.GetValueNames())
{
String directory = rk.GetValue(k).ToString().ToLower();
directory = Regex.Replace(directory, @"\s+", "");
if (!directory.Contains("\"")){
directory = directory.Substring(0, directory.LastIndexOf(".exe") + 4);
}
else
{
directory = directory.Substring(0, directory.LastIndexOf(".exe") + 5);
}
if (directory.StartsWith("\"") && directory.EndsWith("\""))
{
directory = directory.Substring(directory.IndexOf("\"") +1, directory.LastIndexOf("\"") -1);
}
if (directory.Equals(programPath))
{
if (directory.Equals((defaultDirectory + "\\systemstartupprogram.exe").ToLower()))
{
LaunchCheckBox.Checked = !LaunchCheckBox.Checked;
return;
}
rk.DeleteValue(k, false);
return;
}
}
}
/// <summary>
/// Region ends
/// </summary>
#endregion All functions
/// <summary>
/// All GUI related utilities are located here
/// </summary>
#region GUI Utilities
/// <summary>
/// All utilities with a delete function are located here
/// </summary>
#region Delete utilities
// Asks user for confirmation, deletes all entires in file and 'programNames'
private void DeleteAllButton_Click(object sender, EventArgs e)
{
if (ProgramNames.Count() > 0)
{
DialogResult dResult = MessageBox.Show("Are you sure?", "Delete all programs", MessageBoxButtons.YesNo);
if (dResult == DialogResult.Yes)
{
using (writer = new StreamWriter("temp.txt"))
{
if (LaunchCheckBox.Checked)
{
writer.WriteLine("true;startup;");
}
else
{
writer.WriteLine("false;startup;");
}
}
// Renaming & deleting
File.Delete("utilities.txt");
File.Move("temp.txt", "utilities.txt");
ShowSavedButton.PerformClick();
ProgramNames = new List<String>();
}
}
else
{
this.appendingThread = new Thread(() => AppendText("Error deleting: No programs saved!\n"));
appendingThread.Start();
}
}
// Delete specified program from list
private void DeleteTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)ConsoleKey.Enter)
{
DeletePrograms();
}
}
// Delete specified program from list
private void DeleteButton_Click(object sender, EventArgs e)
{
DeletePrograms();
}
/// <summary>
/// Region ends
/// </summary>
#endregion Delete utilities
/// <summary>
/// All utilities related to adding programs to list are here
/// </summary>
#region Add utilities
// Adds specified name to 'programNames'
public void SetButton_Click(object sender, EventArgs e)
{
String name = (InputTextBar.Text).ToLower();
InputTextBar.Text = "";
DisableAllControls();
var thread = new Thread(() => AddToList(name));
thread.Start();
}
// Adds specified name to 'programNames'
public void InputTextBar_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)ConsoleKey.Enter)
{
String name = (InputTextBar.Text).ToLower();
InputTextBar.Text = "";
DisableAllControls();
var thread = new Thread(() => AddToList(name));
thread.Start();
}
}
/// <summary>
/// Region ends
/// </summary>
#endregion Add utilities
/// <summary>
/// All other utilities are located here (Miscellaneous)
/// </summary>
#region Miscellaneous utilities
private void ExplorerButton_Click(object sender, EventArgs e)
{
SelectFileWBrowser();
}
// Launches all applications in 'programNames'
private void LaunchButton_Click(object sender, EventArgs e)
{
ThreadStart refer = new ThreadStart(LaunchSavedPrograms);
this.appendingThread = new Thread(() => AppendText("Loading applications...\n"));
appendingThread.Start();
Thread launchThread = new Thread(refer);
launchThread.Start();
}
private void CancelButton_Click(object sender, EventArgs e)
{
addToListThread.Abort();
writer.Close();
EnableAllControls();
this.appendingThread = new Thread(() => AppendText("Process cancelled."));
appendingThread.Start();
}
// Shows all currently saved programs (in text file)
private void ShowSavedButton_Click(object sender, EventArgs e)
{
if (File.Exists("utilities.txt"))
{
CheckForEmptyFileAndShowPrograms();
}
}
// Clears text area from all text
private void ClearButton_Click(object sender, EventArgs e)
{
TextArea.Text = "";
}
private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("A program by Zecuel\nVersion: 1.0", "About StartupManager");
}
private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void LaunchCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (shouldChange)
{
String line;
String toWrite = "";
if (LaunchCheckBox.Checked == false)
{
toWrite = "false;startup;";
this.appendingThread = new Thread(() => AppendText("Program will no longer start on system startup."));
appendingThread.Start();
}
else if (LaunchCheckBox.Checked == true)
{
toWrite = "true;startup;";
this.appendingThread = new Thread(() => AppendText("Program is set to automatically start on system startup."));
appendingThread.Start();
}
using (reader = new StreamReader("utilities.txt"))
{
using (writer = new StreamWriter("temp.txt"))
{
writer.WriteLine(toWrite);
while ((line = reader.ReadLine()) != null)
{
if (!line.EndsWith(";startup;"))
{
writer.WriteLine(line);
}
}
}
}
File.Delete("utilities.txt");
File.Move("temp.txt", "utilities.txt");
SetStartUp();
}
shouldChange = true;
}
private void HelpToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show
("Common applications and their .exe's :\n\n" +
"Microsoft Edge: microsoftedge.exe\n" +
"Gyazo: gystation.exe\n" +
"Hearthstone: hearthstone beta launcher.exe",
"Help with Startup Manager");
}
#endregion Miscellaneous utilities
/// <summary>
/// Region ends
/// </summary>
#endregion GUI utilities
/// <summary>
/// Region ends
/// </summary>
}
}