public override async void Launch()

in src/csharp/MonoDebugSession.cs [186:398]


		public override async void Launch(Response response, dynamic args)
		{
			_attachMode = false;

			SetExceptionBreakpoints(args.__exceptionOptions);

			// validate argument 'program'
			string programPath = getString(args, "program");
			if (programPath == null) {
				SendErrorResponse(response, 3001, "Property 'program' is missing or empty.", null);
				return;
			}
			programPath = ConvertClientPathToDebugger(programPath);
			if (!File.Exists(programPath) && !Directory.Exists(programPath)) {
				SendErrorResponse(response, 3002, "Program '{path}' does not exist.", new { path = programPath });
				return;
			}

			// validate argument 'cwd'
			var workingDirectory = (string)args.cwd;
			if (workingDirectory != null) {
				workingDirectory = workingDirectory.Trim();
				if (workingDirectory.Length == 0) {
					SendErrorResponse(response, 3003, "Property 'cwd' is empty.");
					return;
				}
				workingDirectory = ConvertClientPathToDebugger(workingDirectory);
				if (!Directory.Exists(workingDirectory)) {
					SendErrorResponse(response, 3004, "Working directory '{path}' does not exist.", new { path = workingDirectory });
					return;
				}
			}

			// validate argument 'runtimeExecutable'
			var runtimeExecutable = (string)args.runtimeExecutable;
			if (runtimeExecutable != null) {
				runtimeExecutable = runtimeExecutable.Trim();
				if (runtimeExecutable.Length == 0) {
					SendErrorResponse(response, 3005, "Property 'runtimeExecutable' is empty.");
					return;
				}
				runtimeExecutable = ConvertClientPathToDebugger(runtimeExecutable);
				if (!File.Exists(runtimeExecutable)) {
					SendErrorResponse(response, 3006, "Runtime executable '{path}' does not exist.", new { path = runtimeExecutable });
					return;
				}
			}


			// validate argument 'env'
			Dictionary<string, string> env = new Dictionary<string, string>();
			var environmentVariables = args.env;
			if (environmentVariables != null) {
				foreach (var entry in environmentVariables) {
					env.Add((string)entry.Name, (string)entry.Value);
				}
			}

			const string host = "127.0.0.1";
			int port = Utilities.FindFreePort(55555);

			string mono_path = runtimeExecutable;
			if (mono_path == null) {
				if (!Utilities.IsOnPath(MONO)) {
					SendErrorResponse(response, 3011, "Can't find runtime '{_runtime}' on PATH.", new { _runtime = MONO });
					return;
				}
				mono_path = MONO;     // try to find mono through PATH
			}


			var cmdLine = new List<String>();

			bool debug = !getBool(args, "noDebug", false);
			
			if (debug) {
				bool passDebugOptionsViaEnvironmentVariable = getBool(args, "passDebugOptionsViaEnvironmentVariable", false);

				if (passDebugOptionsViaEnvironmentVariable) {
					if (!env.ContainsKey("MONO_ENV_OPTIONS"))
						env["MONO_ENV_OPTIONS"] = $" --debug --debugger-agent=transport=dt_socket,server=y,address={host}:{port}";
					else
						env["MONO_ENV_OPTIONS"] = $" --debug --debugger-agent=transport=dt_socket,server=y,address={host}:{port} " + env["MONO_ENV_OPTIONS"];
				}
				else {
					cmdLine.Add("--debug");
					cmdLine.Add($"--debugger-agent=transport=dt_socket,server=y,address={host}:{port}");
				}
			}
			
			if (env.Count == 0) {
				env = null;
			}
			
			// add 'runtimeArgs'
			if (args.runtimeArgs != null) {
				string[] runtimeArguments = args.runtimeArgs.ToObject<string[]>();
				if (runtimeArguments != null && runtimeArguments.Length > 0) {
					cmdLine.AddRange(runtimeArguments);
				}
			}

			// add 'program'
			if (workingDirectory == null) {
				// if no working dir given, we use the direct folder of the executable
				workingDirectory = Path.GetDirectoryName(programPath);
				cmdLine.Add(Path.GetFileName(programPath));
			}
			else {
				// if working dir is given and if the executable is within that folder, we make the program path relative to the working dir
				cmdLine.Add(Utilities.MakeRelativePath(workingDirectory, programPath));
			}

			// add 'args'
			if (args.args != null) {
				string[] arguments = args.args.ToObject<string[]>();
				if (arguments != null && arguments.Length > 0) {
					cmdLine.AddRange(arguments);
				}
			}

			// what console?
			var console = getString(args, "console", null);
			if (console == null) {
				// continue to read the deprecated "externalConsole" attribute
				bool externalConsole = getBool(args, "externalConsole", false);
				if (externalConsole) {
					console = "externalTerminal";
				}
			}

			if (console == "externalTerminal" || console == "integratedTerminal") {

				cmdLine.Insert(0, mono_path);
				var termArgs = new {
					kind = console == "integratedTerminal" ? "integrated" : "external",
					title = "Node Debug Console",
					cwd = workingDirectory,
					args = cmdLine.ToArray(),
					env
				};

				var resp = await SendRequest("runInTerminal", termArgs);
				if (!resp.success) {
					SendErrorResponse(response, 3011, "Cannot launch debug target in terminal ({_error}).", new { _error = resp.message });
					return;
				}

			} else { // internalConsole

				_process = new System.Diagnostics.Process();
				_process.StartInfo.CreateNoWindow = true;
				_process.StartInfo.UseShellExecute = false;
				_process.StartInfo.WorkingDirectory = workingDirectory;
				_process.StartInfo.FileName = mono_path;
				_process.StartInfo.Arguments = Utilities.ConcatArgs(cmdLine.ToArray());

				_stdoutEOF = false;
				_process.StartInfo.RedirectStandardOutput = true;
				_process.OutputDataReceived += (object sender, System.Diagnostics.DataReceivedEventArgs e) => {
					if (e.Data == null) {
						_stdoutEOF = true;
					}
					SendOutput("stdout", e.Data);
				};

				_stderrEOF = false;
				_process.StartInfo.RedirectStandardError = true;
				_process.ErrorDataReceived += (object sender, System.Diagnostics.DataReceivedEventArgs e) => {
					if (e.Data == null) {
						_stderrEOF = true;
					}
					SendOutput("stderr", e.Data);
				};

				_process.EnableRaisingEvents = true;
				_process.Exited += (object sender, EventArgs e) => {
					Terminate("runtime process exited");
				};

				if (env != null) {
					// we cannot set the env vars on the process StartInfo because we need to set StartInfo.UseShellExecute to true at the same time.
					// instead we set the env vars on MonoDebug itself because we know that MonoDebug lives as long as a debug session.
					foreach (var entry in env) {
						System.Environment.SetEnvironmentVariable(entry.Key, entry.Value);
					}
				}

				var cmd = string.Format("{0} {1}", mono_path, _process.StartInfo.Arguments);
				SendOutput("console", cmd);

				try {
					_process.Start();
					_process.BeginOutputReadLine();
					_process.BeginErrorReadLine();
				}
				catch (Exception e) {
					SendErrorResponse(response, 3012, "Can't launch terminal ({reason}).", new { reason = e.Message });
					return;
				}
			}

			if (debug) {
				Connect(IPAddress.Parse(host), port);
			}

			SendResponse(response);

			if (_process == null && !debug) {
				// we cannot track mono runtime process so terminate this session
				Terminate("cannot track mono runtime");
			}
		}