/// <summary>
        /// Creates a smart detector loader object for the child process, based on the command line arguments received from the parent process.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        /// <param name="tracerForLoader">The tracer to use</param>
        /// <returns>The smart detector loader instance</returns>
        public ISmartDetectorLoader CreateLoaderForChildProcess(string[] args, ITracer tracerForLoader)
        {
            // Get the temp folder name from the arguments and create the loader
            ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args);

            return(new SmartDetectorLoader(arguments.TempFolder, tracerForLoader));
        }
        /// <summary>
        /// Creates a tracer object for the child process, based on the command line arguments received from the parent process.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        /// <returns>The tracer instance</returns>
        public ITracer CreateTracerForChildProcess(string[] args)
        {
            ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args);

            // Get sessionId from the arguments and create tracer
            ITracer tracerForChildProcess = TracerFactory.Create(arguments.SessionId, null, true);

            // Get custom dimensions from the arguments - do not override existing properties
            IReadOnlyDictionary <string, string> existingProperties = tracerForChildProcess.GetCustomProperties();

            foreach (var kv in arguments.CustomTracerProperties)
            {
                if (!existingProperties.ContainsKey(kv.Key))
                {
                    tracerForChildProcess.AddCustomProperty(kv.Key, kv.Value);
                }
            }

            return(tracerForChildProcess);
        }
        /// <summary>
        /// Runs the child process task. This method reads and validates the command line
        /// arguments, starts listening to the parent process (for cancellation/termination),
        /// runs the specified function, and returns the result to the parent process.
        /// Should be called by the child process when it starts.
        /// </summary>
        /// <typeparam name="TInput">The child process input type</typeparam>
        /// <typeparam name="TOutput">The child process output type</typeparam>
        /// <param name="args">The command line arguments</param>
        /// <param name="functionToRun">The function to run</param>
        /// <param name="exceptionToExitCodeConverter">A function used to convert an exception thrown by the process to the exit code to return to the parent.</param>
        /// <param name="waitAfterFlush">Whether to wait after flushing the telemetry, to allow all traces to be sent.</param>
        /// <exception cref="ArgumentException">The wrong number of arguments was provided</exception>
        /// <returns>A <see cref="Task"/>, running the specified function and listening to the parent, returning the exit code to be returned from the process</returns>
        public async Task <int> RunAndListenToParentAsync <TInput, TOutput>(
            string[] args,
            Func <TInput, CancellationToken, Task <TOutput> > functionToRun,
            Func <Exception, int> exceptionToExitCodeConverter,
            bool waitAfterFlush = true)
            where TOutput : class
        {
            ChildProcessArguments arguments = ChildProcessArguments.FromCommandLineArguments(args);
            string pipeParentToChildHandle  = arguments.PipeParentToChildHandle;
            string pipeChildToParentHandle  = arguments.PipeChildToParentHandle;

            try
            {
                using (PipeStream pipe = new AnonymousPipeClientStream(PipeDirection.In, pipeParentToChildHandle))
                {
                    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

                    try
                    {
                        // Read the input
                        var input = await this.ReadFromStream <TInput>(pipe, cancellationTokenSource.Token);

                        // Start listening to parent process - run the listeners in separate tasks
                        // We should not wait on these tasks, since:
                        // * If any of these tasks fail, it will requests cancellation, and it is enough to wait on the main method and
                        //   let it handle cancellation gracefully
                        // * The cancellation listener is blocking, and cannot be canceled (anonymous pipes do not support cancellation).
                        //   Waiting on it will block the current thread.
#pragma warning disable 4014
                        this.ParentLiveListenerAsync(pipe, cancellationTokenSource);
                        this.ParentCancellationListenerAsync(pipe, cancellationTokenSource);
#pragma warning restore 4014

                        // Run the main function
                        TOutput output = await functionToRun(input, cancellationTokenSource.Token);

                        // Write the output back to the parent
                        await this.WriteChildProcessResult(pipeChildToParentHandle, output);

                        // Success - return zero for exit code
                        return(0);
                    }
                    catch (Exception e)
                    {
                        // If the exception is due to cancellation, than return dedicated error codes
                        if (cancellationTokenSource.IsCancellationRequested)
                        {
                            await this.WriteChildProcessResult(pipeChildToParentHandle, "Child process was canceled by the parent");

                            return((int)HttpStatusCode.InternalServerError);
                        }

                        return(await this.HandleChildProcessException(e, pipeChildToParentHandle, exceptionToExitCodeConverter));
                    }
                    finally
                    {
                        // Cancel the token to stop the listener tasks
                        cancellationTokenSource.Cancel();
                    }
                }
            }
            catch (Exception e)
            {
                return(await this.HandleChildProcessException(e, pipeChildToParentHandle, exceptionToExitCodeConverter));
            }
            finally
            {
                this.tracer.Flush();
                if (waitAfterFlush)
                {
                    await Task.Delay(1000 * 5);
                }
            }
        }