/// <summary>
        /// Create an AppHost with embedded configuration of app binary location
        /// </summary>
        /// <param name="appHostSourceFilePath">The path of Apphost template, which has the place holder</param>
        /// <param name="appHostDestinationFilePath">The destination path for desired location to place, including the file name</param>
        /// <param name="appBinaryFilePath">Full path to app binary or relative path to the result apphost file</param>
        /// <param name="windowsGraphicalUserInterface">Specify whether to set the subsystem to GUI. Only valid for PE apphosts.</param>
        /// <param name="assemblyToCopyResorcesFrom">Path to the intermediate assembly, used for copying resources to PE apphosts.</param>
        public static void CreateAppHost(
            string appHostSourceFilePath,
            string appHostDestinationFilePath,
            string appBinaryFilePath,
            bool windowsGraphicalUserInterface = false,
            string assemblyToCopyResorcesFrom  = null)
        {
            var bytesToWrite = Encoding.UTF8.GetBytes(appBinaryFilePath);

            if (bytesToWrite.Length > 1024)
            {
                throw new AppNameTooLongException(appBinaryFilePath);
            }

            BinaryUtils.CopyFile(appHostSourceFilePath, appHostDestinationFilePath);

            bool appHostIsPEImage = false;

            void RewriteAppHost()
            {
                // Re-write the destination apphost with the proper contents.
                using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath))
                {
                    using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor())
                    {
                        BinaryUtils.SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite);

                        appHostIsPEImage = BinaryUtils.IsPEImage(accessor);

                        if (windowsGraphicalUserInterface)
                        {
                            if (!appHostIsPEImage)
                            {
                                throw new AppHostNotPEFileException();
                            }

                            BinaryUtils.SetWindowsGraphicalUserInterfaceBit(accessor);
                        }
                    }
                }
            }

            void UpdateResources()
            {
                if (assemblyToCopyResorcesFrom != null && appHostIsPEImage)
                {
                    if (ResourceUpdater.IsSupportedOS())
                    {
                        // Copy resources from managed dll to the apphost
                        new ResourceUpdater(appHostDestinationFilePath)
                        .AddResourcesFromPEImage(assemblyToCopyResorcesFrom)
                        .Update();
                    }
                    else
                    {
                        throw new AppHostCustomizationUnsupportedOSException();
                    }
                }
            }

            void RemoveSignatureIfMachO()
            {
                MachOUtils.RemoveSignature(appHostDestinationFilePath);
            }

            void SetLastWriteTime()
            {
                // Memory-mapped write does not updating last write time
                File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow);
            }

            try
            {
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    var       filePermissionOctal = Convert.ToInt32("755", 8); // -rwxr-xr-x
                    const int EINTR           = 4;
                    int       chmodReturnCode = 0;

                    do
                    {
                        chmodReturnCode = chmod(appHostDestinationFilePath, filePermissionOctal);
                    }while (chmodReturnCode == -1 && Marshal.GetLastWin32Error() == EINTR);

                    if (chmodReturnCode == -1)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {appHostDestinationFilePath}.");
                    }
                }

                RetryUtil.RetryOnIOError(RewriteAppHost);

                RetryUtil.RetryOnWin32Error(UpdateResources);

                RetryUtil.RetryOnIOError(RemoveSignatureIfMachO);

                RetryUtil.RetryOnIOError(SetLastWriteTime);
            }
            catch (Exception ex)
            {
                // Delete the destination file so we don't leave an unmodified apphost
                try
                {
                    File.Delete(appHostDestinationFilePath);
                }
                catch (Exception failedToDeleteEx)
                {
                    throw new AggregateException(ex, failedToDeleteEx);
                }

                throw;
            }
        }
        /// <summary>
        /// Create an AppHost with embedded configuration of app binary location
        /// </summary>
        /// <param name="appHostSourceFilePath">The path of Apphost template, which has the place holder</param>
        /// <param name="appHostDestinationFilePath">The destination path for desired location to place, including the file name</param>
        /// <param name="appBinaryFilePath">Full path to app binary or relative path to the result apphost file</param>
        /// <param name="windowsGraphicalUserInterface">Specify whether to set the subsystem to GUI. Only valid for PE apphosts.</param>
        /// <param name="assemblyToCopyResorcesFrom">Path to the intermediate assembly, used for copying resources to PE apphosts.</param>
        public static void CreateAppHost(
            string appHostSourceFilePath,
            string appHostDestinationFilePath,
            string appBinaryFilePath,
            bool windowsGraphicalUserInterface = false,
            string assemblyToCopyResorcesFrom  = null)
        {
            var bytesToWrite = Encoding.UTF8.GetBytes(appBinaryFilePath);

            if (bytesToWrite.Length > 1024)
            {
                throw new AppNameTooLongException(appBinaryFilePath);
            }

            bool appHostIsPEImage = false;

            void RewriteAppHost(MemoryMappedViewAccessor accessor)
            {
                // Re-write the destination apphost with the proper contents.
                BinaryUtils.SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite);

                appHostIsPEImage = PEUtils.IsPEImage(accessor);

                if (windowsGraphicalUserInterface)
                {
                    if (!appHostIsPEImage)
                    {
                        throw new AppHostNotPEFileException();
                    }

                    PEUtils.SetWindowsGraphicalUserInterfaceBit(accessor);
                }
            }

            void UpdateResources()
            {
                if (assemblyToCopyResorcesFrom != null && appHostIsPEImage)
                {
                    if (ResourceUpdater.IsSupportedOS())
                    {
                        // Copy resources from managed dll to the apphost
                        new ResourceUpdater(appHostDestinationFilePath)
                        .AddResourcesFromPEImage(assemblyToCopyResorcesFrom)
                        .Update();
                    }
                    else
                    {
                        throw new AppHostCustomizationUnsupportedOSException();
                    }
                }
            }

            try
            {
                RetryUtil.RetryOnIOError(() =>
                {
                    FileStream appHostSourceStream    = null;
                    MemoryMappedFile memoryMappedFile = null;
                    MemoryMappedViewAccessor memoryMappedViewAccessor = null;
                    try
                    {
                        // Open the source host file.
                        appHostSourceStream      = new FileStream(appHostSourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
                        memoryMappedFile         = MemoryMappedFile.CreateFromFile(appHostSourceStream, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
                        memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.CopyOnWrite);

                        // Get the size of the source app host to ensure that we don't write extra data to the destination.
                        // On Windows, the size of the view accessor is rounded up to the next page boundary.
                        long sourceAppHostLength = appHostSourceStream.Length;

                        // Transform the host file in-memory.
                        RewriteAppHost(memoryMappedViewAccessor);

                        // Save the transformed host.
                        using (FileStream fileStream = new FileStream(appHostDestinationFilePath, FileMode.Create))
                        {
                            BinaryUtils.WriteToStream(memoryMappedViewAccessor, fileStream, sourceAppHostLength);

                            // Remove the signature from MachO hosts.
                            if (!appHostIsPEImage)
                            {
                                MachOUtils.RemoveSignature(fileStream);
                            }
                        }
                    }
                    finally
                    {
                        memoryMappedViewAccessor?.Dispose();
                        memoryMappedFile?.Dispose();
                        appHostSourceStream?.Dispose();
                    }
                });

                RetryUtil.RetryOnWin32Error(UpdateResources);

                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    var       filePermissionOctal = Convert.ToInt32("755", 8); // -rwxr-xr-x
                    const int EINTR           = 4;
                    int       chmodReturnCode = 0;

                    do
                    {
                        chmodReturnCode = chmod(appHostDestinationFilePath, filePermissionOctal);
                    }while (chmodReturnCode == -1 && Marshal.GetLastWin32Error() == EINTR);

                    if (chmodReturnCode == -1)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error(), $"Could not set file permission {filePermissionOctal} for {appHostDestinationFilePath}.");
                    }
                }
            }
            catch (Exception ex)
            {
                // Delete the destination file so we don't leave an unmodified apphost
                try
                {
                    File.Delete(appHostDestinationFilePath);
                }
                catch (Exception failedToDeleteEx)
                {
                    throw new AggregateException(ex, failedToDeleteEx);
                }

                throw;
            }
        }