| File: ManifestUtil\EmbeddedManifestReader.cs | Web Access |
| Project: src\msbuild\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.IO; #if FEATURE_WINDOWSINTEROP using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.LibraryLoader; #endif #nullable disable namespace Microsoft.Build.Tasks.Deployment.ManifestUtilities { internal class EmbeddedManifestReader { #if FEATURE_WINDOWSINTEROP private Stream _manifest; // The Win32 RT_MANIFEST resource type and the application-manifest resource id (1). private const ushort RT_MANIFEST = 24; private const nint Id1 = 1; [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate BOOL EnumResNameDelegate(HMODULE hModule, PCWSTR lpType, PWSTR lpName, nint lParam); private unsafe EmbeddedManifestReader(string path) { if (!NativeMethodsShared.IsWindows) { return; } HMODULE hModule = default; try { hModule = PInvoke.LoadLibraryEx(path, LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); if (hModule.IsNull) { return; } // EnumResourceNames takes an unmanaged stdcall callback. Bridge a managed delegate to a // function pointer (works on both .NET Framework and .NET Core) and keep it alive across the call. EnumResNameDelegate callback = EnumResNameCallback; var pCallback = (delegate* unmanaged[Stdcall]<HMODULE, PCWSTR, PWSTR, nint, BOOL>)Marshal.GetFunctionPointerForDelegate(callback); PInvoke.EnumResourceNames(hModule, new PCWSTR((char*)RT_MANIFEST), pCallback, 0); GC.KeepAlive(callback); } finally { if (!hModule.IsNull) { PInvoke.FreeLibrary(hModule); } } } [SupportedOSPlatform("windows6.1")] private unsafe BOOL EnumResNameCallback(HMODULE hModule, PCWSTR lpType, PWSTR lpName, nint lParam) { if ((nint)lpName.Value != Id1) { return false; // only look for resources with ID=1 } HRSRC hResInfo = PInvoke.FindResource(hModule, lpName, (PCWSTR)(char*)RT_MANIFEST); if (hResInfo.IsNull) { return false; // continue looking } HGLOBAL hResource = PInvoke.LoadResource(hModule, hResInfo); void* pData = PInvoke.LockResource(hResource); uint bufsize = PInvoke.SizeofResource(hModule, hResInfo); var buffer = new byte[bufsize]; Marshal.Copy((IntPtr)pData, buffer, 0, buffer.Length); _manifest = new MemoryStream(buffer, false); return false; // found what we are looking for } public static Stream Read(string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (!path.EndsWith(".manifest", StringComparison.Ordinal) && !path.EndsWith(".dll", StringComparison.Ordinal)) { // Everything that does not end with .dll or .manifest is not a valid native assembly (this includes // EXEs with ID1 manifest) return null; } int t1 = Environment.TickCount; EmbeddedManifestReader r = new EmbeddedManifestReader(path); Util.WriteLog($"EmbeddedManifestReader.Read t={Environment.TickCount - t1}"); return r._manifest; } #else // Reading embedded Win32 manifest resources requires Windows interop, which is unavailable in // source build (no CsWin32-generated PInvoke surface), so there is nothing to read. public static Stream Read(string path) => null; #endif } }