Skip to content

Support application manifests on Windows #721

Open
@ColonelJ

Description

@ColonelJ

Currently Rust has no support for adding manifests to Windows executables (.exe and .dll) despite some previous failed attempts (see rust-lang/rust#11207).

Application manifests are essentially just an XML resource which is embedded in the executable and is read by Windows to establish how the executable should be run and which DLLs it depends on. There are also other types of manifests which follow the same basic schema but have slightly different purposes, e.g. you can publish configuration files to install on the system which specify that a particular version of a DLL installed is backwards compatible and should be used in place of a whole range of previous version numbers.

Manifests may be useful for several reasons in Rust:

  • Allows easily viewable version numbers and descriptive text on an .exe or .dll
  • Can specify which version of a system (or any other) .dll your application should use, thus avoiding DLL hell - notable alternative use case for this is to ensure version 6 or above of Microsoft Common Controls which will enable XP styles of window controls in 32-bit applications; this particular one probably ought to be automatically included for apps requiring COMCTL32.dll since without you'll get some ugly ancient looking GUIs.
  • Ability to specify UAC privilege level required for your application and avoid Windows determining this heuristically (temporarily solved for test runs using an environment variable in Prevent UAC blocking certain test executables on 32-bit Windows. rust#21496)
  • Allows use of code signing for distributed binaries (also needs a certificate). This is a must for writing Windows drivers nowadays.
  • Enable use of certain features/modes such as (super) high-res scrolling, DPI awareness, printer driver isolation...

There is another significant feature which should NOT be used by default, which is that you can specify which OS version behaviours are supported by your application, which effectively requests certain APIs to behave differently. The reason this is bad is that it is impossible to know if anything that plugs in to your app (e.g. shell extensions, which come in through e.g. common file dialog) also supports these behaviours, so it is generally not safe to turn them on. Even so, for flexibility it would be good to allow users to explicitly request these behaviours.

I believe that the scope and format of these application manifests is sufficiently limited (particularly parts most relevant to Rust) that the Rust ecosystem ought to have the ability to generate these by itself with the help of Rust language attributes to specify particular items/settings to go into the manifest. Having some sane approach to version numbering (which ties into Cargo somehow) would be massively beneficial and avoid all sorts of problems with incompatible binaries. The version numbers for manifests are required to have four parts 0-65535 (without exception) which Microsoft expliclty labels as major.minor.build.revision in the documentation, and changes in the last two are not to break backwards compatibility, which would generally be done by incrementing the major version rather than minor. This I find to be a good link on the motivations behind the versioning system on Windows and there might be no reason why Rust couldn't implement such automatic revision number stepping on top of a similar version attribute.

Likely people will want to override whatever manifest Rust provides (or disable it completely) and this should be catered for. However, it could potentially be useful to have an option for some sort of hybrid approach where Rust can change certain fields such as version number from an existing manifest file. N.B. application manifests are always called filename.exe.manifest or filename.dll.manifest so they could always simply be detected from the filesystem rather than specified on the command line or in an attribute (though being able to specify an alternative manifest path via an attribute may be another very useful feature).

As a demonstration of most of the things I've said, and to demonstrate how simple the XML is, I have prepared an example, which in this case would normally be called helloworld.exe.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="helloworld"
                    version="1.7.2.19"
                    processorArchitecture="x86"/>
  <description>Hello World Application</description>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32"
                        name="Acme.Soft.SayStuffLib"
                        version="3.7.2.4"
                        processorArchitecture="x86"
                        language="*"/>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32"
                        name="Microsoft.Windows.Common-Controls"
                        version="6.0.0.0"
                        processorArchitecture="x86"
                        publicKeyToken="6595b64144ccf1df"
                        language="*"/>
    </dependentAssembly>
  </dependency>
  <file name="hellointl.dll"
        hash="12345678123456781234567812345678abcdef00"
        hashalg="SHA1"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
        xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
      <dpiAware>true</dpiAware>
      <printerDriverIsolation>true</printerDriverIsolation>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

This shows providing version number and description of your app, referencing DLLs by assembly name and version which may be Rust built or on the system, referencing a private DLL which must match the expected file using SHA1 hash, activating XP styles by requesting version 6 of common controls, telling UAC that no special privileges are required (even if the app was called setup.exe) and indicating that your app is DPI aware and that the printer driver should run in a separate process (so it doesn't crash your app if it is faulty). If your app were code signed it would have a public key token as COMCTL32.dll does.

I think there's still a lot of work to do on reconciling differences with how Rust is currently and how these features could possibly be implemented, but I think once Rust (and Cargo too most likely) is capable of all the things above and can generate the manifest mostly by itself, things are looking pretty strong on Windows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-windowsProposals relating to Windows.T-cargoRelevant to the Cargo team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions