Reading Windows Application Manifest of an exe in .NET
The application manifest file is a compatibility feature that allows Windows to run older applications on newer versions of Windows. It contains information such as the supported versions of Windows, if it should run as administrator, if it supports long paths, etc.
This file is stored as an embedded resource in the exe file. .NET doesn't provide any built-in way to read this file. You need to use interop to extract the resource file from the native resources.
Let's add a reference to the NuGet package Microsoft.Windows.CsWin32
:
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.1.635-beta">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Then, add a file named NativeMethods.txt
at the root of the project with the list of native methods and constants needed to read the application manifest file:
LoadLibraryEx
FreeLibrary
FindResource
LoadResource
SizeofResource
LockResource
FreeResource
LOAD_LIBRARY_FLAGS
RT_MANIFEST
You should see the generated code in Visual Studio:
The generated code is not perfect for the FindResource
method, so you can create a new overload:
namespace Windows.Win32;
internal static partial class PInvoke
{
[DllImport("Kernel32", ExactSpelling = true, EntryPoint = "FindResourceW")]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern Windows.Win32.Foundation.HRSRC FindResource(FreeLibrarySafeHandle hModule, uint lpName, uint lpType);
}
Finally, you can create the LoadManifest
method:
unsafe XDocument? LoadManifest(string exePath)
{
using var module = Windows.Win32.PInvoke.LoadLibraryEx(exePath, hFile: null, Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE);
if (module == null)
return null;
var resourceInfo = Windows.Win32.PInvoke.FindResource(module, 1u, Windows.Win32.PInvoke.RT_MANIFEST);
if (resourceInfo == default)
return null;
var dataSize = Windows.Win32.PInvoke.SizeofResource(module, resourceInfo);
if (dataSize == default)
return null;
var data = Windows.Win32.PInvoke.LoadResource(module, resourceInfo);
if (data == default)
return null;
try
{
var dataContent = Windows.Win32.PInvoke.LockResource(data);
var dataSpan = new Span<byte>(dataContent, (int)dataSize);
using var ms = new MemoryStream(dataSpan.ToArray());
return XDocument.Load(ms);
}
finally
{
Windows.Win32.PInvoke.FreeResource(data);
}
}
You can now use the method to read the manifest file of an exe file:
var assemblyLocation = Assembly.GetEntryAssembly().Location;
var manifest = LoadManifest(assemblyLocation);
var xname1 = XNamespace.Get("urn:schemas-microsoft-com:asm.v1");
var xname2 = XNamespace.Get("urn:schemas-microsoft-com:asm.v2");
var xname3 = XNamespace.Get("urn:schemas-microsoft-com:asm.v3");
var requestedExecutionLevel = manifest
?.Element(xname1 + "assembly")
?.Element(xname2 + "trustInfo")
?.Element(xname2 + "security")
?.Element(xname3 + "requestedPrivileges")
?.Element(xname3 + "requestedExecutionLevel")
?.Attribute("level");
Console.WriteLine(requestedExecutionLevel);
#Additional resources
- Source code of the post
- C#/Win32 P/Invoke Source Generator
- Manifest Files Reference
- Manifest File Schema
Do you have a question or a suggestion about this post? Contact me!