/// <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 = 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(); } } } 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; } }