diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs
new file mode 100644
index 0000000000..ce76a6de4b
--- /dev/null
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostCustomizationUnsupportedOSException.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.NET.HostModel.AppHost
+{
+ ///
+ /// The application host executable cannot be customized because adding resources requires
+ /// that the build be performed on Windows (excluding Nano Server).
+ ///
+ public class AppHostCustomizationUnsupportedOSException : AppHostUpdateException
+ {
+ }
+}
+
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs
new file mode 100644
index 0000000000..a119a60b56
--- /dev/null
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotCUIException.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.NET.HostModel.AppHost
+{
+ ///
+ /// Unable to use the input file as application host executable because it's not a
+ /// Windows executable for the CUI (Console) subsystem.
+ ///
+ public class AppHostNotCUIException : AppHostUpdateException
+ {
+ }
+}
+
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs
new file mode 100644
index 0000000000..c788e51826
--- /dev/null
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostNotPEFileException.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.NET.HostModel.AppHost
+{
+ ///
+ /// Unable to use the input file as an application host executable
+ /// because it's not a Windows PE file
+ ///
+ public class AppHostNotPEFileException : AppHostUpdateException
+ {
+ }
+}
+
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs
new file mode 100644
index 0000000000..befd9131ac
--- /dev/null
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppHostUpdateException.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.NET.HostModel.AppHost
+{
+ ///
+ /// An instance of this exception is thrown when an AppHost binary update
+ /// fails due to known user errors.
+ ///
+ public class AppHostUpdateException : Exception
+ {
+ }
+}
+
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUpdateException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs
similarity index 59%
rename from src/managed/Microsoft.NET.HostModel/AppHost/BinaryUpdateException.cs
rename to src/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs
index ecab882d27..cde719e960 100644
--- a/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUpdateException.cs
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppNameTooLongException.cs
@@ -7,13 +7,14 @@
namespace Microsoft.NET.HostModel.AppHost
{
///
- /// This exception is thrown when an AppHost binary update fails due to known user errors.
+ /// Given app file name is longer than 1024 bytes
///
- public class BinaryUpdateException : Exception
+ public class AppNameTooLongException : AppHostUpdateException
{
- public BinaryUpdateException(string message) :
- base(message)
+ public string LongName;
+ public AppNameTooLongException(string name)
{
+ LongName = name;
}
}
}
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppUpdatePlaceHolderNotFoundException.cs b/src/managed/Microsoft.NET.HostModel/AppHost/AppUpdatePlaceHolderNotFoundException.cs
new file mode 100644
index 0000000000..a497c2d673
--- /dev/null
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/AppUpdatePlaceHolderNotFoundException.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.NET.HostModel.AppHost
+{
+ ///
+ /// Unable to use input file as a valid application host executable, as it does not contain
+ /// the expected placeholder byte sequence.
+ ///
+ public class AppUpdatePlaceHolderNotFoundException : AppHostUpdateException
+ {
+ public byte[] MissingPattern;
+
+ public AppUpdatePlaceHolderNotFoundException(byte[] pattern)
+ {
+ MissingPattern = pattern;
+ }
+ }
+}
+
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs b/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs
index 4e7cf100d4..415e73d71e 100644
--- a/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs
@@ -11,7 +11,7 @@ namespace Microsoft.NET.HostModel.AppHost
{
public static class BinaryUtils
{
- internal static unsafe bool SearchAndReplace(
+ internal static unsafe void SearchAndReplace(
MemoryMappedViewAccessor accessor,
byte[] searchPattern,
byte[] patternToReplace)
@@ -26,7 +26,7 @@ internal static unsafe bool SearchAndReplace(
int position = KMPSearch(searchPattern, bytes, accessor.Capacity);
if (position < 0)
{
- return false;
+ throw new AppUpdatePlaceHolderNotFoundException(searchPattern);
}
accessor.WriteArray(
@@ -44,8 +44,6 @@ internal static unsafe bool SearchAndReplace(
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
}
}
-
- return true;
}
private static unsafe void Pad0(byte[] searchPattern, byte[] patternToReplace, byte* bytes, int offset)
@@ -59,31 +57,14 @@ private static unsafe void Pad0(byte[] searchPattern, byte[] patternToReplace, b
}
}
- public static unsafe void SearchAndReplace(MemoryMappedFile mappedFile, byte[] searchPattern, byte[] patternToReplace)
- {
- using (var accessor = mappedFile.CreateViewAccessor())
- {
- if (!SearchAndReplace(accessor, searchPattern, patternToReplace))
- {
- throw new BinaryUpdateException($"SearchPattern {searchPattern} not found.");
- }
- }
- }
-
public static unsafe void SearchAndReplace(string filePath, byte[] searchPattern, byte[] patternToReplace)
{
using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
{
- SearchAndReplace(mappedFile, searchPattern, patternToReplace);
- }
- }
-
- public static unsafe int SearchInFile(MemoryMappedFile mappedFile, byte[] searchPattern)
- {
- using (var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
- {
- var safeBuffer = accessor.SafeMemoryMappedViewHandle;
- return KMPSearch(searchPattern, (byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength);
+ using (var accessor = mappedFile.CreateViewAccessor())
+ {
+ SearchAndReplace(accessor, searchPattern, patternToReplace);
+ }
}
}
@@ -91,7 +72,11 @@ public static unsafe int SearchInFile(string filePath, byte[] searchPattern)
{
using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
{
- return SearchInFile(mappedFile, searchPattern);
+ using (var accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read))
+ {
+ var safeBuffer = accessor.SafeMemoryMappedViewHandle;
+ return KMPSearch(searchPattern, (byte*)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength);
+ }
}
}
@@ -226,10 +211,7 @@ internal static unsafe bool IsPEImage(MemoryMappedViewAccessor accessor)
/// This method will attempt to set the subsystem to GUI. The apphost file should be a windows PE file.
///
/// The memory accessor which has the apphost file opened.
- /// The path to the source apphost.
- internal static unsafe void SetWindowsGraphicalUserInterfaceBit(
- MemoryMappedViewAccessor accessor,
- string appHostSourcePath)
+ internal static unsafe void SetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
{
byte* pointer = null;
@@ -243,7 +225,7 @@ internal static unsafe void SetWindowsGraphicalUserInterfaceBit(
if (accessor.Capacity < peHeaderOffset + SubsystemOffset + sizeof(UInt16))
{
- throw new BinaryUpdateException($" Unable to use '{appHostSourcePath}' as application host executable because it's not a Windows PE file");
+ throw new AppHostNotPEFileException();
}
UInt16* subsystem = ((UInt16*)(bytes + peHeaderOffset + SubsystemOffset));
@@ -252,7 +234,7 @@ internal static unsafe void SetWindowsGraphicalUserInterfaceBit(
// The subsystem of the prebuilt apphost should be set to CUI
if (subsystem[0] != WindowsCUISubsystem)
{
- throw new BinaryUpdateException("Unable to use '{appHostSourcePath}' as application host executable because it's not a Windows executable for the CUI (Console) subsystem");
+ throw new AppHostNotCUIException();
}
// Set the subsystem to GUI
@@ -266,5 +248,61 @@ internal static unsafe void SetWindowsGraphicalUserInterfaceBit(
}
}
}
+
+ public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath)
+ {
+ using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
+ {
+ using (var accessor = mappedFile.CreateViewAccessor())
+ {
+ SetWindowsGraphicalUserInterfaceBit(accessor);
+ }
+ }
+ }
+
+ ///
+ /// This method will return the subsystem CUI/GUI value. The apphost file should be a windows PE file.
+ ///
+ /// The memory accessor which has the apphost file opened.
+ internal static unsafe UInt16 GetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor)
+ {
+ byte* pointer = null;
+
+ try
+ {
+ accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer);
+ byte* bytes = pointer + accessor.PointerOffset;
+
+ // https://en.wikipedia.org/wiki/Portable_Executable
+ UInt32 peHeaderOffset = ((UInt32*)(bytes + PEHeaderPointerOffset))[0];
+
+ if (accessor.Capacity < peHeaderOffset + SubsystemOffset + sizeof(UInt16))
+ {
+ throw new AppHostNotPEFileException();
+ }
+
+ UInt16* subsystem = ((UInt16*)(bytes + peHeaderOffset + SubsystemOffset));
+
+ return subsystem[0];
+ }
+ finally
+ {
+ if (pointer != null)
+ {
+ accessor.SafeMemoryMappedViewHandle.ReleasePointer();
+ }
+ }
+ }
+
+ public static unsafe UInt16 GetWindowsGraphicalUserInterfaceBit(string filePath)
+ {
+ using (var mappedFile = MemoryMappedFile.CreateFromFile(filePath))
+ {
+ using (var accessor = mappedFile.CreateViewAccessor())
+ {
+ return GetWindowsGraphicalUserInterfaceBit(accessor);
+ }
+ }
+ }
}
}
diff --git a/src/managed/Microsoft.NET.HostModel/AppHost/AppUpdater.cs b/src/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
similarity index 63%
rename from src/managed/Microsoft.NET.HostModel/AppHost/AppUpdater.cs
rename to src/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
index a8689ba0a0..ff5ec92073 100644
--- a/src/managed/Microsoft.NET.HostModel/AppHost/AppUpdater.cs
+++ b/src/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs
@@ -12,7 +12,7 @@ namespace Microsoft.NET.HostModel.AppHost
/// Embeds the App Name into the AppHost.exe
/// If an apphost is a single-file bundle, updates the location of the bundle headers.
///
- public static class AppUpdater
+ public static class HostWriter
{
///
/// hash value embedded in default apphost executable in a place where the path to the app binary should be stored.
@@ -20,7 +20,7 @@ public static class AppUpdater
private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);
- private const string BundleHeaderPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f3";
+ private const string BundleHeaderPlaceholder = "db2a6C16fec7fbebe3539d534a3471e95ea6e85c718cba293996a8ac85F90427";
private readonly static byte[] BundleHeaderPlaceholderSearchValue = Encoding.UTF8.GetBytes(BundleHeaderPlaceholder);
///
@@ -30,28 +30,21 @@ public static class AppUpdater
/// The destination path for desired location to place, including the file name
/// Full path to app binary or relative path to the result apphost file
/// Specify whether to set the subsystem to GUI. Only valid for PE apphosts.
- /// Path to the intermediate assembly, used for copying resources to PE apphosts.
- public static void UpdateAppPath(
+ /// Path to the intermediate assembly, used for copying resources to PE apphosts.
+ public static void CreateAppHost(
string appHostSourceFilePath,
string appHostDestinationFilePath,
string appBinaryFilePath,
bool windowsGraphicalUserInterface = false,
- string intermediateAssembly = null)
+ string assemblyToCopyResorcesFrom = null)
{
var bytesToWrite = Encoding.UTF8.GetBytes(appBinaryFilePath);
if (bytesToWrite.Length > 1024)
{
- throw new BinaryUpdateException($"Given file name {appBinaryFilePath} is longer than 1024 bytes");
+ throw new AppNameTooLongException(appBinaryFilePath);
}
- var destinationDirectory = new FileInfo(appHostDestinationFilePath).Directory.FullName;
- if (!Directory.Exists(destinationDirectory))
- {
- Directory.CreateDirectory(destinationDirectory);
- }
-
- // Copy apphost to destination path so it inherits the same attributes/permissions.
- File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwrite: true);
+ CopyAppHost(appHostSourceFilePath, appHostDestinationFilePath);
// Re-write the destination apphost with the proper contents.
bool appHostIsPEImage = false;
@@ -59,10 +52,7 @@ public static void UpdateAppPath(
{
using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor())
{
- if(!BinaryUtils.SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite))
- {
- throw new BinaryUpdateException($"Unable to use '{appHostSourceFilePath}' as application host executable as it does not contain the expected placeholder byte sequence '{AppBinaryPathPlaceholder}' that would mark where the application name would be written");
- }
+ BinaryUtils.SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite);
appHostIsPEImage = BinaryUtils.IsPEImage(accessor);
@@ -70,23 +60,27 @@ public static void UpdateAppPath(
{
if (!appHostIsPEImage)
{
- throw new BinaryUpdateException($"Unable to use '{appHostSourceFilePath}' as application host executable because it's not a Windows executable for the CUI (Console) subsystem");
+ throw new AppHostNotPEFileException();
}
- BinaryUtils.SetWindowsGraphicalUserInterfaceBit(accessor, appHostSourceFilePath);
+ BinaryUtils.SetWindowsGraphicalUserInterfaceBit(accessor);
}
}
}
- if (intermediateAssembly != null && appHostIsPEImage)
+ if (assemblyToCopyResorcesFrom != null && appHostIsPEImage)
{
if (ResourceUpdater.IsSupportedOS())
{
// Copy resources from managed dll to the apphost
new ResourceUpdater(appHostDestinationFilePath)
- .AddResourcesFromPEImage(intermediateAssembly)
+ .AddResourcesFromPEImage(assemblyToCopyResorcesFrom)
.Update();
}
+ else
+ {
+ throw new AppHostCustomizationUnsupportedOSException();
+ }
}
// Memory-mapped write does not updating last write time
@@ -94,15 +88,28 @@ public static void UpdateAppPath(
}
///
- /// Create an AppHost with embedded configuration of app binary location
+ /// Create an AppHost configured to be a single-file bundle.
///
/// The path of Apphost template, which has the place holder
/// The destination path for desired location to place, including the file name
/// The offset to the location of bundle header
- public static void UpdateBundleHeader(
+ public static void CreateBundle(
string appHostSourceFilePath,
string appHostDestinationFilePath,
long bundleHeaderOffset)
+ {
+ CopyAppHost(appHostSourceFilePath, appHostDestinationFilePath);
+
+ // Re-write the destination apphost with the proper contents.
+ BinaryUtils.SearchAndReplace(appHostDestinationFilePath, BundleHeaderPlaceholderSearchValue, BitConverter.GetBytes(bundleHeaderOffset));
+
+ // Memory-mapped write does not updating last write time
+ File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow);
+ }
+
+ private static void CopyAppHost(
+ string appHostSourceFilePath,
+ string appHostDestinationFilePath)
{
var destinationDirectory = new FileInfo(appHostDestinationFilePath).Directory.FullName;
if (!Directory.Exists(destinationDirectory))
@@ -112,22 +119,6 @@ public static void UpdateBundleHeader(
// Copy apphost to destination path so it inherits the same attributes/permissions.
File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwrite: true);
-
- // Re-write the destination apphost with the proper contents.
- using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath))
- {
- using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor())
- {
- if (!BinaryUtils.SearchAndReplace(accessor, BundleHeaderPlaceholderSearchValue, BitConverter.GetBytes(bundleHeaderOffset)))
- {
- throw new BinaryUpdateException($"Unable to use '{appHostSourceFilePath}' as application host executable as it does not contain the expected placeholder byte sequence '{BundleHeaderPlaceholder}' that would mark where the bundle header offset would be written");
- }
- }
- }
-
- // Memory-mapped write does not updating last write time
- File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow);
}
-
}
}