public AdvancedOptionsForm(MainForm mainForm, bool isRepackable, bool openUnpackDirectory, MainForm.UnpackDirectory unpackDirectoryType)
        {
            InitializeComponent();
            this.mainForm = mainForm;

            checkRepackable.Checked       = isRepackable;
            checkOpenUnpackFolder.Checked = openUnpackDirectory;
            if (unpackDirectoryType == MainForm.UnpackDirectory.Desktop)
            {
                comboUnpackDir.SelectedIndex = 1;
            }
            else if (unpackDirectoryType == MainForm.UnpackDirectory.NextToPackedExe)
            {
                comboUnpackDir.SelectedIndex = 2;
            }
            else if (unpackDirectoryType == MainForm.UnpackDirectory.AskAtLaunch)
            {
                comboUnpackDir.SelectedIndex = 3;
            }
            else
            {
                comboUnpackDir.SelectedIndex = 0;
            }

            SetRepackDescription();
        }
Exemple #2
0
        static void NoGuiMode(string[] args)
        {
            string sourceAppFolder = null, mainExePath = null, destinationPath = null, customIconPath = null, launchArguments = "", fileDescription = null, password = "";
            bool   selfRepackable = false, openUnpackedDir = false, quietPacking = false, isWindowlessUnpacker = false;
            bool   showHelp = false;

            MainForm.UnpackDirectory unpackDir = MainForm.UnpackDirectory.Temp;

            // Do all the important things in the try block to be able to execute SendKeys in the finally block regardless of the return point
            try
            {
                // Initialize possible arguments
                var argsParser = new OptionSet()
                {
                    { "s|src|source-folder|input=",
                      "Directory containing all the files of the target application.",
                      s => sourceAppFolder = s.TrimEnd('\\', '/') },
                    { "e|exe|main-exe=",
                      "Local path to the main executable inside the source app folder. Can be .exe, .bat, .cmd, .lnk or .sh file. This is the application that will launch whenever the packed app extracts itself.",
                      e => mainExePath = e.TrimStart('\\', '/') },
                    { "d|dst|destination|output=",
                      "Location where packed app will be created.",
                      d => destinationPath = d },
                    { "i|ico|icon=",
                      "Custom icon for the packed application.",
                      c => customIconPath = c },
                    { "r|repack|self-repack",
                      "Sets the packed application to refresh itself after the main executable closes. Refreshing adds and replaces files in the packed executable with those created and modified during runtime.",
                      r => selfRepackable = r != null },
                    { "a|args|arguments=",
                      "Arguments to pass to the main executable at every launch.\nYou should add escaped quotes for paths:\n-a \"--some-flag \\\"c:\\file with spaces.txt\\\"\"",
                      a => launchArguments = a ?? "" },
                    { "fd|description|file-description=",
                      "FileDescription string for main executable (used in Windows 'Open with...' menu).",
                      fd => fileDescription = fd },
                    { "o|open-dir",
                      "Open folder with unpacked application in Explorer when you launch packed app.",
                      o => openUnpackedDir = o != null },
                    { "q|quiet|silent",
                      "No progress messages will be shown during the packing process.",
                      q => quietPacking = q != null },
                    { "h|help|?",
                      "Show this message and exit.",
                      v => showHelp = v != null },
                    { "u|udir|unpack-dir=",
                      "Directory where to unpack app files. Can be [temp|desktop|same|ask]. Default is 'temp'.\nIf set 'same', temp directory will be created in the same directory where packed exe is.\nIf set 'ask', it will prompt user before each unpacking.",
                      u => {
                          if (u == "temp")
                          {
                              unpackDir = MainForm.UnpackDirectory.Temp;
                          }
                          else if (u == "desktop")
                          {
                              unpackDir = MainForm.UnpackDirectory.Desktop;
                          }
                          else if (u == "same")
                          {
                              unpackDir = MainForm.UnpackDirectory.NextToPackedExe;
                          }
                          else if (u == "ask")
                          {
                              unpackDir = MainForm.UnpackDirectory.AskAtLaunch;
                          }
                      } },
                    { "p|pass|password="******"Password for packed application",
                      p => password = p },
                    { "w|windowless",
                      "Makes unpacker daemon completely hidden (console window doesn't appear for a split-second), BUT it can not redirect stdout of a packed application",
                      w => isWindowlessUnpacker = w != null }
                };

                try
                {
                    // Parse arguments
                    argsParser.Parse(args);
                }
                catch (OptionException e)
                {
                    Console.WriteLine("Appacker: " + e.Message);
                    Console.WriteLine(TRY_HELP, args[0]);
                    return;
                }

                // If --help flag is present
                if (showHelp)
                {
                    ShowHelp(argsParser, args[0]);
                    return;
                }
                else
                {
                    // Validate arguments and show error messages if required
                    if (!ValidateArguments(sourceAppFolder, mainExePath, destinationPath, customIconPath, isQuiet: quietPacking))
                    {
                        Console.WriteLine(USAGE, args[0]);
                        Console.WriteLine(TRY_HELP, args[0]);
                        return;
                    }

                    // If destination file was not specified => save package next to the source directory
                    if (destinationPath == null)
                    {
                        destinationPath = Path.Combine(Path.GetDirectoryName(sourceAppFolder), Path.GetFileName(mainExePath));
                    }

                    // == PACKING ==
                    // Subscribe to progress update events
                    if (!quietPacking)
                    {
                        // Position of the concole cursor
                        (int left, int top)firstMessageCursorPos = default;
                        bool firstMessageReceived = false;
                        // Value from ProgressUpdate message
                        int    maxValue = 0;
                        object syncObj  = new object();

                        MainForm.PackingProgressUpdate += (o, progress) =>
                        {
                            // Multiple messages can try to write to the same spon in the Console, hence lock
                            lock (syncObj)
                            {
                                // This check is kinda crutchy. Using Compatibility mode in XDMessaging may lead to non-sequential progress updates,
                                // i.e. `5% progress` message can arrive after `8% progress` one. It's not nice, but there's no easy fixes, so i'm gonna leave it as it is,
                                // but having `95%` message arrive after `100%` is ugly and this check for packingFinished fixes at least this situation
                                if (!packingFinished)
                                {
                                    // Remember the position of the first message in Console, so we can write to the same spot
                                    // and it looks neat and not like a stream of text
                                    if (!firstMessageReceived)
                                    {
                                        firstMessageReceived  = true;
                                        firstMessageCursorPos = (Console.CursorLeft, Console.CursorTop);
                                        // Also remember maxValue for the PackingFinished event, because that event doesn't have `100/100` like text. Yeah, another crutch, i know .__.
                                        maxValue = progress.maxValue;
                                    }

                                    // Move cursor and write new progress message on top of the old one
                                    Console.SetCursorPosition(firstMessageCursorPos.left, firstMessageCursorPos.top);
                                    Console.WriteLine($"Packing... {progress.currentValue} / {progress.maxValue} done [{100f * progress.currentValue / progress.maxValue:F2}%]");
                                }
                            }
                        };
                        MainForm.PackingFinished += (o, exitCode) =>
                        {
                            // Multiple messages can try to write to the same spon in the Console, hence lock
                            lock (syncObj)
                            {
                                if (exitCode == 0)
                                {
                                    // Write success message
                                    Console.SetCursorPosition(firstMessageCursorPos.left, firstMessageCursorPos.top);
                                    Console.WriteLine($"Packing... {maxValue} / {maxValue} done [100.00%]");
                                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                                    Console.WriteLine($"Packing complete!");
                                    Console.ResetColor();
                                    Console.WriteLine($"Packed app location: `{Path.GetFullPath(destinationPath)}`");
                                }
                                // Show error message if return code is abnormal
                                else
                                {
                                    Console.ForegroundColor = ConsoleColor.DarkRed;
                                    Console.WriteLine($"Packing finished with an error: {MainForm.GetErrorMessage(exitCode)}");
                                    Console.ResetColor();
                                }

                                // Release Sleep() loop
                                packingFinished = true;
                            }
                        };
                    }
                    // If --quiet then subscribe only to PackingFinished event to release Sleep() loop and display possible errors
                    else
                    {
                        MainForm.PackingFinished += (o, exitCode) =>
                        {
                            // Show error message if return code is abnormal
                            if (exitCode != 0)
                            {
                                Console.WriteLine();
                                Console.ForegroundColor = ConsoleColor.DarkRed;
                                Console.WriteLine($"Packing finished with an error: {MainForm.GetErrorMessage(exitCode)}");
                                Console.ResetColor();
                            }

                            // Release Sleep() loop
                            packingFinished = true;
                        };
                    }

                    // Initiate packing process
                    MainForm.StartPacking(sourceAppFolder, mainExePath, destinationPath,
                                          customIconPath: customIconPath,
                                          selfRepackable: selfRepackable,
                                          launchArguments: launchArguments,
                                          customFileDescription: fileDescription,
                                          openUnpackDir: openUnpackedDir,
                                          unpackDirectory: unpackDir,
                                          noGUI: true,
                                          passHash: Password.GetPasswordHashString(password),
                                          isWindowlessUnpacker: isWindowlessUnpacker);

                    // Keep the process alive until packing process finishes in order to receive progress messages and to clean up temp files
                    while (!packingFinished)
                    {
                        Thread.Sleep(200);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw e;
            }
            // When Appacker exits, send {Enter} key press to the parent command prompt (if not in --quiet mode)
            finally
            {
                if (!quietPacking)
                {
                    Console.WriteLine();
                    Console.Write(Environment.CurrentDirectory + ">");
                }
            }
        }