/// <summary> /// Starts the session. /// Opens the emulator view in the control (HWND given in <paramref name="hostcontext" />) by starting the ConEmu child process and giving it that HWND; ConEmu then starts the child Console Process for the commandline given in <paramref name="startinfo" /> and makes it run in the console emulator window. /// </summary> /// <param name="startinfo">User-defined startup parameters for the console process.</param> /// <param name="hostcontext">Control-related parameters.</param> /// <param name="joinableTaskFactory">The <see cref="JoinableTaskFactory"/>.</param> public ConEmuSession([NotNull] ConEmuStartInfo startinfo, [NotNull] HostContext hostcontext, [NotNull] JoinableTaskFactory joinableTaskFactory) { if (startinfo == null) { throw new ArgumentNullException(nameof(startinfo)); } if (hostcontext == null) { throw new ArgumentNullException(nameof(hostcontext)); } if (joinableTaskFactory == null) { throw new ArgumentNullException(nameof(joinableTaskFactory)); } if (string.IsNullOrEmpty(startinfo.ConsoleProcessCommandLine)) { throw new InvalidOperationException($"Cannot start a new console process for command line “{startinfo.ConsoleProcessCommandLine}” because it's either NULL, or empty, or whitespace."); } _joinableTaskFactory = joinableTaskFactory; _startinfo = startinfo; startinfo.MarkAsUsedUp(); // No more changes allowed in this copy // Directory for working files, +cleanup _dirTempWorkingFolder = Init_TempWorkingFolder(); // Events wiring: make sure sinks pre-installed with start-info also get notified Init_WireEvents(startinfo); // Should feed ANSI log? if (startinfo.IsReadingAnsiStream) { _ansilog = Init_AnsiLog(startinfo); } // Cmdline CommandLineBuilder cmdl = Init_MakeConEmuCommandLine(startinfo, hostcontext, _ansilog, _dirTempWorkingFolder); // Start ConEmu // If it fails, lifetime will be terminated; from them on, termination will be bound to ConEmu process exit _process = Init_StartConEmu(startinfo, cmdl); // GuiMacro executor _guiMacroExecutor = new GuiMacroExecutor(startinfo.ConEmuConsoleServerExecutablePath); _lifetime.Add(() => ((IDisposable)_guiMacroExecutor).Dispose()); // Monitor payload process Init_ConsoleProcessMonitoring(); }
private AnsiLog Init_AnsiLog([NotNull] ConEmuStartInfo startinfo) { var ansilog = new AnsiLog(_dirTempWorkingFolder); _lifetime.Add(() => ansilog.Dispose()); if (startinfo.AnsiStreamChunkReceivedEventSink != null) { ansilog.AnsiStreamChunkReceived += startinfo.AnsiStreamChunkReceivedEventSink; } // Do the pumping periodically (TODO: take this to async?.. but would like to keep the final evt on the home thread, unless we go to tasks) // TODO: if ConEmu writes to a pipe, we might be getting events when more data comes to the pipe rather than poll it by timer var timer = new Timer() { Interval = (int)TimeSpan.FromSeconds(.1).TotalMilliseconds, Enabled = true }; timer.Tick += delegate { ansilog.PumpStream(); }; _lifetime.Add(() => timer.Dispose()); return(ansilog); }
private static unsafe CommandLineBuilder Init_MakeConEmuCommandLine([NotNull] ConEmuStartInfo startinfo, [NotNull] HostContext hostcontext, [CanBeNull] AnsiLog ansilog, [NotNull] DirectoryInfo dirLocalTempRoot) { if (startinfo == null) { throw new ArgumentNullException(nameof(startinfo)); } if (hostcontext == null) { throw new ArgumentNullException(nameof(hostcontext)); } var cmdl = new CommandLineBuilder(); // This sets up hosting of ConEmu in our control cmdl.AppendSwitch("-InsideWnd"); cmdl.AppendFileNameIfNotNull("0x" + ((ulong)hostcontext.HWndParent).ToString("X")); // Don't use keyboard hooks in ConEmu when embedded cmdl.AppendSwitch("-NoKeyHooks"); switch (startinfo.LogLevel) { case ConEmuStartInfo.LogLevels.Basic: cmdl.AppendSwitch("-Log"); break; case ConEmuStartInfo.LogLevels.Detailed: cmdl.AppendSwitch("-Log2"); break; case ConEmuStartInfo.LogLevels.Advanced: cmdl.AppendSwitch("-Log3"); break; case ConEmuStartInfo.LogLevels.Full: cmdl.AppendSwitch("-Log4"); break; } // Basic settings, like fonts and hidden tab bar // Plus some of the properties on this class cmdl.AppendSwitch("-LoadCfgFile"); cmdl.AppendFileNameIfNotNull(Init_MakeConEmuCommandLine_EmitConfigFile(dirLocalTempRoot, startinfo, hostcontext)); if (!string.IsNullOrEmpty(startinfo.StartupDirectory)) { cmdl.AppendSwitch("-Dir"); cmdl.AppendFileNameIfNotNull(startinfo.StartupDirectory); } // ANSI Log file if (ansilog != null) { cmdl.AppendSwitch("-AnsiLog"); cmdl.AppendFileNameIfNotNull(ansilog.Directory.FullName); } if (dirLocalTempRoot == null) { throw new ArgumentNullException(nameof(dirLocalTempRoot)); } // This one MUST be the last switch cmdl.AppendSwitch("-cmd"); // Console mode command // NOTE: if placed AFTER the payload command line, otherwise somehow conemu hooks won't fetch the switch out of the cmdline, e.g. with some complicated git fetch/push cmdline syntax which has a lot of colons inside on itself string sConsoleExitMode; switch (startinfo.WhenConsoleProcessExits) { case WhenConsoleProcessExits.CloseConsoleEmulator: sConsoleExitMode = "n"; break; case WhenConsoleProcessExits.KeepConsoleEmulator: sConsoleExitMode = "c0"; break; case WhenConsoleProcessExits.KeepConsoleEmulatorAndShowMessage: sConsoleExitMode = "c"; break; default: throw new ArgumentOutOfRangeException("ConEmuStartInfo" + "::" + "WhenConsoleProcessExits", startinfo.WhenConsoleProcessExits, "This is not a valid enum value."); } cmdl.AppendSwitchIfNotNull("-cur_console:", $"{(startinfo.IsElevated ? "a" : "")}{sConsoleExitMode}"); if (!string.IsNullOrEmpty(startinfo.ConsoleProcessExtraArgs)) { cmdl.AppendSwitch(startinfo.ConsoleProcessExtraArgs); } // And the shell command line itself cmdl.AppendSwitch(startinfo.ConsoleProcessCommandLine); return(cmdl); }
private AnsiLog Init_AnsiLog([NotNull] ConEmuStartInfo startinfo) { var ansilog = new AnsiLog(_dirTempWorkingFolder); _lifetime.Add(() => ansilog.Dispose()); if(startinfo.AnsiStreamChunkReceivedEventSink != null) ansilog.AnsiStreamChunkReceived += startinfo.AnsiStreamChunkReceivedEventSink; // Do the pumping periodically (TODO: take this to async?.. but would like to keep the final evt on the home thread, unless we go to tasks) // TODO: if ConEmu writes to a pipe, we might be getting events when more data comes to the pipe rather than poll it by timer var timer = new Timer() {Interval = (int)TimeSpan.FromSeconds(.1).TotalMilliseconds, Enabled = true}; timer.Tick += delegate { ansilog.PumpStream(); }; _lifetime.Add(() => timer.Dispose()); return ansilog; }
/// <summary> /// Starts the session. /// Opens the emulator view in the control (HWND given in <paramref name="hostcontext" />) by starting the ConEmu child process and giving it that HWND; ConEmu then starts the child Console Process for the commandline given in <paramref name="startinfo" /> and makes it run in the console emulator window. /// </summary> /// <param name="startinfo">User-defined startup parameters for the console process.</param> /// <param name="hostcontext">Control-related parameters.</param> public ConEmuSession([NotNull] ConEmuStartInfo startinfo, [NotNull] HostContext hostcontext) { if(startinfo == null) throw new ArgumentNullException(nameof(startinfo)); if(hostcontext == null) throw new ArgumentNullException(nameof(hostcontext)); if(string.IsNullOrEmpty(startinfo.ConsoleProcessCommandLine)) throw new InvalidOperationException($"Cannot start a new console process for command line “{startinfo.ConsoleProcessCommandLine}” because it's either NULL, or empty, or whitespace."); _startinfo = startinfo; startinfo.MarkAsUsedUp(); // No more changes allowed in this copy // Directory for working files, +cleanup _dirTempWorkingFolder = Init_TempWorkingFolder(); // Events wiring: make sure sinks pre-installed with start-info also get notified Init_WireEvents(startinfo); // Should feed ANSI log? if(startinfo.IsReadingAnsiStream) _ansilog = Init_AnsiLog(startinfo); // Cmdline CommandLineBuilder cmdl = Init_MakeConEmuCommandLine(startinfo, hostcontext, _ansilog, _dirTempWorkingFolder); // Start ConEmu // If it fails, lifetime will be terminated; from them on, termination will be bound to ConEmu process exit _process = Init_StartConEmu(startinfo, cmdl); // GuiMacro executor _guiMacroExecutor = new GuiMacroExecutor(startinfo.ConEmuConsoleServerExecutablePath); _lifetime.Add(() => ((IDisposable)_guiMacroExecutor).Dispose()); // Monitor payload process Init_ConsoleProcessMonitoring(); }