/// <summary> /// Create a ComHost with an embedded CLSIDMap file to map CLSIDs to .NET Classes. /// </summary> /// <param name="comHostSourceFilePath">The path of Apphost template, which has the place holder</param> /// <param name="comHostDestinationFilePath">The destination path for desired location to place, including the file name</param> /// <param name="clsidmapFilePath">The path to the *.clsidmap file.</param> public static void Create( string comHostSourceFilePath, string comHostDestinationFilePath, string clsidmapFilePath) { var destinationDirectory = new FileInfo(comHostDestinationFilePath).Directory.FullName; if (!Directory.Exists(destinationDirectory)) { Directory.CreateDirectory(destinationDirectory); } // Copy apphost to destination path so it inherits the same attributes/permissions. File.Copy(comHostSourceFilePath, comHostDestinationFilePath, overwrite: true); if (!ResourceUpdater.IsSupportedOS()) { throw new ComHostCustomizationUnsupportedOSException(); } string clsidMap = File.ReadAllText(clsidmapFilePath); byte[] clsidMapBytes = Encoding.UTF8.GetBytes(clsidMap); using (ResourceUpdater updater = new ResourceUpdater(comHostDestinationFilePath)) { updater.AddResource(clsidMapBytes, (IntPtr)ClsidmapResourceType, (IntPtr)ClsidmapResourceId); updater.Update(); } }
/// <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); } CopyAppHost(appHostSourceFilePath, appHostDestinationFilePath); // Re-write the destination apphost with the proper contents. bool appHostIsPEImage = false; 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); } } } 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(); } } // Memory-mapped write does not updating last write time File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow); }
/// <summary> /// Create a ComHost with an embedded CLSIDMap file to map CLSIDs to .NET Classes. /// </summary> /// <param name="comHostSourceFilePath">The path of Apphost template, which has the place holder</param> /// <param name="comHostDestinationFilePath">The destination path for desired location to place, including the file name</param> /// <param name="clsidmapFilePath">The path to the *.clsidmap file.</param> /// <param name="typeLibraries">Resource ids for tlbs and paths to the tlb files to be embedded.</param> public static void Create( string comHostSourceFilePath, string comHostDestinationFilePath, string clsidmapFilePath, IReadOnlyDictionary <int, string> typeLibraries = null) { var destinationDirectory = new FileInfo(comHostDestinationFilePath).Directory.FullName; if (!Directory.Exists(destinationDirectory)) { Directory.CreateDirectory(destinationDirectory); } // Copy apphost to destination path so it inherits the same attributes/permissions. File.Copy(comHostSourceFilePath, comHostDestinationFilePath, overwrite: true); if (!ResourceUpdater.IsSupportedOS()) { throw new ComHostCustomizationUnsupportedOSException(); } string clsidMap = File.ReadAllText(clsidmapFilePath); byte[] clsidMapBytes = Encoding.UTF8.GetBytes(clsidMap); using (ResourceUpdater updater = new ResourceUpdater(comHostDestinationFilePath)) { updater.AddResource(clsidMapBytes, (IntPtr)ClsidmapResourceType, (IntPtr)ClsidmapResourceId); if (typeLibraries is not null) { foreach (var typeLibrary in typeLibraries) { if (!ResourceUpdater.IsIntResource((IntPtr)typeLibrary.Key)) { throw new InvalidTypeLibraryIdException(typeLibrary.Value, typeLibrary.Key); } try { byte[] tlbFileBytes = File.ReadAllBytes(typeLibrary.Value); updater.AddResource(tlbFileBytes, "typelib", (IntPtr)typeLibrary.Key); } catch (FileNotFoundException ex) { throw new TypeLibraryDoesNotExistException(typeLibrary.Value, ex); } catch (HResultException hr) when(hr.Win32HResult == E_INVALIDARG) { throw new InvalidTypeLibraryException(typeLibrary.Value, hr); } } } updater.Update(); } }
public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath) { string appHostSourcePath; if (OperatingSystem.IsWindows()) { appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe"); } else { appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension); } var appHostDestinationFilePath = Path.GetFullPath(shimPath.Value); string entryPointFullPath = Path.GetFullPath(entryPoint.Value); var appBinaryFilePath = Path.GetRelativePath(Path.GetDirectoryName(appHostDestinationFilePath), entryPointFullPath); if (ResourceUpdater.IsSupportedOS()) { var windowsGraphicalUserInterfaceBit = PEUtils.GetWindowsGraphicalUserInterfaceBit(entryPointFullPath); HostWriter.CreateAppHost(appHostSourceFilePath: appHostSourcePath, appHostDestinationFilePath: appHostDestinationFilePath, appBinaryFilePath: appBinaryFilePath, windowsGraphicalUserInterface: (windowsGraphicalUserInterfaceBit == WindowsGUISubsystem), assemblyToCopyResorcesFrom: entryPointFullPath); } else { // by passing null to assemblyToCopyResorcesFrom, it will skip copying resources, // which is only supported on Windows HostWriter.CreateAppHost(appHostSourceFilePath: appHostSourcePath, appHostDestinationFilePath: appHostDestinationFilePath, appBinaryFilePath: appBinaryFilePath, windowsGraphicalUserInterface: false, assemblyToCopyResorcesFrom: null, enableMacOSCodeSign: OperatingSystem.IsMacOS()); } _filePermissionSetter.SetUserExecutionPermission(appHostDestinationFilePath); }
/// <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(); } } } try { RetryUtil.RetryOnIOError(RewriteAppHost); RetryUtil.RetryOnWin32Error(UpdateResources); // Memory-mapped write does not updating last write time RetryUtil.RetryOnIOError(() => File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow)); } 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); } 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; } }