How to package a plugin that will work on Windows (Microsoft Store version)

Special actions need to be taken in order for plug-ins work on the Microsoft Store version of GIMP. Specifically, they should be packaged and codesigned. This is due to security restrictions and virtualization of MSIX packages that do not recommend a direct installation at %LOCALAPPDATA%\Packages\GIMP*\LocalCache\Roaming\GIMP, for example. From start, they needs:

  • AppxManifest.xml
  • Assets folder and resources.pri
  • Plug-in files in appropriate VFS folder

Configuring the ‘modification package’ manifest

The package that we will create isn’t a common one but a modification package, which should have the following AppxManifest.xml in the root folder:

<?xml version="1.0" encoding="utf-8"?>
<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
  xmlns:rescap6="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/6">
  <Identity Name="DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES" Publisher="CERT-INFO" Version="MAJOR.MINOR.MICRO.0" ProcessorArchitecture="MSIX-ARCH" />
  <Properties>
    <DisplayName>PLUG-IN-NAME</DisplayName>
    <PublisherDisplayName>DEV-NAME</PublisherDisplayName>
    <Description>PLUG-IN-DESC</Description>
    <Logo>Assets\StoreLogo.png</Logo>
    <rescap6:ModificationPackage>true</rescap6:ModificationPackage>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22621.0" />
    <uap4:MainPackageDependency Name="GIMP.GIMP" Publisher="CN=8705A20E-F3A1-463E-86D3-B71D4DE2E37D" />
  </Dependencies>
  <Resources>
    <Resource Language="en-US" />
    <Resource uap:Scale="100" />
    <Resource uap:Scale="125" />
    <Resource uap:Scale="150" />
    <Resource uap:Scale="200" />
    <Resource uap:Scale="400" />
  </Resources>
</Package>

Change MSIX-ARCH to arm64 if you support ARM 64-bit, x64 if x86 64-bit; and neutral if the plug-in doesn’t have binaries (eg. Lua, Python and Scheme). Them, set the PLUG-IN-NAME, DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES, PLUG-IN-DESC, MAJOR.MINOR.MICRO.0 and DEV-NAME according.

Finally, put the same info of your CA trusted certificate in CERT-INFO. Without a certificate, you can test locally putting , OID.2.25.311729368913984317654407730594956997722=1 right after an arbitrary certificate info, but the plug-in willn’t be able to be sideloaded by the users (as said before, codesigning will be really mandatory).

Generating the assets

The only asset needed by a modification package is the StoreLogo. And, for now, don’t be misleaded by the name: the plug-in (and any modification package) can not be deployed in Store, we will talk about an alternative below. Anyway, keep in mind that this file is needed for compliance. You can do the rendering of your icon to the following sizes:

Filename Size
StoreLogo.png 50px
StoreLogo.scale-125.png 65px (approx.)
StoreLogo.scale-150.png 75px
StoreLogo.scale-200.png 100px
StoreLogo.scale-400.png 200px

Create a folder named Assets in the root of the package folder and put the .pngs there. Then, we will be almost ready to generate the resources.pri.

From now, you need to install Windows SDK. Since the installer don’t configure any PATH, set C:\Program Files (x86)\Windows Kits\10\bin\NT_MAJOR.MINOR.MICRO.0\x64 and C:\Program Files (x86)\Windows Kits\10\App Certification Kit.

Continuing, in the package folder, let’s make a priconfig.xml:

makepri createconfig /cf priconfig.xml /dq lang-en-US /pv 10.0.0

Now, in the uplevel folder (this is due CLI limitations of makepri):

makepri new /pr PACKAGE-FOLDER /cf PACKAGE-FOLDER\priconfig.xml /of PACKAGE-FOLDER

Once the .pri file is generated, you should remove the priconfig.xml file.

Copying the files to appropiate VFS folder

To be properly recognized by GIMP the plug-in will rely in a Windows feature called Virtual File System. In package root folder, create the following: VFS\ProgramFilesX64\GIMP\lib\gimp\*\plug-ins\DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES. If it is a gegl OP, so: VFS\ProgramFilesX64\GIMP\lib\gegl-*\DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES. Copy the plug-in or OP files to DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES.

  • Don’t copy stripped .debug or other dead binaries to avoid signing issues.

Packaging and signing the .msix or .msixbundle

Assuming that you have a multiarch plug-in (the .msixbundle format is recommended for multiarch), package it this way:

makeappx pack /d PACKAGE-FOLDER /p _TempOutput\DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES_MAJOR.MINOR.MICRO.0_MSIX-ARCH.msix
makeappx bundle /bv MAJOR.MINOR.MICRO.0 /d _TempOutput /p _Output\DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES_MAJOR.MINOR.MICRO.0_neutral.msixbundle

Then sign the package:

SignTool sign /fd sha256 /a /f CERT-FILE.pfx /p CERT-PASSWORD _Output\*.msixbundle

Now, if necessary, test the packages installing them:

Add-AppxPackage -Path *.msixbundle
  • Use Add-AppPackage -Path *.msixbundle -AllowUnsigned if you used the OID tag so didn’t signed

Almost the entire process must be done for each supported architecture (in the case of plug-ins with binaries) so, to save your time in the future, we recommend mimic the GIMP Store distribution script. But in this tutorial we still go further:

Distributing and auto-updating with .appinstaller

Typically, your plugin may continue to get new features and bug fixes. And the easiest way to ensure this for the users is to create a simple .appinstaller file. It works similar to a .flatpakref file and guarantees auto update outside the Store. To do this, create a file with this extension and configure the following content:

<?xml version="1.0" encoding="utf-8"?>
<AppInstaller
    xmlns="http://schemas.microsoft.com/appx/appinstaller/2017/2"
    Version="MAJOR.MINOR.MICRO.0"
    Uri="http://mywebservice.net/DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES_MAJOR.MINOR.MICRO.0_neutral.appinstaller" >

    <MainBundle
        Name="PLUG-IN-NAME"
        Publisher="CERT-INFO"
        Version="MAJOR.MINOR.MICRO.0"
        Uri="http://mywebservice.net/DEV-NAME.PLUG-IN-NAME-WITHOUT-SYMBOLS-OR-SPACES_MAJOR.MINOR.MICRO.0_neutral.msixbundle" />

    <UpdateSettings>
        <OnLaunch HoursBetweenUpdateChecks="12"/>   
    </UpdateSettings>

</AppInstaller>

You can test your .appinstaller with:

Add-AppxPackage -Path *.appinstaller -Appinstaller

You also may configure your web server for this. More information at Microsoft Docs.