Менеджер автозагрузки для Windows


Я новичок, самоучка разработчик C#, который работает на Моя первая серьезная программа. Программа Я создаю менеджер автозагрузки системы, которая должна заменить менеджер автозагрузки, который поставляется с Windows.

На GitHub

Вы можете найти инструкции о том, как использовать его из файла readme.txt и чтобы получить представление о том, как это выглядит, смотрите в "SystemStartupProgram.ПНГ".

Эта программа изначально была сделана для частного использования, но я полагал, что я мог бы также позволить другим использовать его (бесплатно), когда она будет закончена.

Основные функции моей программы это то, что она читает ввод пользователя, ищет программу из всех каталогов на ПК, а затем записывает название программы и ее путь к "utilities.txt" и добавляет в список, который называется ProgramNames. Еще один способ добавить программу использовать Explorer, для которого есть кнопка отдельная. Вместо этого, используя этот метод, он не будет искать каталог, поскольку он уже дал проводник. Затем он добавляет программу в ту же сторону.

При входе пользователя в систему, он будет читать программу имена / пути "utilities.txt" и добавит в список ProgramNames. Когда пользователь хочет запустить программу через кнопку, он будет идти по списку сохраненных программ и запуска каждой программы отдельно.

Чтобы иметь представление о том, что программа выглядит как визуально, пожалуйста, обратитесь к "SystemStartupProgram.ПНГ" найти на GitHub, связанной ранее.

Примечание: очевидно, я мог бы заменить AppendingThreads С Task.Run(--). Я планирую сделать отдельные приложения службы Windows для него, вместо запуска .сам exe на запуске. (Эта программа будет просто GUI для версии услуги).

Кроме того, я загрузил скан Пума и он не нашел никаких изъянов безопасности. Я должен доверять свое решение?

Я хотел бы получить обратную связь на двух вещах:

  1. Как качество кода, и есть ли способы это исправить? Если да, то как?
  2. Можете ли вы найти какие-либо слабые места, которые могут вызвать проблемы для конечных пользователей? (Недостатки безопасности, что может замедлить компьютер конечных пользователей и т. д.)

Части моей программы отсутствуют комментарии, сожалею о тех частях.

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>
}
}


156
0
задан 15 апреля 2018 в 06:04 Источник Поделиться
Комментарии
1 ответ

Это довольно много кода в комментарий, Я не могу идти в глубину на все здесь. Я рассмотрю его, как я прокрутите.


Длинные и понятные имена методов хорошие.


Я вижу достойную попытку разделения вещей в методы, но ты еще попал в ловушку смешивая функции в одном методе. Некоторые методы будут слишком длинными или слишком вложенными.

Вы уже создали монстра класса. Файл весит 1060 линий (как на Гитхабе), что не приемлемо. Это должен быть разделены на отдельные файлы, и, что более важно, отдельные классы!

Простое предложение для разных классов:


  • StreamHandler - обрабатывает все потоки и запись/чтение с диска.

  • UtilityFileProcessor - обрабатывает все относящиеся к "utilities.txt". Это будет, вероятно, использовать StreamHandler внутренне.

  • ReportFormatter - определяет формат текста, который записывается на диск. Принимает в переменные данные и форматирует их в то, что вы хотите им быть. Будет использовать StreamHandler внутренне для записи на диск.

Это всего в двух минутах предположение, основанное на первый взгляд на ваш код. Код является слишком большой для меня, чтобы сделать здесь точный анализ.


private Boolean shouldChange = true;

Тут есть два варианта.


  • Этот логический используется локально где-то, где имя делает мало смысла в контексте. В этом случае, объявить его локально.

  • Этот логический используется в нескольких методах и не могут быть помещены на местах, но тогда вы должны переименовать его так, я вижу, что он используется на основе его имени. Надо что менять?


    if (Directory.Exists(subFolderDirectory))
{
Directory.SetCurrentDirectory(subFolderDirectory);
}
else
{
Directory.CreateDirectory(subFolderDirectory);
Directory.SetCurrentDirectory(subFolderDirectory);
}

Вы можете извлечь повторяющиеся строки и поменять если сохранить несколько строк (и повторения):

if(!Directory.Exists(subFolderDirectory))
Directory.CreateDirectory(subFolderDirectory);

Directory.SetCurrentDirectory(subFolderDirectory);


this.appendingThread = new Thread(() => AppendText("Text file could not be found! Creating new file.\n"));
appendingThread.Start();

Я не совсем уверен, почему это заворачивается в потоке. Вы пытались использовать async/задачи? Темы и задачи, в то время как аналогичное, на первый взгляд, это разные звери.


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);
}

Мне не нравится это. Почему .EXE файлы, которых нет на диске C лечится по-другому? Почему файлы, которые не .бывших и не на диске C проигнорировали?

Похож на предыдущий комментарий, ProgramNames.Add(line); можно поставить после этого if поскольку он используется в обоих случаях.

Что ты здесь делаешь обрабатывает список и немедленно хранения форматированная строка, используемая для вывода. Я предлагаю вам отделить бизнес-логику, формируют логику изложения.
Хранить файлы (или их пути к каталогам) в savedPrograms. И только форматировать данные при печати значение (в файл или на консоль).

Без этого разделения, ты собираешься бороться, если вы когда-нибудь решите изменить форматирование или выход свои результаты с нескольких источников (например, файл и консоль), но, используя различные форматы для каждого.


// 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);
}

Почему один способ использовать SetControlPropertyThreadSafe и другим не дает? Это не имеет смысла для меня.

Кроме того, вы можете совместить это в одном методе:

private void EnableAllControls(bool isEnabled)
{
SetControlPropertyThreadSafe(LaunchButton, "Enabled", isEnabled);
//and so on...
}

Это может не быть плохой идеей, чтобы положить все эти элементы в список, и затем делаем:

foreach(myControl in myControlList)
SetControlPropertyThreadSafe(myControl , "Enabled", isEnabled);

Экономит копирования/вставки.


public void AddToList(String name)
{
if (String.IsNullOrWhiteSpace(name))
{
this.appendingThread = new Thread(() => AppendText("Please specify a file name first!"));
appendingThread.Start();
return;
}

Вы смешиваете опять-таки бизнес-логику и логику представления. Ответственность AddToList метод должен быть простой: он либо добавляет элемент, или он говорит вам, почему он не может добавить элемент (чаще всего через исключение со строкой сообщения).

Где это сообщение будет размещена (в файл, на консоль, ...) не должны решаться AddToList метод.


this.appendingThread = new Thread(() => AppendText("Program added: " + name));
appendingThread.Start();

Thread.Sleep(100);

this.appendingThread = new Thread(() => AppendText("Path : " + _path + "\n"));
appendingThread.Start();

Опять же, почему эти все отдельные нити? Почему ты спишь между двух потоков?

Какой смысл делать что-то многопоточным, если ты потом собираешься заставить основной поток ждать, пока другой поток не будет завершен?
Если ты этого хочешь, почему не просто в главном потоке делать ВСЮ работу с самого начала? Таким образом, вы знаете точно, что это будет только сделать следующий шаг, как только он сделал первый шаг.

Пытаясь заполнить пробелы здесь, я подозреваю, что вы пытались внедрить методику задач/асинхронный, но вы решили использовать нити для какой-то необъяснимой причине.


ProgramNames.Add(name);
ProgramNames.Add(_path);

Поскольку вы добавляете имя файла и его путь как две отдельные позиции, это доказывает, что "programnames" - это не правильное название для этой переменной.

Почему вы хранимой двух различных типов данных в одном списке? Я не говорю, что вы неправы, но я ожидаю, что список ProgramNames чтобы содержать только названия программ, а не их пути к каталогам.

Вы либо будете иметь, чтобы переименовать список, что он на самом деле представляет, или изменить код, чтобы сделать это по-другому.


// 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)
{

Незначительные нарекания: в if не надо тут. В for просто цикл 0 раз, если нет элементов в списке.
Если вы, например, возвращается настраиваемое сообщение, если список был пуст, то это было бы соответствующим, чтобы сначала проверить, если там были элементы в списке. Но если это произойдет, этот метод не делает ничего; так что здесь не тот случай.


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);
}

После все кода-нить, вдруг await Task.Delay(300); появляется. Я ожидаю, что вы либо палку с одним, а не другим. В данном случае, я предлагаю перенести все в async/задачного подхода.


AppendText("Error starting program: " + ex.Message + "\n" + "Failed program: " + tempname + ".\nProgram path: " + temppath + "\n")

До сих пор я не упоминал о твоем конкатенации строк, потому что Вашингтон ограничивается двумя или тремя элементами, объединяются. Но теперь вы растете в размерах.

Строки являются неизменными, что означает, что они не могут изменить после того как они были созданы. Любое изменение (например, в длину) означает, что новая строка должна быть создана.

Если у вас "a"+"b"+"c"то, что происходит:


  • Создать 1 строку символов ("а")

  • Создать 1 строку символов ("б")

  • Создать 2 strign характер ("АВ")

  • Создать 1 строку символов ("с")

  • Создать 3 строки символов ("Азбука")

Если у вас "Error starting program:" + ex.Message + "\n" + "Failed program: " + tempname + ".\nProgram path: " + temppath + "\n"то, что происходит:


  • Создать 24 строку символов ("ошибка при запуске программы: ")

  • Предполагая, что ex.Message находится в 50 символов, создать 74 буквенная ("ошибка при запуске программы: это длинной 50 символов сообщение")

  • Создать 1 строку символов ("\Н")

  • Создать 75 символьная строка ("ошибка при запуске программы: это длинной 50 символов\N сообщение")

  • Создать 16 символьную строку ("сбой программы ")

  • Создайте 91 символьную строку ("ошибка при запуске программы: это длинная 50 сообщение символ\nFailed программе: ")

  • Предполагая, что tempname это 15 символов, создать 106 символов ("ошибка при запуске программы: это длинной 50 символов сообщение\nFailed программы: MYTEMPNAME")

  • Создать 17 строку символов (".\nProgram путь: ")

  • ...

Я надеюсь, что вы можете увидеть, как быстро это выходит за борт. Есть намного лучшие альтернативы:

Построитель строк:

StringBuilder sb = new StringBuilder();
sb.Append("Error starting program:");
sb.Append(ex.Message);
sb.Append("\nFailed program: ");
sb.Append(tempname);
sb.Append(".\nProgram path: ");
sb.Append(temppath);
sb.Append("\n");

var myString = sb.ToString();

Формат строки:

var myString = String.Format("Error starting program:{0}\nFailed program: {1}.\nProgram path: {2}\n", ex.Message, tempname, temppath);

String.Format недавно пришел в упадок из-за более короткого (и подобные) строку интерполяции функции:

var myString = $"Error starting program:{ex.Message}\nFailed program: {tempname}.\nProgram path: {temppath}\n";

Все три метода сделать строковую переменную один раз. Никакой нервотрепки, никаких взрывов памяти, производительность не опускается. Используйте их и любить их.


Я вижу постоянно "utilities.txt" используемые в Кодексе. У вас есть несколько глобальных переменных, но это именем не является одним из них?

Просто подумайте, сколько сил потребуется, если пользователь хочет назвать своих файлов что-то другое. Ты потратишь совсем немного времени отследить все вхождения имени файла.


Многие Ваши методы всем потратить немного времени и усилий на форматировании строки и присоединения их к писателю.

Как кто-то, кто просто прочитал ваш код, я до сих пор не знаю, что ваш конечный файл будет выглядеть, потому что всю логику вывода распространяется по всей базе кода.

Потому что вы используете объект StreamWriter (который является хорошей вещью!), это легко попасть в ловушку, имея каждый метод доступа объектом StreamWriter, когда им заблагорассудится. Но это станет поддержание АД, если ваши готовые приложения испортить выходной и вы хотите отладить, почему это происходит. Подготовка к шагу через весь код.

5
ответ дан 23 апреля 2018 в 01:04 Источник Поделиться