From dcacdc04bae4a5f1cbca0976ea843d0911d13049 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 14 Sep 2018 12:49:46 -0700 Subject: [PATCH 01/28] Add apphost customization --- .../GivenAnAppHost.cs | 4 +- .../Microsoft.NET.Build.Tasks/AppHost.cs | 5 + .../ResourceUpdater.cs | 277 ++++++++++++++++++ .../GivenThatWeWantToCustomizeTheApphost.cs | 56 ++++ 4 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs create mode 100644 src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs index cbe2f9476f21..1b0801bd1ef8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs @@ -54,7 +54,7 @@ public void ItFailsToEmbedAppBinaryIfHashIsWrong() { string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content => { - // Corrupt the has value + // Corrupt the hash value content[WindowsFileHeader.Length + 1]++; }); string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); @@ -185,7 +185,7 @@ public void ItFailsToSetGUISubsystemWithWrongDefault() private string PrepareAppHostMockFile(TestDirectory testDirectory, Action customize = null) { // For now we're testing the AppHost on Windows PE files only. - // The only customization which we do on non-Windows files is the embeding + // The only customization which we do on non-Windows files is the embedding // of the binary path, which works the same regardless of the file format. int size = WindowsFileHeader.Length + AppBinaryPathPlaceholderSearchValue.Length; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index d7f38e906a1f..e50887c3afe4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -52,6 +52,11 @@ public static void Create( // Copy AppHostSourcePath to ModifiedAppHostPath so it inherits the same attributes\permissions. File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwriteExisting); + // Copy resources from managed dll to the apphost + new ResourceUpdater(appHostDestinationFilePath) + .AddResourcesFrom(appBinaryFilePath) + .Update(); + // Re-write ModifiedAppHostPath with the proper contents. using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath)) { diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs new file mode 100644 index 000000000000..83453fc103e3 --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -0,0 +1,277 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Microsoft.NET.Build.Tasks +{ + /// + /// Provides methods for modifying the embedded native resources + /// in a PE image. + /// + public class ResourceUpdater + { + // + // Native methods for updating resources + // + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern IntPtr BeginUpdateResource(string pFileName, + [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources); + + // Update a resource with data from an IntPtr + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UpdateResource(IntPtr hUpdate, + IntPtr lpType, + IntPtr lpName, + ushort wLanguage, + IntPtr lpData, + uint cbData); + + // Update a resource with data from a managed byte[] + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool UpdateResource(IntPtr hUpdate, + IntPtr lpType, + IntPtr lpName, + ushort wLanguage, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, + uint cbData); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EndUpdateResource(IntPtr hUpdate, + bool fDiscard); + + // + // Native methods used to read resources from a PE file + // + + // Loading and freeing PE files + + // TODO: use safe handle implementation? + private enum LoadLibraryFlags : uint + { + None = 0, + LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, + LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 + } + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern IntPtr LoadLibraryEx(string lpFileName, + IntPtr hReservedNull, + LoadLibraryFlags dwFlags); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool FreeLibrary(IntPtr hModule); + + // Enumerating resources + + private delegate bool EnumResTypeProc(IntPtr hModule, + ushort lpType, + IntPtr lParam); + + private delegate bool EnumResNameProc(IntPtr hModule, + ushort lpType, + ushort lpName, + IntPtr lParam); + + private delegate bool EnumResLangProc(IntPtr hModule, + ushort lpType, + ushort lpName, + ushort wLang, + IntPtr lParam); + + [DllImport("kernel32.dll",SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumResourceTypes(IntPtr hModule, + EnumResTypeProc lpEnumFunc, + IntPtr lParam); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumResourceNames(IntPtr hModule, + ushort lpType, + EnumResNameProc lpEnumFunc, + IntPtr lParam); + + [DllImport("kernel32.dll", SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumResourceLanguages(IntPtr hModule, + ushort lpType, + ushort lpName, + EnumResLangProc lpEnumFunc, + IntPtr lParam); + + // Querying and loading resources + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern IntPtr FindResourceEx(IntPtr hModule, + IntPtr lpType, + IntPtr lpName, + ushort wLanguage); + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern IntPtr LoadResource(IntPtr hModule, + IntPtr hResInfo); + + [DllImport("kernel32.dll")] // does not call SetLastError + private static extern IntPtr LockResource(IntPtr hResData); + + [DllImport("kernel32.dll", SetLastError=true)] + private static extern uint SizeofResource(IntPtr hModule, + IntPtr hResInfo); + + public const ushort LangID_LangNeutral_SublangNeutral = 0; + + /// + /// Holds the native handle for the resource update. + /// + private IntPtr hUpdate = IntPtr.Zero; + + /// + /// Create a resource updater for the given PE file. This will + /// acquire a native resource update handle for the file, + /// preparing it for updates. Resources can be added to this + /// updater, which will queue them for update. The target PE + /// file will not be modified until Update() is called. + /// + public ResourceUpdater(string peFile) + { + hUpdate = BeginUpdateResource(peFile, false); + if (hUpdate == IntPtr.Zero) + { + ThrowExceptionForLastWin32Error(); + } + } + + /// + /// Add all resources from a source PE file. This will not + /// modify the target until Update() is called. + /// + public ResourceUpdater AddResourcesFrom(string peFile) + { + // TODO: check that they're both valid PE files + + // TODO: produce something if we're not on windows + + // TODO: if it has no resources? maybe do a lazy beginupdateresource instead. + + // Using both flags lets the OS loader decide how to load + // it most efficiently. Either mode will prevent other + // processes from modifying the module while it is loaded. + IntPtr hModule = LoadLibraryEx(peFile, IntPtr.Zero, + LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LoadLibraryFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE); + if (hModule == IntPtr.Zero) + { + ThrowExceptionForLastWin32Error(); + } + + var enumTypesCallback = new EnumResTypeProc(EnumTypesCallback); + if (!EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) + { + ThrowExceptionForLastWin32Error(); + } + + if (!FreeLibrary(hModule)) + { + ThrowExceptionForLastWin32Error(); + } + + return this; + } + + /// + /// Add a language-neutral resource from a byte[] with a + /// particular type and name. This will not modify the target + /// until Update() is called. + /// + public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) + { + //if (!UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, data.Length)) + //{ + //ThrowExceptionForLastWin32Error(); + //} + + return this; + } + + /// + /// Write the pending resource updates to the target PE file. + /// + public void Update() + { + if (!EndUpdateResource(hUpdate, false)) + { + ThrowExceptionForLastWin32Error(); + } + } + + + private bool EnumTypesCallback(IntPtr hModule, ushort lpType, IntPtr lParam) + { + // Console.WriteLine("resource type " + lpType); + // TODO: what if the lpType is a string identifier? + var enumNamesCallback = new EnumResNameProc(EnumNamesCallback); + if (!EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) + { + ThrowExceptionForLastWin32Error(); + } + + return true; + } + + private bool EnumNamesCallback(IntPtr hModule, ushort lpType, ushort lpName, IntPtr lParam) + { + // Console.WriteLine("resource name " + lpName); + // TODO: what if name is a string rather than an int? + var enumLanguagesCallback = new EnumResLangProc(EnumLanguagesCallback); + if (!EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) + { + ThrowExceptionForLastWin32Error(); + } + + return true; + } + + private bool EnumLanguagesCallback(IntPtr hModule, ushort lpType, ushort lpName, ushort wLang, IntPtr lParam) + { + IntPtr hResource = FindResourceEx(hModule, (IntPtr)lpType, (IntPtr)lpName, wLang); + if (hResource == IntPtr.Zero) + { + ThrowExceptionForLastWin32Error(); + } + + IntPtr hResourceLoaded = LoadResource(hModule, hResource); + if (hResourceLoaded == IntPtr.Zero) + { + ThrowExceptionForLastWin32Error(); + } + + IntPtr lpResourceData = LockResource(hResourceLoaded); + if (lpResourceData == IntPtr.Zero) + { + // TODO: better exception + throw new Exception("failed to lock resource"); + } + + if (!UpdateResource(hUpdate, (IntPtr)lpType, (IntPtr)lpName, wLang, lpResourceData, SizeofResource(hModule, hResource))) + { + ThrowExceptionForLastWin32Error(); + } + // TODO: cast ushort to intptr? + + return true; + } + + private static void ThrowExceptionForLastWin32Error() + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + } + } +} diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs new file mode 100644 index 000000000000..c123ccba7640 --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.IO; +using FluentAssertions; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Microsoft.NET.TestFramework.ProjectConstruction; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeWantToCustomizeTheApphost : SdkTest + { + public GivenThatWeWantToCustomizeTheApphost(ITestOutputHelper log) : base(log) + { + } + + [Fact] + public void It_contains_resources_from_the_managed_dll() + { + var targetFramework = "netcoreapp2.0"; + var runtimeIdentifier = EnvironmentInfo.GetCompatibleRid(targetFramework); + + var version = "5.6.7.8"; + var testProject = new TestProject() + { + Name = "ResourceTest", + TargetFrameworks = targetFramework, + RuntimeIdentifier = runtimeIdentifier, + IsSdkProject = true, + IsExe = true, + }; + testProject.AdditionalProperties.Add("AssemblyVersion", version); + + var testAsset = _testAssetsManager.CreateTestProject(testProject) + .Restore(Log, testProject.Name); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + + buildCommand.Execute() + .Should() + .Pass(); + + var outputDirectory = buildCommand.GetOutputDirectory(targetFramework, runtimeIdentifier: runtimeIdentifier); + outputDirectory.Should().HaveFiles(new[] { testProject.Name + ".exe" }); + + string apphostPath = Path.Combine(outputDirectory.FullName, testProject.Name + ".exe"); + var apphostVersion = FileVersionInfo.GetVersionInfo(apphostPath); + apphostVersion.Should().Be(version); + } + } +} From 25bb0d62f5a3ba28cb688eaca33a9193408896f8 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Fri, 14 Sep 2018 14:39:05 -0700 Subject: [PATCH 02/28] Pass intermediate assembly to apphost --- src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs | 3 ++- src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs | 4 ++++ .../targets/Microsoft.NET.Sdk.targets | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index e50887c3afe4..4459ea8d3a17 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -31,6 +31,7 @@ public static void Create( string appHostSourceFilePath, string appHostDestinationFilePath, string appBinaryFilePath, + string intermediateAssembly, bool overwriteExisting = false, AppHostOptions options = null) { @@ -54,7 +55,7 @@ public static void Create( // Copy resources from managed dll to the apphost new ResourceUpdater(appHostDestinationFilePath) - .AddResourcesFrom(appBinaryFilePath) + .AddResourcesFrom(intermediateAssembly) .Update(); // Re-write ModifiedAppHostPath with the proper contents. diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index 5b835117f410..7c11000509aa 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -21,6 +21,9 @@ public class CreateAppHost : TaskBase [Required] public string AppBinaryName { get; set; } + [Required] + public string IntermediateAssembly { get; set; } + public bool WindowsGraphicalUserInterface { get; set; } [Output] @@ -39,6 +42,7 @@ protected override void ExecuteCore() AppHostSourcePath, ModifiedAppHostPath, AppBinaryName, + IntermediateAssembly, options: new AppHostOptions() { WindowsGraphicalUserInterface = WindowsGraphicalUserInterface diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 59385c83d4a1..0679a49acc08 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -309,6 +309,7 @@ Copyright (c) .NET Foundation. All rights reserved. From 0bf8fa50f2ad507d85a104b3769c5ef808f45bb5 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 18 Sep 2018 14:04:48 -0700 Subject: [PATCH 03/28] Change task order to make IntermediateAssembly available to app host --- .../targets/Microsoft.NET.Sdk.targets | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 0679a49acc08..a882833be07f 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -276,9 +276,8 @@ Copyright (c) .NET Foundation. All rights reserved. --> + + + + + <_NoneWithTargetPath Include="@(_AllNETCoreCopyLocalItemsWithTargetPath)" /> + From 888026b119c71f46bc0c02c804baf158b31186f5 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 18 Sep 2018 14:09:00 -0700 Subject: [PATCH 04/28] Make intermediate assembly optional and fix version check test --- src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs | 4 ++-- src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs | 6 +++--- .../GivenThatWeWantToCustomizeTheApphost.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index 4459ea8d3a17..bef4ea480550 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -31,9 +31,9 @@ public static void Create( string appHostSourceFilePath, string appHostDestinationFilePath, string appBinaryFilePath, - string intermediateAssembly, bool overwriteExisting = false, - AppHostOptions options = null) + AppHostOptions options = null, + string intermediateAssembly = null, { var hostExtension = Path.GetExtension(appHostSourceFilePath); var appbaseName = Path.GetFileNameWithoutExtension(appBinaryFilePath); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index 7c11000509aa..e09d46148191 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -21,7 +21,6 @@ public class CreateAppHost : TaskBase [Required] public string AppBinaryName { get; set; } - [Required] public string IntermediateAssembly { get; set; } public bool WindowsGraphicalUserInterface { get; set; } @@ -42,11 +41,12 @@ protected override void ExecuteCore() AppHostSourcePath, ModifiedAppHostPath, AppBinaryName, - IntermediateAssembly, options: new AppHostOptions() { WindowsGraphicalUserInterface = WindowsGraphicalUserInterface - }); + }, + intermediateAssembly: IntermediateAssembly, + log: Log); } } } diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs index c123ccba7640..28497df42095 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs @@ -49,7 +49,7 @@ public void It_contains_resources_from_the_managed_dll() outputDirectory.Should().HaveFiles(new[] { testProject.Name + ".exe" }); string apphostPath = Path.Combine(outputDirectory.FullName, testProject.Name + ".exe"); - var apphostVersion = FileVersionInfo.GetVersionInfo(apphostPath); + var apphostVersion = FileVersionInfo.GetVersionInfo(apphostPath).FileVersion; apphostVersion.Should().Be(version); } } From 2e776e9d62c9a21786b7d8e82fa9d8b2735dac1c Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 18 Sep 2018 14:12:47 -0700 Subject: [PATCH 05/28] Factor out PE image check --- .../Microsoft.NET.Build.Tasks/AppHost.cs | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index bef4ea480550..aa319c3d4c8e 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -59,16 +59,24 @@ public static void Create( .Update(); // Re-write ModifiedAppHostPath with the proper contents. + bool appHostIsPEImage = false; using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath)) { using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor()) { SearchAndReplace(accessor, AppBinaryPathPlaceholderSearchValue, bytesToWrite, appHostSourceFilePath); + appHostIsPEImage = IsPEImage(accessor); + if (options != null) { if (options.WindowsGraphicalUserInterface) { + if (!appHostIsPEImage) + { + throw new BuildErrorException(Strings.AppHostNotWindows, appHostSourceFilePath); + } + SetWindowsGraphicalUserInterfaceBit(accessor, appHostSourceFilePath); } } @@ -223,14 +231,11 @@ private static unsafe void Pad0(byte[] searchPattern, byte[] patternToReplace, b private const UInt16 WindowsCUISubsystem = 0x3; /// - /// If the apphost file is a windows PE file (checked by looking at the first few bytes) - /// this method will set its subsystem to GUI. + /// Check whether the apphost file is a windows PE image by looking at the first few bytes. /// /// The memory accessor which has the apphost file opened. - /// The path to the source apphost. - private static unsafe void SetWindowsGraphicalUserInterfaceBit( - MemoryMappedViewAccessor accessor, - string appHostSourcePath) + /// true if the accessor represents a PE image, false otherwise. + private static unsafe bool IsPEImage(MemoryMappedViewAccessor accessor) { byte* pointer = null; @@ -243,8 +248,34 @@ private static unsafe void SetWindowsGraphicalUserInterfaceBit( // Validate that we're looking at Windows PE file if (((UInt16*)bytes)[0] != PEFileSignature || accessor.Capacity < PEHeaderPointerOffset + sizeof(UInt32)) { - throw new BuildErrorException(Strings.AppHostNotWindows, appHostSourcePath); + return false; + } + return true; + } + finally + { + if (pointer != null) + { + accessor.SafeMemoryMappedViewHandle.ReleasePointer(); } + } + } + + /// + /// This method will attempt to set the subsistem 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. + private static unsafe void SetWindowsGraphicalUserInterfaceBit( + MemoryMappedViewAccessor accessor, + string appHostSourcePath) + { + byte* pointer = null; + + try + { + accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); + byte* bytes = pointer + accessor.PointerOffset; UInt32 peHeaderOffset = ((UInt32*)(bytes + PEHeaderPointerOffset))[0]; From 6e404544b78f558a73d0c898cbf0ea0d67a92d83 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 18 Sep 2018 14:14:40 -0700 Subject: [PATCH 06/28] Check PE image and host to provide appropriate warning This will emit a warning if host customization is requested but we are building on non-Windows (where the required APIs don't exist). --- src/Tasks/Common/Resources/Strings.resx | 6 ++++- src/Tasks/Common/Resources/xlf/Strings.cs.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.de.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.es.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.fr.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.it.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.ja.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.ko.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.pl.xlf | 7 +++++- .../Common/Resources/xlf/Strings.pt-BR.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.ru.xlf | 7 +++++- src/Tasks/Common/Resources/xlf/Strings.tr.xlf | 7 +++++- .../Common/Resources/xlf/Strings.zh-Hans.xlf | 7 +++++- .../Common/Resources/xlf/Strings.zh-Hant.xlf | 7 +++++- .../Microsoft.NET.Build.Tasks/AppHost.cs | 24 ++++++++++++++----- .../AppHostOptions.cs | 2 +- 16 files changed, 102 insertions(+), 21 deletions(-) diff --git a/src/Tasks/Common/Resources/Strings.resx b/src/Tasks/Common/Resources/Strings.resx index f851db935674..31ee2ecf32e9 100644 --- a/src/Tasks/Common/Resources/Strings.resx +++ b/src/Tasks/Common/Resources/Strings.resx @@ -413,4 +413,8 @@ The following are names of parameters or literal values and should not be transl NETSDK1073: The FrameworkReference '{0}' was not recognized {StrBegin="NETSDK1073: "} - \ No newline at end of file + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + + diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf index c53f7bc2ddf5..1e3e20d79bb6 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.de.xlf b/src/Tasks/Common/Resources/xlf/Strings.de.xlf index f60c3a740ff9..fc3869cfb61f 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.de.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf index 6b7b3091e93d..7fedaf5bc1b0 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf index 976ce231e9d6..8337d5799d14 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf index 043627726d66..cfe3db882af2 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf index 1b99a08d5bb2..0e70ac739b55 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf index 255e54db985b..6a7dea93039a 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf index 1da76bd4beb7..07d0bf8e9106 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf index 340b005013cc..996093d8daee 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf index e5aba5589f8c..6b9809e8afe7 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf index b09017e30fc5..a8cbf00ef7fa 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1070: The application configuration file must have root configuration element. NETSDK1070: The application configuration file must have root configuration element. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf index 2f5e5a786194..7f9a5931fe51 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf index e1259776370a..1805fcd172df 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -2,6 +2,11 @@ + + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + {StrBegin="NETSDK1074: "} + NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. NETSDK1071: Unable to use '{0}' as application host executable because it's not a Windows PE file. @@ -373,4 +378,4 @@ The following are names of parameters or literal values and should not be transl - \ No newline at end of file + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index aa319c3d4c8e..b9bddb5af7e9 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -5,13 +5,14 @@ using System.IO; using System.IO.MemoryMappedFiles; using System.Text; +using System.Runtime.InteropServices; namespace Microsoft.NET.Build.Tasks { /// /// Embeds the App Name into the AppHost.exe /// - public static class AppHost + internal static class AppHost { /// /// hash value embedded in default apphost executable in a place where the path to the app binary should be stored. @@ -34,6 +35,7 @@ public static void Create( bool overwriteExisting = false, AppHostOptions options = null, string intermediateAssembly = null, + Logger log = null) { var hostExtension = Path.GetExtension(appHostSourceFilePath); var appbaseName = Path.GetFileNameWithoutExtension(appBinaryFilePath); @@ -53,11 +55,6 @@ public static void Create( // Copy AppHostSourcePath to ModifiedAppHostPath so it inherits the same attributes\permissions. File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwriteExisting); - // Copy resources from managed dll to the apphost - new ResourceUpdater(appHostDestinationFilePath) - .AddResourcesFrom(intermediateAssembly) - .Update(); - // Re-write ModifiedAppHostPath with the proper contents. bool appHostIsPEImage = false; using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostDestinationFilePath)) @@ -83,6 +80,21 @@ public static void Create( } } + if (intermediateAssembly != null && appHostIsPEImage) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Copy resources from managed dll to the apphost + new ResourceUpdater(appHostDestinationFilePath) + .AddResourcesFrom(intermediateAssembly) + .Update(); + } + else if (log != null) + { + log.LogWarning(Strings.AppHostCustomizationRequiresWindowsHostWarning); + } + } + // Memory-mapped write does not updating last write time File.SetLastWriteTimeUtc(appHostDestinationFilePath, DateTime.UtcNow); } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs index 18086734eebc..dcef3a704798 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs @@ -3,7 +3,7 @@ /// /// Options to customize the apphost. /// - public class AppHostOptions + internal class AppHostOptions { /// /// If this is set to true and the apphost is a Windows PE executable, it will have its subsystem set to GUI. From f61feae8e2eb7b572978ae161a4f135da0939e16 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 19 Sep 2018 10:47:54 -0700 Subject: [PATCH 07/28] Clean up resource updater a bit --- .../ResourceUpdater.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 83453fc103e3..97942b742c6c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -10,7 +10,8 @@ namespace Microsoft.NET.Build.Tasks { /// /// Provides methods for modifying the embedded native resources - /// in a PE image. + /// in a PE image. It currently only works on Windows, because it + /// requires various kernel32 APIs. /// public class ResourceUpdater { @@ -151,17 +152,13 @@ public ResourceUpdater(string peFile) } /// - /// Add all resources from a source PE file. This will not - /// modify the target until Update() is called. + /// Add all resources from a source PE file. It is assumed + /// that the input is a valid PE file. If it is not, an + /// exception will be thrown. This will not modify the target + /// until Update() is called. /// public ResourceUpdater AddResourcesFrom(string peFile) { - // TODO: check that they're both valid PE files - - // TODO: produce something if we're not on windows - - // TODO: if it has no resources? maybe do a lazy beginupdateresource instead. - // Using both flags lets the OS loader decide how to load // it most efficiently. Either mode will prevent other // processes from modifying the module while it is loaded. @@ -215,7 +212,6 @@ public void Update() private bool EnumTypesCallback(IntPtr hModule, ushort lpType, IntPtr lParam) { - // Console.WriteLine("resource type " + lpType); // TODO: what if the lpType is a string identifier? var enumNamesCallback = new EnumResNameProc(EnumNamesCallback); if (!EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) @@ -228,7 +224,6 @@ private bool EnumTypesCallback(IntPtr hModule, ushort lpType, IntPtr lParam) private bool EnumNamesCallback(IntPtr hModule, ushort lpType, ushort lpName, IntPtr lParam) { - // Console.WriteLine("resource name " + lpName); // TODO: what if name is a string rather than an int? var enumLanguagesCallback = new EnumResLangProc(EnumLanguagesCallback); if (!EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) From 7a342b5fc1c7fadee6d91976d7ad18f733067af8 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 20 Sep 2018 14:04:18 -0700 Subject: [PATCH 08/28] Clean up ResourceUpdater Add proper error handling and documentation --- .../Microsoft.NET.Build.Tasks/AppHost.cs | 2 +- .../ResourceUpdater.cs | 136 ++++++++++++------ 2 files changed, 93 insertions(+), 45 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index b9bddb5af7e9..78edfd7382e6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -86,7 +86,7 @@ public static void Create( { // Copy resources from managed dll to the apphost new ResourceUpdater(appHostDestinationFilePath) - .AddResourcesFrom(intermediateAssembly) + .AddResourcesFromPEImage(intermediateAssembly) .Update(); } else if (log != null) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 97942b742c6c..9314b9689bd6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -54,10 +54,8 @@ private static extern bool EndUpdateResource(IntPtr hUpdate, // Loading and freeing PE files - // TODO: use safe handle implementation? private enum LoadLibraryFlags : uint { - None = 0, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 } @@ -74,19 +72,19 @@ private static extern IntPtr LoadLibraryEx(string lpFileName, // Enumerating resources private delegate bool EnumResTypeProc(IntPtr hModule, - ushort lpType, - IntPtr lParam); + IntPtr lpType, + IntPtr lParam); private delegate bool EnumResNameProc(IntPtr hModule, - ushort lpType, - ushort lpName, - IntPtr lParam); + IntPtr lpType, + IntPtr lpName, + IntPtr lParam); private delegate bool EnumResLangProc(IntPtr hModule, - ushort lpType, - ushort lpName, - ushort wLang, - IntPtr lParam); + IntPtr lpType, + IntPtr lpName, + ushort wLang, + IntPtr lParam); [DllImport("kernel32.dll",SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] @@ -97,15 +95,15 @@ private static extern bool EnumResourceTypes(IntPtr hModule, [DllImport("kernel32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EnumResourceNames(IntPtr hModule, - ushort lpType, + IntPtr lpType, EnumResNameProc lpEnumFunc, IntPtr lParam); [DllImport("kernel32.dll", SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EnumResourceLanguages(IntPtr hModule, - ushort lpType, - ushort lpName, + IntPtr lpType, + IntPtr lpName, EnumResLangProc lpEnumFunc, IntPtr lParam); @@ -128,8 +126,6 @@ private static extern IntPtr LoadResource(IntPtr hModule, private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); - public const ushort LangID_LangNeutral_SublangNeutral = 0; - /// /// Holds the native handle for the resource update. /// @@ -140,7 +136,9 @@ private static extern uint SizeofResource(IntPtr hModule, /// acquire a native resource update handle for the file, /// preparing it for updates. Resources can be added to this /// updater, which will queue them for update. The target PE - /// file will not be modified until Update() is called. + /// file will not be modified until Update() is called, after + /// which the ResourceUpdater can not be used for further + /// updates. /// public ResourceUpdater(string peFile) { @@ -156,9 +154,15 @@ public ResourceUpdater(string peFile) /// that the input is a valid PE file. If it is not, an /// exception will be thrown. This will not modify the target /// until Update() is called. + /// Throws an InvalidOperationException if Update() was already called. /// - public ResourceUpdater AddResourcesFrom(string peFile) + public ResourceUpdater AddResourcesFromPEImage(string peFile) { + if (hUpdate == IntPtr.Zero) + { + ThrowExceptionForInvalidUpdate(); + } + // Using both flags lets the OS loader decide how to load // it most efficiently. Either mode will prevent other // processes from modifying the module while it is loaded. @@ -169,50 +173,87 @@ public ResourceUpdater AddResourcesFrom(string peFile) ThrowExceptionForLastWin32Error(); } - var enumTypesCallback = new EnumResTypeProc(EnumTypesCallback); - if (!EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) + try { - ThrowExceptionForLastWin32Error(); + var enumTypesCallback = new EnumResTypeProc(EnumTypesCallback); + if (!EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) + { + ThrowExceptionForLastWin32Error(); + } } - - if (!FreeLibrary(hModule)) + finally { - ThrowExceptionForLastWin32Error(); + if (!FreeLibrary(hModule)) + { + ThrowExceptionForLastWin32Error(); + } } return this; } + private const ushort LangID_LangNeutral_SublangNeutral = 0; + + private static bool IsIntResource(IntPtr lpType) + { + return ((uint)lpType >> 16) == 0; + } + /// - /// Add a language-neutral resource from a byte[] with a - /// particular type and name. This will not modify the target - /// until Update() is called. + /// Add a language-neutral integer resource from a byte[] with + /// a particular type and name. This will not modify the + /// target until Update() is called. + /// Throws an InvalidOperationException if Update() was already called. /// public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) { - //if (!UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, data.Length)) - //{ - //ThrowExceptionForLastWin32Error(); - //} + if (hUpdate == IntPtr.Zero) + { + ThrowExceptionForInvalidUpdate(); + } + + if (!IsIntResource(lpType) || !IsIntResource(lpName)) + { + throw new ArgumentException("AddResource can only be used with integer resource types."); + } + + if (!UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) + { + ThrowExceptionForLastWin32Error(); + } return this; } /// - /// Write the pending resource updates to the target PE file. + /// Write the pending resource updates to the target PE + /// file. After this, the ResourceUpdater no longer maintains + /// an update handle, and can not be used for further updates. + /// Throws an InvalidOperationException if Update() was already called. /// public void Update() { - if (!EndUpdateResource(hUpdate, false)) + if (hUpdate == IntPtr.Zero) { - ThrowExceptionForLastWin32Error(); + ThrowExceptionForInvalidUpdate(); + } + + try + { + if (!EndUpdateResource(hUpdate, false)) + { + ThrowExceptionForLastWin32Error(); + } + } + finally + { + hUpdate = IntPtr.Zero; } } - private bool EnumTypesCallback(IntPtr hModule, ushort lpType, IntPtr lParam) + private bool EnumTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) { - // TODO: what if the lpType is a string identifier? var enumNamesCallback = new EnumResNameProc(EnumNamesCallback); if (!EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) { @@ -222,9 +263,8 @@ private bool EnumTypesCallback(IntPtr hModule, ushort lpType, IntPtr lParam) return true; } - private bool EnumNamesCallback(IntPtr hModule, ushort lpType, ushort lpName, IntPtr lParam) + private bool EnumNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, IntPtr lParam) { - // TODO: what if name is a string rather than an int? var enumLanguagesCallback = new EnumResLangProc(EnumLanguagesCallback); if (!EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) { @@ -234,36 +274,44 @@ private bool EnumNamesCallback(IntPtr hModule, ushort lpType, ushort lpName, Int return true; } - private bool EnumLanguagesCallback(IntPtr hModule, ushort lpType, ushort lpName, ushort wLang, IntPtr lParam) + private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLang, IntPtr lParam) { - IntPtr hResource = FindResourceEx(hModule, (IntPtr)lpType, (IntPtr)lpName, wLang); + IntPtr hResource = FindResourceEx(hModule, lpType, lpName, wLang); if (hResource == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); } + // hResourceLoadedq is just a handle to the resource, + // which can be used to get the resource data IntPtr hResourceLoaded = LoadResource(hModule, hResource); if (hResourceLoaded == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); } + // This doesn't actually lock memory. It just retrieves a + // pointer to the resource data. The pointer is valid + // until the module is unloaded. IntPtr lpResourceData = LockResource(hResourceLoaded); if (lpResourceData == IntPtr.Zero) { - // TODO: better exception - throw new Exception("failed to lock resource"); + throw new Exception("Failed to lock resource."); } - if (!UpdateResource(hUpdate, (IntPtr)lpType, (IntPtr)lpName, wLang, lpResourceData, SizeofResource(hModule, hResource))) + if (!UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, SizeofResource(hModule, hResource))) { ThrowExceptionForLastWin32Error(); } - // TODO: cast ushort to intptr? return true; } + private static void ThrowExceptionForInvalidUpdate() + { + throw new InvalidOperationException("Update handle is invalid. This instance may not be used for further updates."); + } + private static void ThrowExceptionForLastWin32Error() { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); From 0574dcce7999807bb7350d5a9e50eff0dc3b8d04 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 20 Sep 2018 14:04:39 -0700 Subject: [PATCH 09/28] Run the resource test on windows only --- .../GivenThatWeWantToCustomizeTheApphost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs index 28497df42095..54ef21a1e016 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToCustomizeTheApphost.cs @@ -19,7 +19,7 @@ public GivenThatWeWantToCustomizeTheApphost(ITestOutputHelper log) : base(log) { } - [Fact] + [WindowsOnlyFact] public void It_contains_resources_from_the_managed_dll() { var targetFramework = "netcoreapp2.0"; From e8fcf281f345e5203cadc265e2e05ae29373b7f6 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 10:46:32 -0700 Subject: [PATCH 10/28] Use resource strings for all error messages --- src/Tasks/Common/Resources/Strings.resx | 12 ++++++++++++ src/Tasks/Common/Resources/xlf/Strings.cs.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.de.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.es.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.fr.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.it.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.ja.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.ko.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.pl.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.ru.xlf | 15 +++++++++++++++ src/Tasks/Common/Resources/xlf/Strings.tr.xlf | 15 +++++++++++++++ .../Common/Resources/xlf/Strings.zh-Hans.xlf | 15 +++++++++++++++ .../Common/Resources/xlf/Strings.zh-Hant.xlf | 15 +++++++++++++++ .../Microsoft.NET.Build.Tasks/ResourceUpdater.cs | 6 +++--- 15 files changed, 210 insertions(+), 3 deletions(-) diff --git a/src/Tasks/Common/Resources/Strings.resx b/src/Tasks/Common/Resources/Strings.resx index 31ee2ecf32e9..8dba5775f91c 100644 --- a/src/Tasks/Common/Resources/Strings.resx +++ b/src/Tasks/Common/Resources/Strings.resx @@ -417,4 +417,16 @@ The following are names of parameters or literal values and should not be transl NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. {StrBegin="NETSDK1074: "} + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf index 1e3e20d79bb6..ddb2489b800a 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.de.xlf b/src/Tasks/Common/Resources/xlf/Strings.de.xlf index fc3869cfb61f..aeddaa8e7a0f 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.de.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf index 7fedaf5bc1b0..4cfa7fde54ca 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf index 8337d5799d14..1e26f967e880 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf index cfe3db882af2..651e2bad00c6 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf index 0e70ac739b55..f3277e4ad566 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf index 6a7dea93039a..b51d717da41c 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf index 07d0bf8e9106..ff5ede10ace2 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf index 996093d8daee..56da3ecca66e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf index 6b9809e8afe7..212c838b8b62 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf index a8cbf00ef7fa..a533a0d4dc19 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf index 7f9a5931fe51..a6ace4f83e8e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf index 1805fcd172df..9e7ee12e5a41 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -2,6 +2,21 @@ + + NETSDK1077: Failed to lock resource. + NETSDK1077: Failed to lock resource. + {StrBegin="NETSDK1077: "} + + + NETSDK1076: AddResource can only be used with integer resource types. + NETSDK1076: AddResource can only be used with integer resource types. + {StrBegin="NETSDK1076: "} + + + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + NETSDK1075: Update handle is invalid. This instance may not be used for further updates. + {StrBegin="NETSDK1075: "} + NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 9314b9689bd6..44da858ab5d0 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -214,7 +214,7 @@ public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) if (!IsIntResource(lpType) || !IsIntResource(lpName)) { - throw new ArgumentException("AddResource can only be used with integer resource types."); + throw new ArgumentException(Strings.AddResourceWithNonIntegerResource); } if (!UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) @@ -296,7 +296,7 @@ private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, IntPtr lpResourceData = LockResource(hResourceLoaded); if (lpResourceData == IntPtr.Zero) { - throw new Exception("Failed to lock resource."); + throw new Exception(Strings.FailedToLockResource); } if (!UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, SizeofResource(hModule, hResource))) @@ -309,7 +309,7 @@ private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, private static void ThrowExceptionForInvalidUpdate() { - throw new InvalidOperationException("Update handle is invalid. This instance may not be used for further updates."); + throw new InvalidOperationException(Strings.InvalidResourceUpdate); } private static void ThrowExceptionForLastWin32Error() From 156e0c33f4b906ac8cd655f27c7fc0dc626c6863 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 11:26:37 -0700 Subject: [PATCH 11/28] Use a private sealed class for pinvokes --- .../ResourceUpdater.cs | 241 +++++++++--------- 1 file changed, 123 insertions(+), 118 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 44da858ab5d0..1e4539ad6919 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -15,116 +15,120 @@ namespace Microsoft.NET.Build.Tasks /// public class ResourceUpdater { - // - // Native methods for updating resources - // - - [DllImport("kernel32.dll", SetLastError=true)] - private static extern IntPtr BeginUpdateResource(string pFileName, - [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources); - - // Update a resource with data from an IntPtr - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool UpdateResource(IntPtr hUpdate, - IntPtr lpType, - IntPtr lpName, - ushort wLanguage, - IntPtr lpData, - uint cbData); - - // Update a resource with data from a managed byte[] - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool UpdateResource(IntPtr hUpdate, - IntPtr lpType, - IntPtr lpName, - ushort wLanguage, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, - uint cbData); - - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EndUpdateResource(IntPtr hUpdate, - bool fDiscard); - - // - // Native methods used to read resources from a PE file - // - - // Loading and freeing PE files - - private enum LoadLibraryFlags : uint - { - LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, - LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 - } - [DllImport("kernel32.dll", SetLastError=true)] - private static extern IntPtr LoadLibraryEx(string lpFileName, - IntPtr hReservedNull, - LoadLibraryFlags dwFlags); - - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool FreeLibrary(IntPtr hModule); - - // Enumerating resources - - private delegate bool EnumResTypeProc(IntPtr hModule, - IntPtr lpType, - IntPtr lParam); - - private delegate bool EnumResNameProc(IntPtr hModule, - IntPtr lpType, - IntPtr lpName, - IntPtr lParam); - - private delegate bool EnumResLangProc(IntPtr hModule, - IntPtr lpType, - IntPtr lpName, - ushort wLang, - IntPtr lParam); - - [DllImport("kernel32.dll",SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EnumResourceTypes(IntPtr hModule, - EnumResTypeProc lpEnumFunc, - IntPtr lParam); - - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EnumResourceNames(IntPtr hModule, + private sealed class Kernel32 + { + // + // Native methods for updating resources + // + + [DllImport(nameof(Kernel32), SetLastError=true)] + public static extern IntPtr BeginUpdateResource(string pFileName, + [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources); + + // Update a resource with data from an IntPtr + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UpdateResource(IntPtr hUpdate, IntPtr lpType, - EnumResNameProc lpEnumFunc, - IntPtr lParam); + IntPtr lpName, + ushort wLanguage, + IntPtr lpData, + uint cbData); + + // Update a resource with data from a managed byte[] + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UpdateResource(IntPtr hUpdate, + IntPtr lpType, + IntPtr lpName, + ushort wLanguage, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, + uint cbData); + + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EndUpdateResource(IntPtr hUpdate, + bool fDiscard); + + // + // Native methods used to read resources from a PE file + // + + // Loading and freeing PE files + + public enum LoadLibraryFlags : uint + { + LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, + LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 + } - [DllImport("kernel32.dll", SetLastError=true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool EnumResourceLanguages(IntPtr hModule, + [DllImport(nameof(Kernel32), SetLastError=true)] + public static extern IntPtr LoadLibraryEx(string lpFileName, + IntPtr hReservedNull, + LoadLibraryFlags dwFlags); + + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FreeLibrary(IntPtr hModule); + + // Enumerating resources + + public delegate bool EnumResTypeProc(IntPtr hModule, + IntPtr lpType, + IntPtr lParam); + + public delegate bool EnumResNameProc(IntPtr hModule, + IntPtr lpType, + IntPtr lpName, + IntPtr lParam); + + public delegate bool EnumResLangProc(IntPtr hModule, + IntPtr lpType, + IntPtr lpName, + ushort wLang, + IntPtr lParam); + + [DllImport(nameof(Kernel32),SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumResourceTypes(IntPtr hModule, + EnumResTypeProc lpEnumFunc, + IntPtr lParam); + + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpType, - IntPtr lpName, - EnumResLangProc lpEnumFunc, + EnumResNameProc lpEnumFunc, IntPtr lParam); - // Querying and loading resources + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumResourceLanguages(IntPtr hModule, + IntPtr lpType, + IntPtr lpName, + EnumResLangProc lpEnumFunc, + IntPtr lParam); - [DllImport("kernel32.dll", SetLastError=true)] - private static extern IntPtr FindResourceEx(IntPtr hModule, - IntPtr lpType, - IntPtr lpName, - ushort wLanguage); + // Querying and loading resources - [DllImport("kernel32.dll", SetLastError=true)] - private static extern IntPtr LoadResource(IntPtr hModule, - IntPtr hResInfo); + [DllImport(nameof(Kernel32), SetLastError=true)] + public static extern IntPtr FindResourceEx(IntPtr hModule, + IntPtr lpType, + IntPtr lpName, + ushort wLanguage); - [DllImport("kernel32.dll")] // does not call SetLastError - private static extern IntPtr LockResource(IntPtr hResData); + [DllImport(nameof(Kernel32), SetLastError=true)] + public static extern IntPtr LoadResource(IntPtr hModule, + IntPtr hResInfo); - [DllImport("kernel32.dll", SetLastError=true)] - private static extern uint SizeofResource(IntPtr hModule, - IntPtr hResInfo); + [DllImport(nameof(Kernel32))] // does not call SetLastError + public static extern IntPtr LockResource(IntPtr hResData); + + [DllImport(nameof(Kernel32), SetLastError=true)] + public static extern uint SizeofResource(IntPtr hModule, + IntPtr hResInfo); + } /// /// Holds the native handle for the resource update. @@ -142,7 +146,7 @@ private static extern uint SizeofResource(IntPtr hModule, /// public ResourceUpdater(string peFile) { - hUpdate = BeginUpdateResource(peFile, false); + hUpdate = Kernel32.BeginUpdateResource(peFile, false); if (hUpdate == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); @@ -166,8 +170,9 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) // Using both flags lets the OS loader decide how to load // it most efficiently. Either mode will prevent other // processes from modifying the module while it is loaded. - IntPtr hModule = LoadLibraryEx(peFile, IntPtr.Zero, - LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LoadLibraryFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE); + IntPtr hModule = Kernel32.LoadLibraryEx(peFile, IntPtr.Zero, + Kernel32.LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | + Kernel32.LoadLibraryFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (hModule == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); @@ -175,15 +180,15 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) try { - var enumTypesCallback = new EnumResTypeProc(EnumTypesCallback); - if (!EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) + var enumTypesCallback = new Kernel32.EnumResTypeProc(EnumTypesCallback); + if (!Kernel32.EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) { ThrowExceptionForLastWin32Error(); } } finally { - if (!FreeLibrary(hModule)) + if (!Kernel32.FreeLibrary(hModule)) { ThrowExceptionForLastWin32Error(); } @@ -217,7 +222,7 @@ public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) throw new ArgumentException(Strings.AddResourceWithNonIntegerResource); } - if (!UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) + if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) { ThrowExceptionForLastWin32Error(); } @@ -240,7 +245,7 @@ public void Update() try { - if (!EndUpdateResource(hUpdate, false)) + if (!Kernel32.EndUpdateResource(hUpdate, false)) { ThrowExceptionForLastWin32Error(); } @@ -254,8 +259,8 @@ public void Update() private bool EnumTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) { - var enumNamesCallback = new EnumResNameProc(EnumNamesCallback); - if (!EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) + var enumNamesCallback = new Kernel32.EnumResNameProc(EnumNamesCallback); + if (!Kernel32.EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) { ThrowExceptionForLastWin32Error(); } @@ -265,8 +270,8 @@ private bool EnumTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) private bool EnumNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, IntPtr lParam) { - var enumLanguagesCallback = new EnumResLangProc(EnumLanguagesCallback); - if (!EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) + var enumLanguagesCallback = new Kernel32.EnumResLangProc(EnumLanguagesCallback); + if (!Kernel32.EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) { ThrowExceptionForLastWin32Error(); } @@ -276,15 +281,15 @@ private bool EnumNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, Int private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLang, IntPtr lParam) { - IntPtr hResource = FindResourceEx(hModule, lpType, lpName, wLang); + IntPtr hResource = Kernel32.FindResourceEx(hModule, lpType, lpName, wLang); if (hResource == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); } - // hResourceLoadedq is just a handle to the resource, - // which can be used to get the resource data - IntPtr hResourceLoaded = LoadResource(hModule, hResource); + // hResourceLoaded is just a handle to the resource, which + // can be used to get the resource data + IntPtr hResourceLoaded = Kernel32.LoadResource(hModule, hResource); if (hResourceLoaded == IntPtr.Zero) { ThrowExceptionForLastWin32Error(); @@ -293,13 +298,13 @@ private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, // This doesn't actually lock memory. It just retrieves a // pointer to the resource data. The pointer is valid // until the module is unloaded. - IntPtr lpResourceData = LockResource(hResourceLoaded); + IntPtr lpResourceData = Kernel32.LockResource(hResourceLoaded); if (lpResourceData == IntPtr.Zero) { throw new Exception(Strings.FailedToLockResource); } - if (!UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, SizeofResource(hModule, hResource))) + if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, Kernel32.SizeofResource(hModule, hResource))) { ThrowExceptionForLastWin32Error(); } From 5833c673bc53d75f0a301e7e497906f7f7e2e428 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 11:32:33 -0700 Subject: [PATCH 12/28] Add comment about PE header --- src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index 78edfd7382e6..c58f9c339dd1 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -289,6 +289,7 @@ private static unsafe void SetWindowsGraphicalUserInterfaceBit( 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)) From b029ce0cc326b0e1f6b9d4a5c07e0619d5fe2f00 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 11:32:50 -0700 Subject: [PATCH 13/28] Document new parameters to AppHost.Create --- src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index c58f9c339dd1..06e895c2cf01 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -28,6 +28,8 @@ internal static class AppHost /// Full path to app binary or relative path to the result apphost file /// If override the file existed in /// Options to customize the created apphost + /// Path to the intermediate assembly, used for copying resources to PE apphosts. + /// Specify the logger used to log warnings and messages. If null, no logging is done. public static void Create( string appHostSourceFilePath, string appHostDestinationFilePath, From c455384badbeb552d096daead2c1aff48911a420 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 11:36:12 -0700 Subject: [PATCH 14/28] Use more descriptive callback names --- .../Microsoft.NET.Build.Tasks/ResourceUpdater.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 1e4539ad6919..8d3c6387242c 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -180,7 +180,7 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) try { - var enumTypesCallback = new Kernel32.EnumResTypeProc(EnumTypesCallback); + var enumTypesCallback = new Kernel32.EnumResTypeProc(EnumAndUpdateTypesCallback); if (!Kernel32.EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) { ThrowExceptionForLastWin32Error(); @@ -257,9 +257,9 @@ public void Update() } - private bool EnumTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) + private bool EnumAndUpdateTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) { - var enumNamesCallback = new Kernel32.EnumResNameProc(EnumNamesCallback); + var enumNamesCallback = new Kernel32.EnumResNameProc(EnumAndUpdateNamesCallback); if (!Kernel32.EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) { ThrowExceptionForLastWin32Error(); @@ -268,9 +268,9 @@ private bool EnumTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) return true; } - private bool EnumNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, IntPtr lParam) + private bool EnumAndUpdateNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, IntPtr lParam) { - var enumLanguagesCallback = new Kernel32.EnumResLangProc(EnumLanguagesCallback); + var enumLanguagesCallback = new Kernel32.EnumResLangProc(EnumAndUpdateLanguagesCallback); if (!Kernel32.EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) { ThrowExceptionForLastWin32Error(); @@ -279,7 +279,7 @@ private bool EnumNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, Int return true; } - private bool EnumLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLang, IntPtr lParam) + private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPtr lpName, ushort wLang, IntPtr lParam) { IntPtr hResource = Kernel32.FindResourceEx(hModule, lpType, lpName, wLang); if (hResource == IntPtr.Zero) From ecba3055f921df81e9e7ef3096c7f463c43692ef Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 17:44:25 -0700 Subject: [PATCH 15/28] Implement IDisposable using a SafeHandle for hUpdate --- .../ResourceUpdater.cs | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 8d3c6387242c..4e56141955dc 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -13,9 +13,8 @@ namespace Microsoft.NET.Build.Tasks /// in a PE image. It currently only works on Windows, because it /// requires various kernel32 APIs. /// - public class ResourceUpdater + public class ResourceUpdater : IDisposable { - private sealed class Kernel32 { // @@ -23,13 +22,13 @@ private sealed class Kernel32 // [DllImport(nameof(Kernel32), SetLastError=true)] - public static extern IntPtr BeginUpdateResource(string pFileName, - [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources); + public static extern SafeUpdateHandle BeginUpdateResource(string pFileName, + [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources); // Update a resource with data from an IntPtr [DllImport(nameof(Kernel32), SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool UpdateResource(IntPtr hUpdate, + public static extern bool UpdateResource(SafeUpdateHandle hUpdate, IntPtr lpType, IntPtr lpName, ushort wLanguage, @@ -39,13 +38,20 @@ public static extern bool UpdateResource(IntPtr hUpdate, // Update a resource with data from a managed byte[] [DllImport(nameof(Kernel32), SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool UpdateResource(IntPtr hUpdate, + public static extern bool UpdateResource(SafeUpdateHandle hUpdate, IntPtr lpType, IntPtr lpName, ushort wLanguage, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5)] byte[] lpData, uint cbData); + [DllImport(nameof(Kernel32), SetLastError=true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EndUpdateResource(SafeUpdateHandle hUpdate, + bool fDiscard); + + // The IntPtr version of this dllimport is used in the + // SafeHandle implementation [DllImport(nameof(Kernel32), SetLastError=true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EndUpdateResource(IntPtr hUpdate, @@ -130,10 +136,33 @@ public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); } + /// + /// Holds the update handle returned by BeginUpdateResource. + /// Normally, native resources for the update handle are + /// released by a call to ResourceUpdater.Update(). In case + /// this doesn't happen, the SafeUpdateHandle will release the + /// native resources for the update handle without updating + /// the target file. + /// + private class SafeUpdateHandle : SafeHandle + { + private SafeUpdateHandle() : base(IntPtr.Zero, true) + { + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + // discard pending updates without writing them + return Kernel32.EndUpdateResource(handle, true); + } + } + /// /// Holds the native handle for the resource update. /// - private IntPtr hUpdate = IntPtr.Zero; + private readonly SafeUpdateHandle hUpdate; /// /// Create a resource updater for the given PE file. This will @@ -147,7 +176,7 @@ public static extern uint SizeofResource(IntPtr hModule, public ResourceUpdater(string peFile) { hUpdate = Kernel32.BeginUpdateResource(peFile, false); - if (hUpdate == IntPtr.Zero) + if (hUpdate.IsInvalid) { ThrowExceptionForLastWin32Error(); } @@ -162,7 +191,7 @@ public ResourceUpdater(string peFile) /// public ResourceUpdater AddResourcesFromPEImage(string peFile) { - if (hUpdate == IntPtr.Zero) + if (hUpdate.IsInvalid) { ThrowExceptionForInvalidUpdate(); } @@ -212,7 +241,7 @@ private static bool IsIntResource(IntPtr lpType) /// public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) { - if (hUpdate == IntPtr.Zero) + if (hUpdate.IsInvalid) { ThrowExceptionForInvalidUpdate(); } @@ -238,7 +267,7 @@ public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) /// public void Update() { - if (hUpdate == IntPtr.Zero) + if (hUpdate.IsInvalid) { ThrowExceptionForInvalidUpdate(); } @@ -252,7 +281,7 @@ public void Update() } finally { - hUpdate = IntPtr.Zero; + hUpdate.SetHandleAsInvalid(); } } @@ -321,5 +350,19 @@ private static void ThrowExceptionForLastWin32Error() { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void Dispose(bool disposing) + { + if (disposing) + { + hUpdate.Dispose(); + } + } } } From 49ee50215d2c44f25d1317986676fe55fb161f73 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 17:52:15 -0700 Subject: [PATCH 16/28] Use custom exception type for LockResource failure --- .../ResourceUpdater.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 4e56141955dc..e7deacd08fe6 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -330,7 +330,7 @@ private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPt IntPtr lpResourceData = Kernel32.LockResource(hResourceLoaded); if (lpResourceData == IntPtr.Zero) { - throw new Exception(Strings.FailedToLockResource); + throw new ResourceNotAvailableException(Strings.FailedToLockResource); } if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, Kernel32.SizeofResource(hModule, hResource))) @@ -341,6 +341,21 @@ private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPt return true; } + private class ResourceNotAvailableException : Exception + { + public ResourceNotAvailableException() + { + } + + public ResourceNotAvailableException(string message) : base(message) + { + } + + public ResourceNotAvailableException(string message, Exception inner) : base(message, inner) + { + } + } + private static void ThrowExceptionForInvalidUpdate() { throw new InvalidOperationException(Strings.InvalidResourceUpdate); From 20f4c1ec75512d0804c1cdb789ff7f742bf01932 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 17:58:50 -0700 Subject: [PATCH 17/28] Remove AppHostOptions --- .../GivenAnAppHost.cs | 24 +++++-------------- .../Microsoft.NET.Build.Tasks/AppHost.cs | 17 ++++++------- .../AppHostOptions.cs | 14 ----------- .../CreateAppHost.cs | 5 +--- 4 files changed, 14 insertions(+), 46 deletions(-) delete mode 100644 src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs index 1b0801bd1ef8..c64ca5843954 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs @@ -29,8 +29,7 @@ public void ItEmbedsAppBinaryPath() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, - options: null); + overwriteExisting: false); byte[] binaryPathBlob = Encoding.UTF8.GetBytes(appBinaryFilePath); byte[] result = File.ReadAllBytes(destinationFilePath); @@ -65,8 +64,7 @@ public void ItFailsToEmbedAppBinaryIfHashIsWrong() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, - options: null)) + overwriteExisting: false)) .Message .Should() .Contain(sourceAppHostMock) @@ -89,8 +87,7 @@ public void ItFailsToEmbedTooLongAppBinaryPath() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, - options: null)) + overwriteExisting: false)) .Message .Should() .Contain(appBinaryFilePath); @@ -111,10 +108,7 @@ public void ItCanSetWindowsGUISubsystem() destinationFilePath, appBinaryFilePath, overwriteExisting: false, - options: new AppHostOptions - { - WindowsGraphicalUserInterface = true - }); + windowsGraphicalUserInterface: true); BitConverter .ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset) @@ -143,10 +137,7 @@ public void ItFailsToSetGUISubsystemOnNonWindowsPEFile() destinationFilePath, appBinaryFilePath, overwriteExisting: false, - options: new AppHostOptions - { - WindowsGraphicalUserInterface = true - })) + windowsGraphicalUserInterface: true)) .Message .Should() .Contain(sourceAppHostMock); @@ -172,10 +163,7 @@ public void ItFailsToSetGUISubsystemWithWrongDefault() destinationFilePath, appBinaryFilePath, overwriteExisting: false, - options: new AppHostOptions - { - WindowsGraphicalUserInterface = true - })) + windowsGraphicalUserInterface: true)) .Message .Should() .Contain(sourceAppHostMock); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index 06e895c2cf01..1086f7519ba2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -27,7 +27,7 @@ internal static class AppHost /// 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 /// If override the file existed in - /// Options to customize the created apphost + /// 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. /// Specify the logger used to log warnings and messages. If null, no logging is done. public static void Create( @@ -35,7 +35,7 @@ public static void Create( string appHostDestinationFilePath, string appBinaryFilePath, bool overwriteExisting = false, - AppHostOptions options = null, + bool windowsGraphicalUserInterface = false, string intermediateAssembly = null, Logger log = null) { @@ -67,17 +67,14 @@ public static void Create( appHostIsPEImage = IsPEImage(accessor); - if (options != null) + if (windowsGraphicalUserInterface) { - if (options.WindowsGraphicalUserInterface) + if (!appHostIsPEImage) { - if (!appHostIsPEImage) - { - throw new BuildErrorException(Strings.AppHostNotWindows, appHostSourceFilePath); - } - - SetWindowsGraphicalUserInterfaceBit(accessor, appHostSourceFilePath); + throw new BuildErrorException(Strings.AppHostNotWindows, appHostSourceFilePath); } + + SetWindowsGraphicalUserInterfaceBit(accessor, appHostSourceFilePath); } } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs deleted file mode 100644 index dcef3a704798..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHostOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Microsoft.NET.Build.Tasks -{ - /// - /// Options to customize the apphost. - /// - internal class AppHostOptions - { - /// - /// If this is set to true and the apphost is a Windows PE executable, it will have its subsystem set to GUI. - /// By default apphost's subsystem on Windows is set to CUI (Console). - /// - public bool WindowsGraphicalUserInterface { get; set; } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index e09d46148191..daf6c7de0ea8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -41,10 +41,7 @@ protected override void ExecuteCore() AppHostSourcePath, ModifiedAppHostPath, AppBinaryName, - options: new AppHostOptions() - { - WindowsGraphicalUserInterface = WindowsGraphicalUserInterface - }, + windowsGraphicalUserInterface : WindowsGraphicalUserInterface, intermediateAssembly: IntermediateAssembly, log: Log); } From 4199da9360bf2d4c1e8b53fd29d293117cdf53f6 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 24 Sep 2018 18:01:22 -0700 Subject: [PATCH 18/28] Make IntermediateAssembly required in CreateAppHost task --- src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index daf6c7de0ea8..1e0dc4cdc0e5 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -21,6 +21,7 @@ public class CreateAppHost : TaskBase [Required] public string AppBinaryName { get; set; } + [Required] public string IntermediateAssembly { get; set; } public bool WindowsGraphicalUserInterface { get; set; } From 3af93e195e2c5ee372a1382d8a5b2d65a75c4760 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 25 Sep 2018 10:18:05 -0700 Subject: [PATCH 19/28] Make ResourceUpdater internal --- src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index e7deacd08fe6..c6536a81ceee 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -13,7 +13,7 @@ namespace Microsoft.NET.Build.Tasks /// in a PE image. It currently only works on Windows, because it /// requires various kernel32 APIs. /// - public class ResourceUpdater : IDisposable + internal class ResourceUpdater : IDisposable { private sealed class Kernel32 { From ea4f506a0916f8523e32273ceff64f3ecd82a2a1 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 25 Sep 2018 16:30:32 -0700 Subject: [PATCH 20/28] Don't throw managed exceptions from callbacks Instead, use the extra parameter to capture error information, and throw the appropriate error from the caller of EnumResourceTypes. --- .../ResourceUpdater.cs | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index c6536a81ceee..4dd49944b790 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -116,6 +116,8 @@ public static extern bool EnumResourceLanguages(IntPtr hModule, EnumResLangProc lpEnumFunc, IntPtr lParam); + public const int UserStoppedResourceEnumerationHRESULT = unchecked((int)0x80073B02); + // Querying and loading resources [DllImport(nameof(Kernel32), SetLastError=true)] @@ -207,16 +209,24 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) ThrowExceptionForLastWin32Error(); } + var enumTypesCallback = new Kernel32.EnumResTypeProc(EnumAndUpdateTypesCallback); + var errorInfo = new EnumResourcesErrorInfo(); + GCHandle errorInfoHandle = GCHandle.Alloc(errorInfo); + var errorInfoPtr = GCHandle.ToIntPtr(errorInfoHandle); + try { - var enumTypesCallback = new Kernel32.EnumResTypeProc(EnumAndUpdateTypesCallback); - if (!Kernel32.EnumResourceTypes(hModule, enumTypesCallback, IntPtr.Zero)) + if (!Kernel32.EnumResourceTypes(hModule, enumTypesCallback, errorInfoPtr)) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(errorInfoPtr); + + errorInfo.ThrowException(); } } finally { + errorInfoHandle.Free(); + if (!Kernel32.FreeLibrary(hModule)) { ThrowExceptionForLastWin32Error(); @@ -285,15 +295,14 @@ public void Update() } } - private bool EnumAndUpdateTypesCallback(IntPtr hModule, IntPtr lpType, IntPtr lParam) { var enumNamesCallback = new Kernel32.EnumResNameProc(EnumAndUpdateNamesCallback); if (!Kernel32.EnumResourceNames(hModule, lpType, enumNamesCallback, lParam)) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(lParam); + return false; } - return true; } @@ -302,9 +311,9 @@ private bool EnumAndUpdateNamesCallback(IntPtr hModule, IntPtr lpType, IntPtr lp var enumLanguagesCallback = new Kernel32.EnumResLangProc(EnumAndUpdateLanguagesCallback); if (!Kernel32.EnumResourceLanguages(hModule, lpType, lpName, enumLanguagesCallback, lParam)) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(lParam); + return false; } - return true; } @@ -313,7 +322,8 @@ private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPt IntPtr hResource = Kernel32.FindResourceEx(hModule, lpType, lpName, wLang); if (hResource == IntPtr.Zero) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(lParam); + return false; } // hResourceLoaded is just a handle to the resource, which @@ -321,7 +331,8 @@ private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPt IntPtr hResourceLoaded = Kernel32.LoadResource(hModule, hResource); if (hResourceLoaded == IntPtr.Zero) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(lParam); + return false; } // This doesn't actually lock memory. It just retrieves a @@ -330,40 +341,69 @@ private bool EnumAndUpdateLanguagesCallback(IntPtr hModule, IntPtr lpType, IntPt IntPtr lpResourceData = Kernel32.LockResource(hResourceLoaded); if (lpResourceData == IntPtr.Zero) { - throw new ResourceNotAvailableException(Strings.FailedToLockResource); - } + ((EnumResourcesErrorInfo)GCHandle.FromIntPtr(lParam).Target).failedToLockResource = true; + } if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, wLang, lpResourceData, Kernel32.SizeofResource(hModule, hResource))) { - ThrowExceptionForLastWin32Error(); + CaptureEnumResourcesErrorInfo(lParam); + return false; } return true; } - private class ResourceNotAvailableException : Exception + private class EnumResourcesErrorInfo { - public ResourceNotAvailableException() + public int hResult; + public bool failedToLockResource; + + public void ThrowException() { + if (failedToLockResource) + { + Debug.Assert(hResult == 0); + throw new ResourceNotAvailableException(Strings.FailedToLockResource); + } + + Debug.Assert(hResult != 0); + throw new HResultException(hResult); } + } - public ResourceNotAvailableException(string message) : base(message) + private static void CaptureEnumResourcesErrorInfo(IntPtr errorInfoPtr) + { + int hResult = Marshal.GetHRForLastWin32Error(); + if (hResult != Kernel32.UserStoppedResourceEnumerationHRESULT) { + GCHandle errorInfoHandle = GCHandle.FromIntPtr(errorInfoPtr); + var errorInfo = (EnumResourcesErrorInfo)errorInfoHandle.Target; + errorInfo.hResult = hResult; } + } - public ResourceNotAvailableException(string message, Exception inner) : base(message, inner) + private class ResourceNotAvailableException : Exception + { + public ResourceNotAvailableException(string message) : base(message) { } } - private static void ThrowExceptionForInvalidUpdate() + private class HResultException : Exception { - throw new InvalidOperationException(Strings.InvalidResourceUpdate); + public HResultException(int hResult) : base(hResult.ToString("X4")) + { + } } private static void ThrowExceptionForLastWin32Error() { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw new HResultException(Marshal.GetHRForLastWin32Error()); + } + + private static void ThrowExceptionForInvalidUpdate() + { + throw new InvalidOperationException(Strings.InvalidResourceUpdate); } public void Dispose() From 982d4ab37bc56d7d08760137085b84991f84cbdd Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 25 Sep 2018 16:31:53 -0700 Subject: [PATCH 21/28] Move language-neutral constant to Kernel32 class --- src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs index 4dd49944b790..bc8c2c41c7e3 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResourceUpdater.cs @@ -57,6 +57,8 @@ public static extern bool EndUpdateResource(SafeUpdateHandle hUpdate, public static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard); + public const ushort LangID_LangNeutral_SublangNeutral = 0; + // // Native methods used to read resources from a PE file // @@ -236,8 +238,6 @@ public ResourceUpdater AddResourcesFromPEImage(string peFile) return this; } - private const ushort LangID_LangNeutral_SublangNeutral = 0; - private static bool IsIntResource(IntPtr lpType) { return ((uint)lpType >> 16) == 0; @@ -261,7 +261,7 @@ public ResourceUpdater AddResource(byte[] data, IntPtr lpType, IntPtr lpName) throw new ArgumentException(Strings.AddResourceWithNonIntegerResource); } - if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) + if (!Kernel32.UpdateResource(hUpdate, lpType, lpName, Kernel32.LangID_LangNeutral_SublangNeutral, data, (uint)data.Length)) { ThrowExceptionForLastWin32Error(); } From 38d485a63ad64c4987f0535e30ebb14ec3f74d9d Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 26 Sep 2018 17:11:35 -0700 Subject: [PATCH 22/28] Separate _CreateAppHost from _ComputeNETCoreBuildOutputFiles This cuts the dependency from _ComputeNETCoreBuildOutputFiles on CoreCompile. Instead, _CreateAppHost will run after compilation via CompileDependson. Locating the restored apphost has been factored into another target _GetAppHostPaths, which is a dependency of both _ComputeNETCoreBuildOutputFiles and _CreateAppHost. The destination apphost path is also computed here, and passed to the CreateAppHost task as an input. This way, during a "publish --no-build", the apphost destintation path (of the previously-generated apphost) is computed as a dependency of _ComputeNETCoreBuildOutputFiles, and it is copied to the publish directory without causing CoreCompile to run. --- .../CreateAppHost.cs | 11 +-- .../targets/Microsoft.NET.Sdk.targets | 83 +++++++++++++------ 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index 1e0dc4cdc0e5..8aeb2205c3d2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -16,7 +16,7 @@ public class CreateAppHost : TaskBase public string AppHostSourcePath { get; set; } [Required] - public string AppHostDestinationDirectoryPath { get; set; } + public string AppHostDestinationPath { get; set; } [Required] public string AppBinaryName { get; set; } @@ -26,21 +26,16 @@ public class CreateAppHost : TaskBase public bool WindowsGraphicalUserInterface { get; set; } - [Output] - public string ModifiedAppHostPath { get; set; } - protected override void ExecuteCore() { var hostExtension = Path.GetExtension(AppHostSourcePath); var appbaseName = Path.GetFileNameWithoutExtension(AppBinaryName); - var destinationDirectory = Path.GetFullPath(AppHostDestinationDirectoryPath); - ModifiedAppHostPath = Path.Combine(destinationDirectory, $"{appbaseName}{hostExtension}"); - if (!File.Exists(ModifiedAppHostPath)) + if (!File.Exists(AppHostDestinationPath)) { AppHost.Create( AppHostSourcePath, - ModifiedAppHostPath, + AppHostDestinationPath, AppBinaryName, windowsGraphicalUserInterface : WindowsGraphicalUserInterface, intermediateAssembly: IntermediateAssembly, diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index a882833be07f..8bb61d22ba3b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -267,6 +267,64 @@ Copyright (c) .NET Foundation. All rights reserved. + + + $(CompileDependsOn) + _CreateAppHost; + + + + + + + + true + + + + + + + + + + + + $(BaseIntermediateOutputPath)\$(TargetFramework)\$(RuntimeIdentifier)\host + + + + + + + + + - @@ -291,30 +348,8 @@ Copyright (c) .NET Foundation. All rights reserved. Condition="'$(SelfContained)' == 'true' and ('%(NativeCopyLocalItems.FileName)%(NativeCopyLocalItems.Extension)' == '$(_DotNetHostPolicyLibraryName)' or '%(NativeCopyLocalItems.FileName)%(NativeCopyLocalItems.Extension)' == '$(_DotNetHostFxrLibraryName)')" /> - - - - $(BaseIntermediateOutputPath)\$(TargetFramework)\$(RuntimeIdentifier)\host - true - - - - - - - - - From 9e5b84ba1438d28f7affb81e312268f20e070eae Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 09:25:16 -0700 Subject: [PATCH 23/28] Place customized apphost into IntermediateOutputPath --- .../targets/Microsoft.NET.Sdk.targets | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 8bb61d22ba3b..d499685cdd08 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -311,17 +311,13 @@ Copyright (c) .NET Foundation. All rights reserved. Condition="'%(NativeCopyLocalItems.FileName)%(NativeCopyLocalItems.Extension)' == '$(_DotNetAppHostExecutableName)'"/> - - $(BaseIntermediateOutputPath)\$(TargetFramework)\$(RuntimeIdentifier)\host - - + Include="@(NativeRestoredAppHostNETCore->'$(IntermediateOutputPath)\$(AssemblyName)%(Extension)')" /> From 801cacd149b05752fe19c59aa33d4ef4cf0f064f Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 09:27:27 -0700 Subject: [PATCH 24/28] Make UseWindowsGraphicalUserInterface property private And fix a typo --- .../targets/Microsoft.NET.Sdk.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index d499685cdd08..79a130b0dbc1 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -277,7 +277,7 @@ Copyright (c) .NET Foundation. All rights reserved. @@ -286,13 +286,13 @@ Copyright (c) .NET Foundation. All rights reserved. DependsOnTargets="_GetAppHostPaths;CoreCompile" Condition="'$(ComputeNETCoreBuildOutputFiles)' == 'true'"> - true + <_UseWindowsGraphicalUserInterface Condition="$(RuntimeIdentifier.StartsWith('win')) and '$(OutputType)'=='WinExe'">true From dce92c78a867da9c4d6e066d9b56e8862fb24566 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 09:36:39 -0700 Subject: [PATCH 25/28] Add missing semicolon in CompileDependsOn --- .../Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 79a130b0dbc1..782ed5e178a0 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -269,7 +269,7 @@ Copyright (c) .NET Foundation. All rights reserved. - $(CompileDependsOn) + $(CompileDependsOn); _CreateAppHost; From 45eef495961ef7887f8618251649d9a86b447575 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 09:42:42 -0700 Subject: [PATCH 26/28] Improve warning message --- src/Tasks/Common/Resources/Strings.resx | 2 +- src/Tasks/Common/Resources/xlf/Strings.cs.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.de.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.es.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.fr.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.it.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.ja.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.ko.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.pl.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.ru.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.tr.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Tasks/Common/Resources/Strings.resx b/src/Tasks/Common/Resources/Strings.resx index 8dba5775f91c..f31c540ea9c7 100644 --- a/src/Tasks/Common/Resources/Strings.resx +++ b/src/Tasks/Common/Resources/Strings.resx @@ -414,7 +414,7 @@ The following are names of parameters or literal values and should not be transl {StrBegin="NETSDK1073: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf index ddb2489b800a..339c0232d163 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.de.xlf b/src/Tasks/Common/Resources/xlf/Strings.de.xlf index aeddaa8e7a0f..159274faeb37 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.de.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf index 4cfa7fde54ca..8e2e560ac60a 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf index 1e26f967e880..a1d525cc595e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf index 651e2bad00c6..fa0066197922 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf index f3277e4ad566..12b22769a982 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf index b51d717da41c..04dec375bbba 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf index ff5ede10ace2..f5223ac8c0e9 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf index 56da3ecca66e..fb308fbe777b 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf index 212c838b8b62..4c73772a194a 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf index a533a0d4dc19..e2eef0cbeca7 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf index a6ace4f83e8e..8e703814dde8 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf index 9e7ee12e5a41..68f129b00e7f 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -18,8 +18,8 @@ {StrBegin="NETSDK1075: "} - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. - NETSDK1074: Unable to add resources to the application host executable because it's not being built on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. + NETSDK1074: The application host executable will not be customized because adding resources requires that the build be performed on Windows. {StrBegin="NETSDK1074: "} From 677fb260549b5634df6ce1f63bcd8fa77f42c738 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 09:59:31 -0700 Subject: [PATCH 27/28] Fix apphost incremental build The apphost is (optionally) customized by copying resources from the managed dll, and setting the GUI bit based on the Target property (which also modifies the managed dll). Therefore, we use the managed dll as an input for incremental build (the apphost needs to be updated iff the managed dll was updated). This fixes https://github.com/dotnet/sdk/issues/2554. This also removes the unnecessary overwriteExisting flag from AppHost.Create, fixing https://github.com/dotnet/sdk/issues/2473. --- src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs | 4 +--- .../Microsoft.NET.Build.Tasks/CreateAppHost.cs | 17 +++++++---------- .../Microsoft.NET.Build.Tasks/GenerateShims.cs | 3 +-- .../targets/Microsoft.NET.Sdk.targets | 2 ++ 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs index 1086f7519ba2..23f4b53c010d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/AppHost.cs @@ -26,7 +26,6 @@ internal static class AppHost /// The path of Apphost template, which has the place holder /// 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 - /// If override the file existed in /// 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. /// Specify the logger used to log warnings and messages. If null, no logging is done. @@ -34,7 +33,6 @@ public static void Create( string appHostSourceFilePath, string appHostDestinationFilePath, string appBinaryFilePath, - bool overwriteExisting = false, bool windowsGraphicalUserInterface = false, string intermediateAssembly = null, Logger log = null) @@ -55,7 +53,7 @@ public static void Create( } // Copy AppHostSourcePath to ModifiedAppHostPath so it inherits the same attributes\permissions. - File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwriteExisting); + File.Copy(appHostSourceFilePath, appHostDestinationFilePath, overwrite: true); // Re-write ModifiedAppHostPath with the proper contents. bool appHostIsPEImage = false; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs index 8aeb2205c3d2..6cf38a5215ef 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateAppHost.cs @@ -31,16 +31,13 @@ protected override void ExecuteCore() var hostExtension = Path.GetExtension(AppHostSourcePath); var appbaseName = Path.GetFileNameWithoutExtension(AppBinaryName); - if (!File.Exists(AppHostDestinationPath)) - { - AppHost.Create( - AppHostSourcePath, - AppHostDestinationPath, - AppBinaryName, - windowsGraphicalUserInterface : WindowsGraphicalUserInterface, - intermediateAssembly: IntermediateAssembly, - log: Log); - } + AppHost.Create( + AppHostSourcePath, + AppHostDestinationPath, + AppBinaryName, + windowsGraphicalUserInterface : WindowsGraphicalUserInterface, + intermediateAssembly: IntermediateAssembly, + log: Log); } } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateShims.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateShims.cs index 286666c1a860..43b691fd8d58 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateShims.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateShims.cs @@ -102,8 +102,7 @@ protected override void ExecuteCore() AppHost.Create( resolvedApphostAssetPath, appHostDestinationFilePath, - appBinaryFilePath, - overwriteExisting: true + appBinaryFilePath ); var item = new TaskItem(appHostDestinationFilePath); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets index 782ed5e178a0..aa5998286b98 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets @@ -283,6 +283,8 @@ Copyright (c) .NET Foundation. All rights reserved. --> From c3aba73f8a404f20038c94ba1d86275e280988a6 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 1 Oct 2018 10:39:00 -0700 Subject: [PATCH 28/28] Remove overwriteExisting option from tests --- .../GivenAnAppHost.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs index c64ca5843954..d2e7a05b4976 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs @@ -28,8 +28,7 @@ public void ItEmbedsAppBinaryPath() AppHost.Create( sourceAppHostMock, destinationFilePath, - appBinaryFilePath, - overwriteExisting: false); + appBinaryFilePath); byte[] binaryPathBlob = Encoding.UTF8.GetBytes(appBinaryFilePath); byte[] result = File.ReadAllBytes(destinationFilePath); @@ -63,8 +62,7 @@ public void ItFailsToEmbedAppBinaryIfHashIsWrong() AppHost.Create( sourceAppHostMock, destinationFilePath, - appBinaryFilePath, - overwriteExisting: false)) + appBinaryFilePath)) .Message .Should() .Contain(sourceAppHostMock) @@ -86,8 +84,7 @@ public void ItFailsToEmbedTooLongAppBinaryPath() AppHost.Create( sourceAppHostMock, destinationFilePath, - appBinaryFilePath, - overwriteExisting: false)) + appBinaryFilePath)) .Message .Should() .Contain(appBinaryFilePath); @@ -107,7 +104,6 @@ public void ItCanSetWindowsGUISubsystem() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, windowsGraphicalUserInterface: true); BitConverter @@ -136,7 +132,6 @@ public void ItFailsToSetGUISubsystemOnNonWindowsPEFile() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, windowsGraphicalUserInterface: true)) .Message .Should() @@ -162,7 +157,6 @@ public void ItFailsToSetGUISubsystemWithWrongDefault() sourceAppHostMock, destinationFilePath, appBinaryFilePath, - overwriteExisting: false, windowsGraphicalUserInterface: true)) .Message .Should()