Support for MSIx

Hello

Using Software distribution, and was wondering is it supports MSIx packages. MSIx is a new Windows applications format.

I’m not sure whether the SWD Dashboard will build the command-line for you, but basically, yes, SWD can execute any command-line you give it.

The default for MSIX is to install only for the current user (in which case you’d need to add relevance to check for a logged-on user, and use the ‘override’ ActionScript commands to execute in the user’s context).

If you want to install the msix package for all users, you’d need to use the PowerShell cmdlet Add-AppxProvisionedPackage

Below is a generalized snippet of some PowerShell that we’ve used to install more complex MSIX files via BigFix provisioned at the System level then installed for each user’s log-on.

I haven’t validated the script is fully functional after removing some of our proprietary libraries/functions/calls but it should be close enough to get someone started if there are errors present.
YMMV & this is provided with no warranty/support so proceed at your own risk. :slight_smile:

#Sample stub PS1 installing an MSIX with or without app dependencies 

#Function:  CheckProcessorArch
#Usage:     CheckProcessorArch  
#Purpose:   Returns the processor architecture of the device.  This data may be useful for installing MSIX architecture specific dependencies among other uses.
function CheckProcessorArch
{
    Write-host "Starting CheckProcessorArch..."
    
    
    $IMAGE_FILE_MACHINE_i386  = 0x014c
    $IMAGE_FILE_MACHINE_AMD64 = 0x8664
    $IMAGE_FILE_MACHINE_ARM64 = 0xAA64
    $IMAGE_FILE_MACHINE_ARM   = 0x01c0
    $IMAGE_FILE_MACHINE_THUMB = 0x01c2
    $IMAGE_FILE_MACHINE_ARMNT = 0x01c4
    
    $MACHINE_ATTRIBUTES_UserEnabled    = 0x01
    $MACHINE_ATTRIBUTES_KernelEnabled  = 0x02
    $MACHINE_ATTRIBUTES_Wow64Container = 0x04

    # First try to use IsWow64Process2 to determine the OS architecture
    try
    {
        $IsWow64Process2_MethodDefinition = @"
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool IsWow64Process2(IntPtr process, out ushort processMachine, out ushort nativeMachine);
"@

        $Kernel32 = Add-Type -MemberDefinition $IsWow64Process2_MethodDefinition -Name "Kernel32" -Namespace "Win32" -PassThru

        $Proc = Get-Process -id $pid
        $processMachine = New-Object uint16
        $nativeMachine = New-Object uint16

        $Res = $Kernel32::IsWow64Process2($Proc.Handle, [ref] $processMachine, [ref] $nativeMachine)
        if ($Res -eq $True)
        {
            switch ($nativeMachine)
            {
                $IMAGE_FILE_MACHINE_i386  { $global:ProcessorArchitecture = "x86" }
                $IMAGE_FILE_MACHINE_AMD64 { $global:ProcessorArchitecture = "amd64" }
                $IMAGE_FILE_MACHINE_ARM64 { $global:ProcessorArchitecture = "arm64" }
                $IMAGE_FILE_MACHINE_ARM   { $global:ProcessorArchitecture = "arm" }
                $IMAGE_FILE_MACHINE_THUMB { $global:ProcessorArchitecture = "arm" }
                $IMAGE_FILE_MACHINE_ARMNT { $global:ProcessorArchitecture = "arm" }
            }
        }
        Write-host " Processor architecture identified as: $ProcessorArchitecture"
    }
    catch
    {
        # Ignore exception and fall back to using environment variables to determine the OS architecture
        Write-host " Fall back to using ENV to determine architecture"
    }

    
    $global:Amd64AppsSupportedOnArm64 = $False
    if ($null -eq $ProcessorArchitecture)
    {
        $global:ProcessorArchitecture = $Env:Processor_Architecture
    
        # Getting $Env:Processor_Architecture on arm64 machines will return x86.  So check if the environment
        # variable "ProgramFiles(Arm)" is also set, if it is we know the actual processor architecture is arm64.
        # The value will also be x86 on amd64 machines when running the x86 version of PowerShell.
        if ($ProcessorArchitecture -eq "x86")
        {
            if ($null -ne ${Env:ProgramFiles(Arm)})
            {
                $global:ProcessorArchitecture = "arm64"
            }
            elseif ($null -ne ${Env:ProgramFiles(x86)})
            {
                $global:ProcessorArchitecture = "amd64"
            }
        }
        Write-host " Processor architecture identified as: $ProcessorArchitecture"
    }
    elseif ("arm64" -eq $ProcessorArchitecture)
    {
        # If we successfully determined the OS to be arm64 with the above call to IsWow64Process2 and we're on Win11+
        # we need to check for a special case where amd64 apps are supported as well.
        if ([Environment]::OSVersion.Version -ge (new-object 'Version' 10,0,22000))
        {
            try
            {
                $GetMachineTypeAttributes_MethodDefinition = @"
[DllImport("api-ms-win-core-processthreads-l1-1-7.dll", EntryPoint = "GetMachineTypeAttributes", ExactSpelling = true, PreserveSig = false)]
public static extern void GetMachineTypeAttributes(ushort Machine, out ushort machineTypeAttributes);
"@
    
                $ProcessThreads = Add-Type -MemberDefinition $GetMachineTypeAttributes_MethodDefinition -Name "processthreads_l1_1_7" -Namespace "Win32" -PassThru
                $machineTypeAttributes = New-Object uint16
    
                $ProcessThreads::GetMachineTypeAttributes($IMAGE_FILE_MACHINE_AMD64, [ref] $machineTypeAttributes)
    
                # We're looking for the case where the UserEnabled flag is set
                if (($machineTypeAttributes -band $MACHINE_ATTRIBUTES_UserEnabled) -ne 0)
                {
                    $global:Amd64AppsSupportedOnArm64 = $True
                    Write-host "Processor architecture supports amd64 apps"
                }
            }
            catch
            {
                # Ignore exceptions and maintain assumption that amd64 apps aren't supported on this machine
                Write-host "Processor architecture does not support amd64 apps"
            }
        }
    }

    Write-host "Finished CheckProcessorArch." 
}


#####################################################
##MAIN

CheckProcessorArch
#Provide the name of the folder the MSIX will be staged in (as written must be a sub-folder of c:\programdata)
$AppName = "myApp"
#Provide the name of the MSIX file without a file extension here:
$appfile = "myApp_1.0"


try 
{
	#Identify dependencies for the application 
	Write-host  "Begin identification of dependencies...." 
	
    CheckProcessorArch
    
    #Any Appx or MSIX dependencies should be aligned into the appropriate folders.  
    #The root dependency directory for any non-Architecture specific ones
    #Alternatively if you have files that are platform architecture specific use the following sub-folders as appropriate:
    #  x86, x64, arm, arm64
    $DependencyPackagesDir = "C:\Programdata\$AppName\Dependencies"
    $DependencyPackages = @()
    if (Test-Path $DependencyPackagesDir)
    {
        # Get architecture-neutral dependencies
        Write-host  "    Identify Dependencies : Architecture neutral"
        $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" }
        $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "*.msix") | Where-Object { $_.Mode -NotMatch "d" }
        Write-host  "    Dependencies identified : Architecture neutral"

        Write-host " Check for dependencies for architecture: $ProcessorArchitecture"
        # Get architecture-specific dependencies
        if (($ProcessorArchitecture -eq "x86" -or $ProcessorArchitecture -eq "amd64" -or $ProcessorArchitecture -eq "arm64") -and (Test-Path (Join-Path $DependencyPackagesDir "x86")))
        {
            Write-host  "    Identify Dependencies : Architecture x86"
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x86\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x86\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
            Write-host  "    Dependencies identified : Architecture x86"
        }
        if ((($ProcessorArchitecture -eq "amd64") -or ($ProcessorArchitecture -eq "arm64" -and $Amd64AppsSupportedOnArm64)) -and (Test-Path (Join-Path $DependencyPackagesDir "x64")))
        {
            Write-host  "    Identify Dependencies : Architecture x64"
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x64\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x64\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
            Write-host  "    Dependencies identified : Architecture x64"
        }
        if (($ProcessorArchitecture -eq "arm" -or $ProcessorArchitecture -eq "arm64") -and (Test-Path (Join-Path $DependencyPackagesDir "arm")))
        {
            Write-host  "    Identify Dependencies : Architecture arm"
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
            Write-host  "    Dependencies identified : Architecture arm"
        }
        if (($ProcessorArchitecture -eq "arm64") -and (Test-Path (Join-Path $DependencyPackagesDir "arm64")))
        {
            Write-host  "    Identify Dependencies : Architecture arm64"
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm64\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
            $DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm64\*.msix") | Where-Object { $_.Mode -NotMatch "d" }
            Write-host  "    Dependencies identified : Architecture arm64"
        }
    }
    Write-host "$AppName dependencies have been identified." 	
}
catch 
{
	Write-host "$AppName dependencies have NOT been identified." 
}	

try 
{
	#Install for all users 
	Write-host  "Begin provisioning of $AppName"

    if ($DependencyPackages.FullName.Count -gt 0)
    {
        Write-host "Found [$($DependencyPackages.FullName.Count)] dependencies to include"
        $DependencyPackagesCSV = $DependencyPackages.FullName -join ","
        Write-host "Command to execute: [Add-AppProvisionedPackage -online -PackagePath `"C:\Programdata\$AppName\$appfile.msix`" -DependencyPackagePath $DependencyPackages.FullName -SkipLicense]"
	    $null = Add-AppProvisionedPackage -online -PackagePath "C:\Programdata\$AppName\$appfile.msix" -DependencyPackagePath $DependencyPackages.FullName -SkipLicense
    }
    else
    {
        Write-host "No dependencies found to include"
        Write-host "Command to execute: [Add-AppProvisionedPackage -online -PackagePath `"C:\Programdata\$AppName\$appfile.msix`" -SkipLicense]"
        $null = Add-AppProvisionedPackage -online -PackagePath "C:\Programdata\$AppName\$appfile.msix" -SkipLicense
    
    }
    Write-host "$AppName has been Provisioned." 
}
catch 
{
	Write-host "$AppName has NOT been provisioned."
    exit -1
}	

exit 0
3 Likes