MSIX - Create Desktop Shortcuts with Package Support Framework and PowerShell
Published May 02 2022 04:19 PM 9,132 Views
Microsoft

desktopShortcuts.png

Introduction

This post will help you add desktop shortcuts to your MSIX packaged applications. Currently there is no built-in support in the MSIX Packaging Tool to carry over desktop shortcuts you may have defined in your MSI or setup.exe.

The approach here will use a PowerShell script (CreateShortcuts.ps1) to create shortcuts on the users desktop when the app is run for the first time. It requires updating the Appxmanifest.xml file and adding Package Support Framework (PSF) support to the MSIX package. It does not require any existing shortcut (.lnk) files or applications icons (.ico).

Prerequisites

You'll need the following:

  1. CreateShortcuts.ps1. Clone this repository: https://github.com/mjfusa/msix-create-desktop-shortcuts. The script is in the ScriptInPackage folder.
  2. An icon embedded in your application's EXE. This is typical for a Win32 desktop app. This will be used to create the shortcut icon.
  3. Package Support Framework binaries. Here are the steps to download:

 

 

 

# PowerShell - Download PsdBinaries 2.0 into the PsfBinaries folder
md PsfBinaires
cd PsfBinaires
wget https://github.com/microsoft/MSIX-PackageSupportFramework/releases/download/v2.0/PSFBinaries.zip -OutFile PSFBinaries.zip
Expand-Archive .\PSFBinaries.zip

 

 

 

  1. MSIX Packaging Tool. Install from the Microsoft Store here.
  2. An MSIX Packaged app.

Preparing your MSIX

Create Icons, AppxmanifestNew.xml and config.json.

You will need to start with an existing MSIX packaged app.

  1. Extract the contents of your MSIX package to a temporary directory. You can use the MSIX Packaging Tool to do this.

  2. Change to the temporary directory to where you unpackaged the application.

  3. Run the script CreateManifestAndConfigForPSF.ps1 in the same folder where you have saved the extracted the application.

  4. After running the script, the icon(s) are extracted, converted to ico files and saved to the 'Icons' folder. Also AppxmanifestNew.xml and config.json are created. config.json is required by the Package Support Framework. You will replace the existing manifest with the contents of AppxmanifestNew.xml.

Update your MSIX Package with the MSIX Packaging Tool

Update the manifest

Open your MSIX package for editing in the MSIX Packaging tool (MPT)

mpt1.png

  1. In the 'Manifest file' section click on 'Open File'. The manifest will be opened into Notepad for editing.

  2. Open AppmanifestNew.xml created in the section above in another editor and copy the entire contents to the clipboard.

  3. Go back Notepad. Delete all of the contents and paste in the contents you copied in the previous step.

  4. Notepad should now have the contents of AppmanifestNew.xml. Save the file and close notepad.

  5. Verify in MPT that you have no errors in the 'Manifest file' section.

  6. See the How it works section for information on what is changed in the manifest.

Add config.json

  1. Click on 'Package files' in MPT

  2. Right click on Package

  3. Click Add File and add the config.json file that was created in the Create Icons, AppxmanifestNew.xml and config section above.

Add the Icons

  1. Click on 'Package files' in MPT

  2. Right click on Package

  3. Click on New folder. Type Icons. Click Save

  4. Right click on Icons

  5. Click Add File and add the ico files that were created in the section Create Icons, AppxmanifestNew.xml and config above.

Add the Package Support Framework (PSF) Binaries and CreateShortcuts.ps1 script

Note: For this step you will need the PSF Binaries and the CreateShortcuts.ps1 script included in the GitHub repository. See Prerequisites.

  1. Go back to MPT

  2. Click on 'Package files' in MPT

  3. Right click on Package

  4. Click 'Add File' and add the following files from the PSFBinaries\bin folder:

 

 

 

PsfLauncher32.exe
PsfLauncher64.exe
PsfRunDll32.exe
PsfRunDll64.exe
PsfRuntime32.dll
PsfRuntime64.dll
StartingScriptWrapper.ps1

 

 

 

The StartingScriptWrapper.ps1 is required to run PowerShell scripts from PSF.

Add the script CreateShortcuts.ps1

  1. Go back to MPT
  2. Click on 'Package files' in MPT
  3. Right click on Package
  4. Click 'Add File' and add the file CreateShortcuts.ps1.The script is in the ScriptInPackage folder in the clone repository. See prerequisties.

After adding the Icons, CreateShortcuts.ps1, PsfBinaries and StartingScriptWrapper.ps1, the Package files section in MPT should look like this:

addosfBinaries.png

Save the MSIX package

You have added all of the necessary files and can now save and sign the MSIX.

  1. Go back to MPT
  2. Provide the code signing certificate.
  3. Click Save and click Yes to increment the version number.
  4. The MSIX is updated with create shortcut support, signed and ready for testing.

How it works

Creating the Icons, AppxmanifestNew.xml and Config.json (CreateManifestAndConfigForPSF.ps1)

Appxmanifest.xml

CreateManifestAndConfigForPSF.ps1 scans the manifest file for the App Ids, display name and app description for displaying in the shortcut. For example:

 

 

 

<Application Id="App" . . . >
    <uap:VisualElements DisplayName="Hello MSIX World" Description="This app displays Hello World and the app version" . . . />
</uap:VisualElements>

 

 

 

The script will skip the applications that have the attribute AppListEntry="none" as these are not displayed in the Start menu.

The following changes are made to the manifest file:

  1. The Executable attribute is replaced with the PsfLauncher.exe that matches the architecture of the app. For example:

 

 

 

<Application Id="App" Executable="HelloWpf\HelloWpf.exe" EntryPoint="Windows.FullTrustApplication">

 

 

 

is replaced with:

 

 

 

<Application Id="App" Executable="psflauncher64.exe" EntryPoint="Windows.FullTrustApplication">

 

 

 

The app architecture is determined by the ProcessorArchitecture setting in the Identity node of the manifest. For example:

<Identity Name="Hello.MSIX.World" Publisher="CN=@MikeFrancis" Version="1.0.0.0" ProcessorArchitecture="x64" />

Shortcuts work by providing a path to an executable's EXE. Since the path to an MSIX packaged EXE is not accessible, we need to define an alias which the shortcut will use to launch the app.

The script will define an alias in the Appmanifest.xml. For example:

 

 

 

<Extensions>
  <uap3:Extension Category="windows.appExecutionAlias" Executable="HelloWpf\HelloWpf.exe" EntryPoint="Windows.FullTrustApplication">
   <uap3:AppExecutionAlias>
    <desktop:ExecutionAlias Alias="hellowpf.exe" />
   </uap3:AppExecutionAlias>
  </uap3:Extension>
</Extensions>
<!-- The app will launch when 'hellowpf.exe' is entered on the command line - as if it were in the users PATH -->

 

 

 

The script will also include the additional schema references (uap3 and desktop) as needed by the AppExecutionAlias.

Config.json

CreateManifestAndConfigForPSF.ps1 collected the Executable paths and App Ids from the Appxmanifest.xml. These values are used to create the config.json file.

Icons

The ico files are copied from the Images folder (see section Icons) to the $env:APPDATA folder. The shortcut references the icon from this folder.

Creating the desktop shortcuts at first time app launch (CreateShortcuts.ps1)

When the user clicks on the app icon in the Start menu, PsfLauncher*.exe starts. It loads config.json which contains the script to run - in this case CreateShortcuts.ps1. After the script runs, the app HelloWpf\HelloWpf.exe is launched. The script will only run when the app is launched the first time. See here for the full list of config.json settings:

 

 

 

config.json
{
 "applications": [ 
   { "startScript": {
        "scriptPath": "CreateShortcuts.ps1"
     },
     "arguments": "",
     "id": "App",
     "executable": "HelloWpf\\HelloWpf.exe"
  }
 ]
}

 

 

 

The shortcuts are created calling the Windows Script Host class methods to create the shortcuts. Here is the relevant code in CreateShortcuts.ps1:

 

 

 

$WshShell = New-Object -comObject WScript.Shell
$lnkPath = ($destinationDesktopPath + "\" + $app.DisplayName + ".lnk")
$Shortcut = $WshShell.CreateShortcut($lnkPath)
$Shortcut.TargetPath = $(Split-Path $app.Executable -Leaf)
$Shortcut.IconLocation = ($env:APPDATA + "\" + $app.DisplayName + ".ico")
$Shortcut.WorkingDirectory = ($ENV:LOCALAPPDATA + "\Microsoft\WindowsApps")
$Shortcut.Description = ($app.Description)
$Shortcut.Save()

 

 

 

Note that $Shortcut.IconLocation requires an .ico file. This is created by extracting the icon from the EXE and then bitmap and conversion Win32 APIs are used to create the .ico file. The icons are created by the CreateManifestAndConfigForPSF.ps1 script - see the section 'Create Icons, AppxmanifestNew.xml and config.json' above. 

The key functions are:

ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int am...

System.Drawing.Bitmap.Save(mem_data, System.Drawing.Imaging.ImageFormat.Png);

This uses the feature of PowerShell that can interpret C# code! Credit: Crissy LeMaire for the C# / PowerShell Icon Extraction code.

10 Comments
Co-Authors
Version history
Last update:
‎Aug 15 2022 08:42 AM
Updated by: