diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 2547dca..0000000 --- a/.editorconfig +++ /dev/null @@ -1,4 +0,0 @@ -[*.cs] - -# CA1822: Member als statisch markieren -dotnet_diagnostic.CA1822.severity = none diff --git a/ExeToBat.sln b/ExeToBat.sln index 34cdaa3..24ea6f9 100644 --- a/ExeToBat.sln +++ b/ExeToBat.sln @@ -1,14 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32929.385 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2036 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExeToBat", "ExeToBat\ExeToBat.csproj", "{97ED5369-CCC1-4EAD-B316-97FB983E0A62}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1E4294A3-12F4-4F45-93B3-05D31958042B}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExeToBat", "ExeToBat\ExeToBat.csproj", "{04F45237-23C8-4EE6-B61C-6C47B9979A4B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -16,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Release|Any CPU.Build.0 = Release|Any CPU + {04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9F9811F2-7CC0-4D74-8D4A-51D126D13ECD} + SolutionGuid = {4AA99DAC-6B37-42AF-8FB7-8394329BD79A} EndGlobalSection EndGlobal diff --git a/ExeToBat/App.config b/ExeToBat/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/ExeToBat/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ExeToBat/BatGen.cs b/ExeToBat/BatGen.cs new file mode 100644 index 0000000..71d65da --- /dev/null +++ b/ExeToBat/BatGen.cs @@ -0,0 +1,720 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; + +namespace ExeToBat +{ + static class Generator + { + public const int chunk = 8000; + + public static List Sources = new List(); + + static void Main() => MainMenu(); + + static void MainMenu() + { + Dictionary options = new Dictionary { + { "Files", ChooseSource }, + { "Generate", () => BuildBat(Sources, "output.bat") } + }; + + new ListMenu(options.Keys.ToList()) + { + DisplayTitle = (List Options) => Console.WriteLine("ExeToBat > Main"), + DisplayEntry = (List Options, int index, int i) => + { + Console.WriteLine("[{0}] {1}", i, Options[index]); + }, + HandleEntry = (List Options, int index) => + { + if (options.ContainsKey(Options[index])) + { + options[Options[index]](); + } + else + { + ResetInput(); + } + + return false; + }, + ExitEntry = "Exit", + + }.Show(); + } + + static void ChooseSource() + { + new ListMenu(Sources) + { + DisplayTitle = (List sources) => + { + Console.WriteLine("ExeToBat > Main > Files"); + Console.WriteLine("[{0}] ({1})", Convert.ToString(0).PadLeft(Convert.ToString(sources.Count).Length, ' '), "Add Files"); + }, + DisplayEntry = (List sources, int index, int i) => + Console.WriteLine("[{0}] {1}", + Convert.ToString(i).PadLeft( + Convert.ToString(sources.Count).Length, ' '), + Path.GetFileName(sources[index].File)), + ZeroEntry = (List sources) => + { + AddSource(); + return false; + }, + HandleEntry = (List sources, int index) => + { + ManageSource(sources[index]); + return false; + }, + RefreshEntries = (List sources) => Sources, + + }.Show(); + } + + static void AddSource() + { + bool IsInputValid = false; + while (!IsInputValid) + { + + Console.Clear(); + Console.WriteLine("ExeToBat > Main > Files > Add"); + Console.Write("\n"); + Console.Write("{0}> ", "File/Folder"); + string input = Console.ReadLine(); + + input.Trim(); + input = input.Replace("\"", ""); + if (!string.IsNullOrEmpty(input)) + { + switch (input) + { + case var i when Directory.Exists(i): + IsInputValid = true; + foreach (string file in Directory.GetFiles(input)) + { + Sources.Add(new SourceFile(file)); + } + break; + + case var i when File.Exists(i): + IsInputValid = true; + Sources.Add(new SourceFile(input)); + break; + + default: + ResetInput(); + break; + } + } + else + { + IsInputValid = true; + } + } + } + + static void ManageSource(SourceFile source) + { + Dictionary> options = new Dictionary> + { + {"Edit", () => { + ModifySource(source); + return false; + } + }, + { "Position", () => { + EditPosition(source); + return false; + } + }, + { "Delete", () => { + Sources.Remove(source); + return true; + } + }, + }; + + new ListMenu(options.Keys.ToList()) + { + DisplayTitle = (List Options) => Console.WriteLine("ExeToBat > Main > Files > {0}", Path.GetFileName(source.File)), + DisplayEntry = (List Options, int index, int i) => Console.WriteLine("[{0}] {1}", i, Options[index]), + HandleEntry = (List Options, int index) => + { + if (options.ContainsKey(Options[index])) + { + return options[Options[index]](); + } + else + { + ResetInput(); + } + + return false; + }, + + }.Show(); + } + + static void ModifySource(SourceFile source) + { + + // this could be solved better with an enum + + Dictionary options() + { + Dictionary opts = new Dictionary { + {"File", () => { } }, + {"Extraction directory", () => EditExtraction(source)}, + {"Execute after extraction", () => source.Execute = !source.Execute }, + }; + if (source.Execute) + { + opts.Add("Parameters", () => EditParameters(source)); + opts.Add("Wait for exit", () => source.Wait = !source.Wait); + } + if (source.Execute && source.Wait) { opts.Add("Delete after execution", () => source.Delete = !source.Delete); } + return opts; + } + + Dictionary ValueMap = new Dictionary + { + { "File", "File" }, + { "Extraction directory", "Directory" }, + { "Execute after extraction", "Execute" }, + { "Parameters", "Parameters" }, + { "Wait for exit", "Wait"}, + { "Delete after execution", "Delete"}, + }; + + new ListMenu(options().Keys.ToList()) + { + DisplayTitle = (List Options) => Console.WriteLine("ExeToBat > Main > Files > {0} > Edit", Path.GetFileName(source.File)), + DisplayEntry = (List Options, int index, int i) => + { + int MaxLength = options().Keys.Select(x => x.Length).Max(); + Console.WriteLine("[{0}] {1} | {2}", i, Options[index].PadRight(MaxLength, ' '), source.GetType().GetProperty(ValueMap[Options[index]]).GetValue(source).ToString()); + }, + HandleEntry = (List Options, int index) => + { + if (options().ContainsKey(Options[index])) + { + options()[Options[index]](); + } + else + { + ResetInput(); + } + + return false; + }, + RefreshEntries = (List Options) => options().Keys.ToList(), + + }.Show(); + + } + + static void EditExtraction(SourceFile source) + { + bool IsInputValid = false; + while (!IsInputValid) + { + Console.Clear(); + Console.WriteLine("ExeToBat > Main > Files > {0} > Edit > Extraction", Path.GetFileName(source.File)); + Console.Write("\n"); + Console.WriteLine("Documentation: "); + Console.WriteLine("https://ss64.com/nt/syntax-variables.html"); + Console.WriteLine("https://ss64.com/nt/syntax-args.html"); + Console.Write("\n"); + Console.Write("{0}> ", "Directory"); + string input = Console.ReadLine(); + + input.Trim(); + if (!string.IsNullOrEmpty(input)) + { + source.Directory = input; + IsInputValid = true; + } + else + { + IsInputValid = true; + } + } + } + + static void EditParameters(SourceFile source) + { + bool IsInputValid = false; + while (!IsInputValid) + { + Console.Clear(); + Console.WriteLine("ExeToBat > Main > Files > {0} > Edit > Parameters", Path.GetFileName(source.File)); + Console.Write("\n"); + Console.Write("{0}> ", "Parameters"); + string input = Console.ReadLine(); + + input.Trim(); + if (!string.IsNullOrEmpty(input)) + { + source.Parameters = input; + IsInputValid = true; + } + else + { + IsInputValid = true; + } + } + } + + static void EditPosition(SourceFile source) + { + bool IsInputValid = false; + while (!IsInputValid) + { + Console.Clear(); + Console.WriteLine("ExeToBat > Main > Files > {0} > Position : {1}", Path.GetFileName(source.File), Sources.IndexOf(source)); + + Console.Write("\n"); + Console.Write("{0}> ", "New index"); + string input = Console.ReadLine(); + + if (int.TryParse(input, out int index)) + { + if (index < Sources.Count) + { + Sources.Remove(source); + Sources.Insert(index, source); + IsInputValid = true; + } + else + { + ResetInput(); + } + } + else + { + if (string.IsNullOrEmpty(input)) + { + IsInputValid = true; + } + else + { + ResetInput(); + } + } + } + } + + static void BuildBat(List sources, string outputFile) + { + Console.Clear(); + Console.WriteLine("ExeToBat > Main > Generate"); + + if (Sources.Any()) + { + using (StreamWriter writer = new StreamWriter(outputFile)) + { + Console.WriteLine("[Preparing] basic batch structure..."); + writer.WriteLine("@echo off"); + writer.WriteLine(":: Auto-generated batch file by ExeToBat ::"); + writer.WriteLine(""); + + foreach (SourceFile source in sources) + { + Console.WriteLine("[ Reading ] {0}", source.File); + List fileChunks = Convert.ToBase64String(File.ReadAllBytes(source.File)).Chunks(chunk).ToList(); + string tempFile = Path.Combine("%temp%", source.Resource); + writer.WriteLine("("); + + int pos = 0; + foreach (string part in fileChunks) + { + pos++; + Console.Write("[ Writing ] {0} part {1}/{2}\r", Path.GetFileName(source.File), pos.ToString().PadLeft(fileChunks.Count.ToString().Length, '0'), fileChunks.Count); + writer.WriteLine(string.Format("echo {0}", part)); + } + + Console.WriteLine(); + writer.WriteLine(string.Format(") >> \"{0}\"", tempFile)); + writer.WriteLine(""); + + Console.WriteLine("[ Writing ] decode mechanism"); + writer.WriteLine(string.Format("certutil -decode \"{0}\" \"{1}\" >nul 2>&1", tempFile, Path.Combine(source.Directory, Path.GetFileName(source.File)))); + writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", tempFile)); + writer.WriteLine(""); + + if (source.Execute) + { + string wait; + if (source.Wait) { wait = " /wait"; } else { wait = " "; } + Console.WriteLine("[ Writing ] execute mechanism"); + writer.WriteLine(string.Format("start{0} \"\" \"cmd /c {1}\" {2}", wait, Path.Combine(source.Directory, Path.GetFileName(source.File)), source.Parameters)); + if (source.Wait) + { + Console.WriteLine("[ Writing ] wait mechanism"); + if (source.Delete) + { + Console.WriteLine("[ Writing ] delete mechanism"); + writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", Path.Combine(source.Directory, Path.GetFileName(source.File)))); + writer.WriteLine(""); + } + } + + writer.WriteLine(""); + + } + + writer.Flush(); + Console.WriteLine("[Generated] {0}", Path.GetFileName(source.File)); + + } + + Console.WriteLine("[Generated] All done"); + + Console.WriteLine("Press anything..."); + Console.ReadKey(); + + } + } + else + { + Console.WriteLine("No files specified"); + Wait(500); + + } + + + } + + public class SourceFile + { + public string File { get; set; } + public bool Execute { get; set; } = false; + public bool Wait { get; set; } = false; + public bool Delete { get; set; } = false; + public string Resource { get; set; } = GenTemp(); + public string Directory { get; set; } = "%~dp0"; + public string Parameters { get; set; } = ""; + + + public SourceFile(string file) + { + File = file; + } + + static public string GenTemp() + { + return string.Format($"res_{new Random().Next(1000, 10000)}.b64"); + } + + } + + static string[] Chunks(this string toSplit, int chunkSize) + { + int stringLength = toSplit.Length; + + int chunksRequired = (int)Math.Ceiling(stringLength / (decimal)chunkSize); + var stringArray = new string[chunksRequired]; + + int lengthRemaining = stringLength; + + for (int i = 0; i < chunksRequired; i++) + { + int lengthToUse = Math.Min(lengthRemaining, chunkSize); + int startIndex = chunkSize * i; + stringArray[i] = toSplit.Substring(startIndex, lengthToUse); + + lengthRemaining -= lengthToUse; + } + + return stringArray; + } + + + + public static void Wait(int ms) + { + using (System.Threading.ManualResetEvent wait = new System.Threading.ManualResetEvent(false)) + { + wait.WaitOne(ms); + } + } + + public class ListMenu + { + + /// + /// CLOE (Command-line List Options Enumerator). + /// Turns a List into a menu of options. Each list item is asigned a number. Provides several callbacks to customise the menu. + /// + /// List of objects you want to display + public ListMenu(List entries) + { + Entries = entries; + } + + /// + /// The prompt that is displayed to the user. + /// + public string Prompt = "Choose"; + + /// + /// The string to be displayed for the option to exit the menu. + /// + public string ExitEntry = "Back"; + + /// + /// The key the user has to press to exit the menu. + /// + public char ExitKey = 'q'; + + /// + /// Wether or not the user can exit the menu. + /// + public bool UserCanExit = true; + + + private List Entries; + + /// + /// The function that processes the chosen menu entries. + /// + public Func, int, bool> HandleEntry = (entries, index) => + { + Console.Clear(); + Console.WriteLine(entries[index]); + Wait(200); + return false; + }; + + /// + /// The function that displays the menu title. + /// + public Action> DisplayTitle = (entries) => { }; + + /// + /// The function that displays the entry to the user. + /// + public Action, int, int> DisplayEntry = (entries, index, num) => + { + Console.WriteLine("[{0}] {1}", Convert.ToString(num).PadLeft(Convert.ToString(entries.Count).Length, ' '), entries[index]); + }; + + /// + /// The function to update the list of entries. + /// + public Func, List> RefreshEntries = (entries) => + { + return entries; + }; + + /// + /// The function that is called when 0th entry in the list is chosen. + /// Display this entry with the title function. + /// + public Func, bool> ZeroEntry = (entries) => + { + ResetInput(); + return false; + }; + + /// + /// Display the menu. + /// + /// + public ListMenu Show() + { + string readInput = string.Empty; + bool MenuExitIsPending = false; + while (!MenuExitIsPending) + { + Console.Clear(); + int printedEntries = 0; + Entries = RefreshEntries(Entries); + DisplayTitle(Entries); + if (Entries.Any()) + { + int num = 0; + foreach (T entry in Entries) + { + num++; + if (string.IsNullOrEmpty(readInput) || Convert.ToString(num).StartsWith(readInput)) + { + DisplayEntry(Entries, Entries.IndexOf(entry), num); + printedEntries++; + } + + if (Entries.Count > Console.WindowHeight - 5) + { + if (printedEntries >= Console.WindowHeight - (5 + 1)) + { + Console.WriteLine("[{0}] +{1}", ".".PadLeft(Convert.ToString(Entries.Count).Length, '.'), Entries.Count); + break; + } + } + else + { + if (printedEntries == Console.WindowHeight - 5) + { + break; + } + } + + } + } + + if (UserCanExit) + { + Console.WriteLine("[{0}] {1}", Convert.ToString(ExitKey).PadLeft(Convert.ToString(Entries.Count).Length, ' '), ExitEntry); + } + + Console.WriteLine(); + + bool InputIsValid = false; + while (!InputIsValid) + { + Console.Write("{0}> {1}", Prompt, readInput); + ConsoleKeyInfo input = Console.ReadKey(); + Wait(20); + int choiceNum = -1; + switch (input) + { + case var key when key.KeyChar.Equals(ExitKey): + if (UserCanExit) + { + Console.WriteLine(); + InputIsValid = true; + MenuExitIsPending = true; + } + else + { + Console.WriteLine(); + ResetInput(); + } + break; + + case var key when key.Key.Equals(ConsoleKey.Backspace): + if (!string.IsNullOrEmpty(readInput)) + { + Console.Write("\b"); + readInput = readInput.Remove(readInput.Length - 1); + } + InputIsValid = true; + break; + + case var key when key.Key.Equals(ConsoleKey.Enter): + if (!string.IsNullOrEmpty(readInput)) + { + if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1))) + { + MenuExitIsPending = true; + } + readInput = string.Empty; + } + InputIsValid = true; + break; + + case var key when int.TryParse(key.KeyChar.ToString(), out choiceNum): + Console.WriteLine(); + if (string.IsNullOrEmpty(readInput) && choiceNum.Equals(0)) + { + InputIsValid = true; + if (ZeroEntry(Entries)) + { + MenuExitIsPending = true; + } + } + else + { + if (Convert.ToInt32(readInput + Convert.ToString(choiceNum)) <= Entries.Count) + { + InputIsValid = true; + int matchingEntries = 0; + readInput = new System.Text.StringBuilder().Append(readInput).Append(Convert.ToString(choiceNum)).ToString(); + for (int i = 0; i < Entries.Count; i++) + { + if (Convert.ToString(i + 1).StartsWith(readInput) || Convert.ToString(i + 1) == readInput) { matchingEntries++; } + } + if ((readInput.Length == Convert.ToString(Entries.Count).Length) || (matchingEntries == 1)) + { + if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1))) + { + MenuExitIsPending = true; + } + readInput = string.Empty; + } + } + else + { + ResetInput(); + } + } + break; + + default: + Console.WriteLine(); + ResetInput(); + break; + } + } + } + return this; + } + } + + /// + /// A simple template to create Yes or No menus. + /// + /// The title of the menu. + /// The function to be called upon Yes + /// The function to be called upon No + public static void YesNoMenu(string title, Action Yes, Action No) + { + bool IsInputValid = false; + while (!IsInputValid) + { + Console.Write("{0}? [{1}]> ", title, "Y/N"); + string Input = Console.ReadKey().KeyChar.ToString(); + Wait(20); + Console.Write("\n"); + if (string.Equals(Input, "Y", StringComparison.OrdinalIgnoreCase)) + { + IsInputValid = true; + Yes(); + + } + else if (string.Equals(Input, "N", StringComparison.OrdinalIgnoreCase)) + { + IsInputValid = true; + No(); + } + else + { + ResetInput(); + } + } + } + + public static void ResetInput(string error = "Input Invalid") + { + Console.Write(string.Format("[{0}] {1}", "Error", error)); + Wait(150); + ClearCurrentConsoleLine(); + Console.SetCursorPosition(0, Console.CursorTop - 1); + ClearCurrentConsoleLine(); + } + + public static void ClearCurrentConsoleLine() + { + int currentLineCursor = Console.CursorTop; + Console.SetCursorPosition(0, Console.CursorTop); + Console.Write(new string(' ', Console.BufferWidth)); + Console.SetCursorPosition(0, currentLineCursor); + } + + } + +} diff --git a/ExeToBat/Console.cs b/ExeToBat/Console.cs deleted file mode 100644 index 2742be4..0000000 --- a/ExeToBat/Console.cs +++ /dev/null @@ -1,485 +0,0 @@ -using static ExeToBat.Generator; -using static System.ConsoleUtils; -using Mono.Options; -using System.Text.Json; -using System.Reflection; - -namespace ExeToBat -{ - class Console - { - public static void Main(string[] args) => new Console().Show(args); - - public Console() { } - - private readonly Generator generator = new(); - - public void Show(string[] args) - { - string? config = null; - bool help = false; - - OptionSet options = - new() - { - { - "c|config=", - "the config file used for automatic generation", - v => config = v - }, - { "h|help", "show this message and exit", v => help = v != null }, - }; - - try - { - List extra = options.Parse(args); - if (help) - { - Help(options); - } - else if (config != null) - { - try - { - generator.LoadConfig(GeneratorConfig.FromJson(File.ReadAllText(config))); - Generate(interactive: false); - } - catch (Exception e) when (e is IOException || e is JsonException) - { - System.Console.WriteLine("Failed to load config {0}:\n{1}", config, e.Message); - } - } - else - { - MainMenu(); - } - } - catch (OptionException e) - { - System.Console.WriteLine("Invalid arguments: {0}", e.Message); - Help(options); - } - } - - public void Show() => MainMenu(); - - private void Help(OptionSet options) - { - string version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? ""; - System.Console.WriteLine(); - System.Console.WriteLine(string.Format("ExeToBat {0}", version)); - System.Console.WriteLine(); - System.Console.WriteLine("From github.com/clragon/ExeToBat"); - System.Console.WriteLine("Licensed under GNUPL v3"); - System.Console.WriteLine(); - System.Console.WriteLine("Usage:"); - options.WriteOptionDescriptions(System.Console.Out); - } - - private void MainMenu() - { - Dictionary options = - new() - { - { "Files", ChooseSource }, - { "Save config", SaveConfig }, - { "Load config", LoadConfig }, - { "Generate", Generate }, - }; - - new ListMenu(options.Keys.ToList()) - { - DisplayTitle = (Options) => System.Console.WriteLine("ExeToBat"), - DisplayEntry = (Options, index, i) => - { - System.Console.WriteLine("[{0}] {1}", i, Options[index]); - }, - HandleEntry = (Options, index) => - { - options[Options[index]](); - return false; - }, - ExitEntry = "Exit", - }.Show(); - } - - private void SaveConfig() - { - bool IsInputValid = false; - while (!IsInputValid) - { - System.Console.Clear(); - System.Console.WriteLine("ExeToBat > Config > Save"); - System.Console.WriteLine(); - System.Console.Write("{0}> ", "Output File"); - string? input = System.Console.ReadLine(); - - input = input?.Trim()?.Replace("\"", ""); - if (!string.IsNullOrEmpty(input)) - { - try - { - File.WriteAllText(input, generator.SaveConfig().ToJson()); - IsInputValid = true; - } - catch (IOException e) - { - System.Console.WriteLine("Failed to save config:\n{0}", e.Message); - ResetInput(); - } - } - } - } - - private void LoadConfig() - { - bool IsInputValid = false; - while (!IsInputValid) - { - System.Console.Clear(); - System.Console.WriteLine("ExeToBat > Config > Load"); - System.Console.WriteLine(); - System.Console.Write("{0}> ", "Config File"); - string? input = System.Console.ReadLine(); - - input = input?.Trim()?.Replace("\"", ""); - if (!string.IsNullOrEmpty(input)) - { - try - { - generator.LoadConfig(GeneratorConfig.FromJson(File.ReadAllText(input))); - IsInputValid = true; - } - catch (Exception e) when (e is IOException || e is JsonException) - { - System.Console.WriteLine("Failed to load config {0}:\n{1}", input, e.Message); - ResetInput(); - } - } - } - } - - private void ChooseSource() - { - new ListMenu(generator.Sources) - { - DisplayTitle = (sources) => - { - System.Console.WriteLine("ExeToBat > Files"); - System.Console.WriteLine( - "[{0}] ({1})", - Convert.ToString(0).PadLeft(Convert.ToString(sources.Count).Length, ' '), - "Add Files" - ); - }, - DisplayEntry = (sources, index, i) => - System.Console.WriteLine( - "[{0}] {1}", - Convert.ToString(i).PadLeft(Convert.ToString(sources.Count).Length, ' '), - Path.GetFileName(sources[index].Path) - ), - ZeroEntry = (sources) => - { - AddSource(); - return false; - }, - HandleEntry = (sources, index) => - { - ManageSource(sources[index]); - return false; - }, - RefreshEntries = (sources) => generator.Sources, - }.Show(); - } - - private void AddSource() - { - bool IsInputValid = false; - while (!IsInputValid) - { - System.Console.Clear(); - System.Console.WriteLine("ExeToBat > Files > Add"); - System.Console.WriteLine(); - System.Console.Write("{0}> ", "File/Folder"); - string? input = System.Console.ReadLine(); - - input = input?.Trim()?.Replace("\"", ""); - switch (input) - { - case var i when string.IsNullOrEmpty(i): - IsInputValid = true; - break; - case var i when Directory.Exists(i): - IsInputValid = true; - foreach (string file in Directory.GetFiles(i)) - { - generator.Sources.Add(new SourceFile(file)); - } - break; - case var i when File.Exists(i): - IsInputValid = true; - generator.Sources.Add(new SourceFile(i)); - break; - - default: - ResetInput(); - break; - } - } - } - - private void ManageSource(SourceFile source) - { - Dictionary> options = - new() - { - { - "Edit", - () => - { - ModifySource(source); - return false; - } - }, - { - "Position", - () => - { - EditPosition(source); - return false; - } - }, - { - "Delete", - () => - { - generator.Sources.Remove(source); - return true; - } - }, - }; - - new ListMenu(options.Keys.ToList()) - { - DisplayTitle = (Options) => - System.Console.WriteLine( - "ExeToBat > Files > {0}", - Path.GetFileName(source.Path) - ), - DisplayEntry = (Options, index, i) => - System.Console.WriteLine("[{0}] {1}", i, Options[index]), - HandleEntry = (Options, index) => options[Options[index]](), - }.Show(); - } - - private void ModifySource(SourceFile source) - { - List<(string, string, Action)> options() - { - List<(string, string, Action)> result = - new() - { - ("File", source.Path, () => { }), - ("Extraction directory", source.Directory, () => EditExtraction(source)), - ( - "Execute after extraction", - source.Execute.ToString(), - () => source.Execute = !source.Execute - ), - }; - if (source.Execute) - { - result.Add(("Parameters", source.Parameters, () => EditParameters(source))); - result.Add( - ("Wait for exit", source.Wait.ToString(), () => source.Wait = !source.Wait) - ); - } - if (source.Execute && source.Wait) - { - result.Add( - ( - "Delete after execution", - source.Delete.ToString(), - () => source.Delete = !source.Delete - ) - ); - } - return result; - } - - new ListMenu<(string, string, Action)>(options()) - { - DisplayTitle = (Options) => - System.Console.WriteLine( - "ExeToBat > Files > {0} > Edit", - Path.GetFileName(source.Path) - ), - DisplayEntry = (Options, index, i) => - { - int MaxLength = options().ConvertAll(e => e.Item1.Length).Max(); - System.Console.WriteLine( - "[{0}] {1} | {2}", - i, - Options[index].Item1.PadRight(MaxLength, ' '), - Options[index].Item2 - ); - }, - HandleEntry = (Options, index) => - { - Options[index].Item3(); - return false; - }, - RefreshEntries = (Options) => options(), - }.Show(); - } - - private void EditExtraction(SourceFile source) - { - System.Console.Clear(); - System.Console.WriteLine( - "ExeToBat > Files > {0} > Edit > Extraction", - Path.GetFileName(source.Path) - ); - System.Console.WriteLine(); - System.Console.WriteLine("Documentation: "); - System.Console.WriteLine("https://ss64.com/nt/syntax-variables.html"); - System.Console.WriteLine("https://ss64.com/nt/syntax-args.html"); - System.Console.WriteLine(); - System.Console.Write("{0}> ", "Directory"); - string? input = System.Console.ReadLine(); - - if (!string.IsNullOrEmpty(input)) - { - source.Directory = input; - } - } - - private void EditParameters(SourceFile source) - { - System.Console.Clear(); - System.Console.WriteLine( - "ExeToBat > Files > {0} > Edit > Parameters", - Path.GetFileName(source.Path) - ); - System.Console.WriteLine(); - System.Console.Write("{0}> ", "Parameters"); - string? input = System.Console.ReadLine(); - - input = input?.Trim(); - if (!string.IsNullOrEmpty(input)) - { - source.Parameters = input; - } - } - - private void EditPosition(SourceFile source) - { - bool IsInputValid = false; - while (!IsInputValid) - { - System.Console.Clear(); - System.Console.WriteLine( - "ExeToBat > Files > {0} > Position : {1}", - Path.GetFileName(source.Path), - generator.Sources.IndexOf(source) - ); - - System.Console.WriteLine(); - System.Console.Write("{0}> ", "New index"); - string? input = System.Console.ReadLine(); - - if (int.TryParse(input, out int index)) - { - if (index < generator.Sources.Count) - { - generator.Sources.Remove(source); - generator.Sources.Insert(index, source); - IsInputValid = true; - } - else - { - ResetInput(); - } - } - else - { - if (string.IsNullOrEmpty(input)) - { - IsInputValid = true; - } - else - { - ResetInput(); - } - } - } - } - - private void Generate() => Generate(true); - - private void Generate(bool interactive) - { - System.Console.Clear(); - System.Console.WriteLine("ExeToBat > Generate"); - - generator.Generation += OnGenerate; - - generator.Generate("output.bat"); - - generator.Generation -= OnGenerate; - - if (interactive) - { - System.Console.WriteLine("Press anything..."); - System.Console.ReadKey(); - } - } - - private void OnGenerate(object? sender, GeneratorEvent e) - { - switch (e) - { - case GenerationStartEvent s: - System.Console.WriteLine("Starting generation..."); - System.Console.WriteLine("{0} files scheduled", s.Files.Count); - break; - case ReadingFileEvent s: - System.Console.WriteLine("[{0}] Reading file", s.File.Path); - break; - case WritingFilePartEvent s: - System.Console.Write( - "[{0}] writing part {1}/{2}\r", - s.File.Path, - s.Part.ToString().PadLeft(s.Max.ToString().Length, '0'), - s.Max - ); - break; - case WritingFileDecoderEvent s: - System.Console.WriteLine(); - System.Console.WriteLine("[{0}] Writing decode mechanism", s.File.Path); - break; - case WritingFileExecuteEvent s: - System.Console.WriteLine("[{0}] Writing execute mechanism", s.File.Path); - break; - case WritingFileWaitEvent s: - System.Console.WriteLine("[{0}] Writing wait mechanism", s.File.Path); - break; - case WritingFileDeleteEvent s: - System.Console.WriteLine("[{0}] Writing delete mechanism", s.File.Path); - break; - case WritingFileCompleteEvent s: - System.Console.WriteLine("[{0}] Finshed generating!", s.File.Path); - break; - case GenerationCompleteEvent s: - System.Console.WriteLine("Finished generation! Result written to {0}", s.Path); - break; - case GenerationEmptyEvent _: - System.Console.WriteLine("No files specified"); - break; - case GenerationFailedEvent s: - System.Console.WriteLine("Generation failed with:\n{0}", s.Error.Message); - break; - } - } - } -} diff --git a/ExeToBat/ConsoleUtils.cs b/ExeToBat/ConsoleUtils.cs deleted file mode 100644 index 77c0d20..0000000 --- a/ExeToBat/ConsoleUtils.cs +++ /dev/null @@ -1,328 +0,0 @@ -namespace System -{ - public static class ConsoleUtils - { - /// - /// Pauses the thread for a duration. - /// - /// The duration to pause. - public static void Wait(int ms) - { - using (Threading.ManualResetEvent wait = new Threading.ManualResetEvent(false)) - { - wait.WaitOne(ms); - } - } - - /// - /// Clears the current line of the Console. - /// - public static void ClearCurrentConsoleLine() - { - int currentLineCursor = Console.CursorTop; - Console.SetCursorPosition(0, Console.CursorTop); - Console.Write(new string(' ', Console.BufferWidth)); - Console.SetCursorPosition(0, currentLineCursor); - } - - /// - /// Deletes the last line of the console and displays an error for a short duration. - /// - /// The error to display. - public static void ResetInput(string error = "Input Invalid") - { - Console.Write(string.Format("[{0}] {1}", "Error", error)); - Wait(150); - ClearCurrentConsoleLine(); - Console.SetCursorPosition(0, Console.CursorTop - 1); - ClearCurrentConsoleLine(); - } - - /// - /// A simple template to create Yes or No menus. - /// - /// The title of the menu. - /// The function to be called upon Yes - /// The function to be called upon No - public static void YesNoMenu(string title, Action Yes, Action No) - { - bool IsInputValid = false; - while (!IsInputValid) - { - Console.Write("{0}? [{1}]> ", title, "Y/N"); - string Input = Console.ReadKey().KeyChar.ToString(); - Wait(20); - Console.Write("\n"); - if (string.Equals(Input, "Y", StringComparison.OrdinalIgnoreCase)) - { - IsInputValid = true; - Yes(); - } - else if (string.Equals(Input, "N", StringComparison.OrdinalIgnoreCase)) - { - IsInputValid = true; - No(); - } - else - { - ResetInput(); - } - } - } - - public class ListMenu - { - /// - /// Command-line List Options Enumerator - /// Turns a List into a menu of options. Each list item is asigned a number. Provides several callbacks to customise the menu. - /// - /// List of objects you want to display - public ListMenu(List entries) - { - Entries = entries; - } - - /// - /// The prompt that is displayed to the user. - /// - public string Prompt = "Choose"; - - /// - /// The string to be displayed for the option to exit the menu. - /// - public string ExitEntry = "Back"; - - /// - /// The key the user has to press to exit the menu. - /// - public char ExitKey = 'q'; - - /// - /// Wether or not the user can exit the menu. - /// - public bool UserCanExit = true; - - private List Entries; - - /// - /// The function that processes the chosen menu entries. - /// - public Func, int, bool> HandleEntry = (entries, index) => - { - Console.Clear(); - Console.WriteLine(entries[index]); - Wait(200); - return false; - }; - - /// - /// The function that displays the menu title. - /// - public Action> DisplayTitle = (entries) => { }; - - /// - /// The function that displays the entry to the user. - /// - public Action, int, int> DisplayEntry = (entries, index, num) => - { - Console.WriteLine( - "[{0}] {1}", - Convert.ToString(num).PadLeft(Convert.ToString(entries.Count).Length, ' '), - entries[index] - ); - }; - - /// - /// The function to update the list of entries. - /// - public Func, List> RefreshEntries = (entries) => entries; - - /// - /// The function that is called when 0th entry in the list is chosen. - /// Display this entry with the title function. - /// - public Func, bool> ZeroEntry = (entries) => - { - ResetInput(); - return false; - }; - - /// - /// Display the menu. - /// - /// - public ListMenu Show() - { - string readInput = string.Empty; - bool MenuExitIsPending = false; - while (!MenuExitIsPending) - { - Console.Clear(); - int printedEntries = 0; - Entries = RefreshEntries(Entries); - DisplayTitle(Entries); - if (Entries.Any()) - { - int num = 0; - foreach (T entry in Entries) - { - num++; - if ( - string.IsNullOrEmpty(readInput) - || Convert.ToString(num).StartsWith(readInput) - ) - { - DisplayEntry(Entries, Entries.IndexOf(entry), num); - printedEntries++; - } - - if (Entries.Count > Console.WindowHeight - 5) - { - if (printedEntries >= Console.WindowHeight - (5 + 1)) - { - Console.WriteLine( - "[{0}] +{1}", - ".".PadLeft(Convert.ToString(Entries.Count).Length, '.'), - Entries.Count - ); - break; - } - } - else - { - if (printedEntries == Console.WindowHeight - 5) - { - break; - } - } - } - } - - if (UserCanExit) - { - Console.WriteLine( - "[{0}] {1}", - Convert - .ToString(ExitKey) - .PadLeft(Convert.ToString(Entries.Count).Length, ' '), - ExitEntry - ); - } - - Console.WriteLine(); - - bool InputIsValid = false; - while (!InputIsValid) - { - Console.Write("{0}> {1}", Prompt, readInput); - ConsoleKeyInfo input = Console.ReadKey(); - Wait(20); - int choiceNum = -1; - switch (input) - { - case var key when key.KeyChar.Equals(ExitKey): - if (UserCanExit) - { - Console.WriteLine(); - InputIsValid = true; - MenuExitIsPending = true; - } - else - { - Console.WriteLine(); - ResetInput(); - } - break; - - case var key when key.Key.Equals(ConsoleKey.Backspace): - if (!string.IsNullOrEmpty(readInput)) - { - Console.Write("\b"); - readInput = readInput.Remove(readInput.Length - 1); - } - InputIsValid = true; - break; - - case var key when key.Key.Equals(ConsoleKey.Enter): - if (!string.IsNullOrEmpty(readInput)) - { - if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1))) - { - MenuExitIsPending = true; - } - readInput = string.Empty; - } - InputIsValid = true; - break; - - case var key when int.TryParse(key.KeyChar.ToString(), out choiceNum): - Console.WriteLine(); - if (string.IsNullOrEmpty(readInput) && choiceNum.Equals(0)) - { - InputIsValid = true; - if (ZeroEntry(Entries)) - { - MenuExitIsPending = true; - } - } - else - { - if ( - Convert.ToInt32(readInput + Convert.ToString(choiceNum)) - <= Entries.Count - ) - { - InputIsValid = true; - int matchingEntries = 0; - readInput = new System.Text.StringBuilder() - .Append(readInput) - .Append(Convert.ToString(choiceNum)) - .ToString(); - for (int i = 0; i < Entries.Count; i++) - { - if ( - Convert.ToString(i + 1).StartsWith(readInput) - || Convert.ToString(i + 1) == readInput - ) - { - matchingEntries++; - } - } - if ( - ( - readInput.Length - == Convert.ToString(Entries.Count).Length - ) || (matchingEntries == 1) - ) - { - if ( - HandleEntry( - Entries, - (Convert.ToInt32(readInput) - 1) - ) - ) - { - MenuExitIsPending = true; - } - readInput = string.Empty; - } - } - else - { - ResetInput(); - } - } - break; - - default: - Console.WriteLine(); - ResetInput(); - break; - } - } - } - return this; - } - } - } -} diff --git a/ExeToBat/ExeToBat.csproj b/ExeToBat/ExeToBat.csproj index 3a0003b..fcf647c 100644 --- a/ExeToBat/ExeToBat.csproj +++ b/ExeToBat/ExeToBat.csproj @@ -1,29 +1,53 @@ - - + + + + Debug + AnyCPU + {04F45237-23C8-4EE6-B61C-6C47B9979A4B} Exe - true - true - net6.0 - enable - enable - 2.0.0.0 + ExeToBat + ExeToBat + v4.6.1 + 512 + true + true - - - $(WarningsAsErrors);NU1605;CS8600;CS8601;CS8602;CS8603;CS8604;CS8613;CS8614;CS8619;CS8620;CS8622;CS8625;CS8629;CS8633;CS8767; + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 - - - $(WarningsAsErrors);NU1605;CS8600;CS8601;CS8602;CS8603;CS8604;CS8613;CS8614;CS8619;CS8620;CS8622;CS8625;CS8629;CS8633;CS8767; + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 - - + + + + + + + + - - + + - - + + + + + \ No newline at end of file diff --git a/ExeToBat/Generator.cs b/ExeToBat/Generator.cs deleted file mode 100644 index ee361f4..0000000 --- a/ExeToBat/Generator.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System.Text.Json; - -namespace ExeToBat -{ - public class Generator - { - public Generator() { } - - public Generator(GeneratorConfig config) - { - LoadConfig(config); - } - - public const int ChunkSize = 8000; - - public List Sources = new(); - - public class SourceFile - { - /// - /// Represents a file that is later embedded in a bat. - /// - /// The path of the file. - public SourceFile(string path) - { - Path = path; - } - - public string Path { get; set; } - public bool Execute { get; set; } = false; - public bool Wait { get; set; } = false; - public bool Delete { get; set; } = false; - public string Resource { get; set; } = GetTempFileName(); - public string Directory { get; set; } = "%~dp0"; - public string Parameters { get; set; } = ""; - - static public string GetTempFileName() - { - return string.Format($"res_{new Random().Next(1000, 10000)}.b64"); - } - } - - public class GeneratorConfig - { - /// - /// The configuration for a Generator. - /// - /// - public GeneratorConfig(List sources) - { - Sources = sources; - } - - public List Sources { get; private set; } - - public string ToJson() - { - return JsonSerializer.Serialize(this); - } - - public static GeneratorConfig FromJson(string raw) - { - return JsonSerializer.Deserialize(raw) - ?? new GeneratorConfig(new()); - } - } - - /// - /// Exports the variables of this Generator as a configuration. - /// - /// - public GeneratorConfig SaveConfig() - { - return new GeneratorConfig(Sources); - } - - /// - /// Loads a configuration into this Generator. - /// - /// - public void LoadConfig(GeneratorConfig config) - { - Sources = config.Sources; - } - - /// - /// Sends progress updates about ongoing generation task. - /// - public event EventHandler? Generation; - - protected virtual void OnGeneration(GeneratorEvent e) - { - EventHandler? handler = Generation; - handler?.Invoke(this, e); - } - - /// - /// Generates a batch file with all specified source files. - /// - /// Target output file path. - public void Generate(string outputFile) - { - if (Sources.Any()) - { - using StreamWriter writer = new(outputFile); - OnGeneration(new GenerationStartEvent(Sources)); - writer.WriteLine("@echo off"); - writer.WriteLine(":: Auto-generated batch file by ExeToBat ::"); - writer.WriteLine(""); - - foreach (SourceFile source in Sources) - { - OnGeneration(new ReadingFileEvent(source)); - List fileChunks = Convert - .ToBase64String(File.ReadAllBytes(source.Path)) - .Chunks(ChunkSize) - .ToList(); - string tempFile = Path.Combine("%temp%", source.Resource); - writer.WriteLine("("); - - int pos = 0; - foreach (string part in fileChunks) - { - pos++; - OnGeneration(new WritingFilePartEvent(source, pos, fileChunks.Count)); - writer.WriteLine(string.Format("echo {0}", part)); - } - - writer.WriteLine(string.Format(") >> \"{0}\"", tempFile)); - writer.WriteLine(""); - - OnGeneration(new WritingFileDecoderEvent(source)); - writer.WriteLine( - string.Format( - "certutil -decode \"{0}\" \"{1}\" >nul 2>&1", - tempFile, - Path.Combine(source.Directory, Path.GetFileName(source.Path)) - ) - ); - writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", tempFile)); - writer.WriteLine(""); - - if (source.Execute) - { - string wait; - if (source.Wait) - { - wait = " /wait"; - } - else - { - wait = " "; - } - - OnGeneration(new WritingFileExecuteEvent(source)); - writer.WriteLine( - string.Format( - "start{0} \"\" \"cmd /c {1}\" {2}", - wait, - Path.Combine(source.Directory, Path.GetFileName(source.Path)), - source.Parameters - ) - ); - if (source.Wait) - { - OnGeneration(new WritingFileWaitEvent(source)); - if (source.Delete) - { - OnGeneration(new WritingFileDeleteEvent(source)); - writer.WriteLine( - string.Format( - "del /f /q \"{0}\" >nul 2>&1", - Path.Combine( - source.Directory, - Path.GetFileName(source.Path) - ) - ) - ); - writer.WriteLine(""); - } - } - - writer.WriteLine(""); - } - - writer.Flush(); - OnGeneration(new WritingFileCompleteEvent(source)); - } - - OnGeneration(new GenerationCompleteEvent(outputFile)); - } - else - { - OnGeneration(new GenerationEmptyEvent()); - } - } - - public abstract class GeneratorEvent : EventArgs { } - - public abstract class GeneratorFileEvent : GeneratorEvent - { - public GeneratorFileEvent(SourceFile file) - { - File = file; - } - - public SourceFile File { get; private set; } - } - - public class GenerationStartEvent : GeneratorEvent - { - public GenerationStartEvent(List files) - { - Files = files; - } - - public List Files { get; private set; } - } - - public class ReadingFileEvent : GeneratorFileEvent - { - public ReadingFileEvent(SourceFile file) : base(file) { } - } - - public class WritingFilePartEvent : GeneratorFileEvent - { - public WritingFilePartEvent(SourceFile file, int part, int max) : base(file) - { - Part = part; - Max = max; - } - - public int Part { get; private set; } - public int Max { get; private set; } - } - - public class WritingFileDecoderEvent : GeneratorFileEvent - { - public WritingFileDecoderEvent(SourceFile file) : base(file) { } - } - - public class WritingFileExecuteEvent : GeneratorFileEvent - { - public WritingFileExecuteEvent(SourceFile file) : base(file) { } - } - - public class WritingFileWaitEvent : GeneratorFileEvent - { - public WritingFileWaitEvent(SourceFile file) : base(file) { } - } - - public class WritingFileDeleteEvent : GeneratorFileEvent - { - public WritingFileDeleteEvent(SourceFile file) : base(file) { } - } - - public class WritingFileCompleteEvent : GeneratorFileEvent - { - public WritingFileCompleteEvent(SourceFile file) : base(file) { } - } - - public class GenerationCompleteEvent : GeneratorEvent - { - public GenerationCompleteEvent(string path) - { - Path = path; - } - - public string Path { get; private set; } - } - - public class GenerationEmptyEvent : GeneratorEvent - { - public GenerationEmptyEvent() { } - } - - public class GenerationFailedEvent : GeneratorEvent - { - public GenerationFailedEvent(Exception error) - { - Error = error; - } - - public Exception Error { get; private set; } - } - } - - internal static class StringChunks - { - public static string[] Chunks(this string toSplit, int chunkSize) - { - int stringLength = toSplit.Length; - - int chunksRequired = (int)Math.Ceiling(stringLength / (decimal)chunkSize); - var stringArray = new string[chunksRequired]; - - int lengthRemaining = stringLength; - - for (int i = 0; i < chunksRequired; i++) - { - int lengthToUse = Math.Min(lengthRemaining, chunkSize); - int startIndex = chunkSize * i; - stringArray[i] = toSplit.Substring(startIndex, lengthToUse); - - lengthRemaining -= lengthToUse; - } - - return stringArray; - } - } -} diff --git a/ExeToBat/Properties/AssemblyInfo.cs b/ExeToBat/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6af03c8 --- /dev/null +++ b/ExeToBat/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("ExeToBat")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ExeToBat")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly +// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("04f45237-23c8-4ee6-b61c-6c47b9979a4b")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]