AlkanePSF.psm1
Add-Type -AssemblyName System.IO.Compression.FileSystem $script:applicationsModel = @() $script:processesModel = @() $script:capabilitiesArray = @() $script:dependenciesModel = @() $script:protocolsModel = @() $script:stringReplaceModel = @() $script:scriptArray = @() $script:removeApplicationArray = @() $script:removeShortcutArray = @() $script:alkanePSFConfigured = $false $script:MSIXInputFilePath = "" $script:MSIXOutputFilePath = "" $script:MSIXStagingFolderPath = "" $script:MSIXCertificateFilePath = "" $script:MSIXCertificatePassword = "" $script:MSIXArchitecture = "" $script:TimManganZipUrl = "" $script:PSFType = "" $fileRedirectionDllName = "FileRedirectionFixup.dll" $regLegacyDllName = "RegLegacyFixups.dll" $envVarDllName = "EnvVarFixup.dll" $dynamicLibraryDllName = "DynamicLibraryFixup.dll" $mfrDllName = "MFRFixup.dll" $traceDllName = "TraceFixup.dll" function Write-AlkanePSFOutput { Param($message) if ($message -like "*error*") { Write-Host $message -ForegroundColor Red } elseif ($message -like "*warning*") { Write-Host $message -ForegroundColor DarkYellow } else { Write-Host $message } } #******************************************* #******************************************** #function to configure AlkanePSF #******************************************* #******************************************** function Set-AlkanePSFConfiguration { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [string]$MSIXInputFilePath, [string]$MSIXOutputFilePath, [string]$MSIXStagingFolderPath, [string]$MSIXCertificateFilePath, [securestring]$MSIXCertificatePassword, [ValidateSet("32","64")] [string]$MSIXArchitecture, [ValidateSet("MS","TM")] [string]$PSFType="MS", [string]$TimManganZipUrl ) if($PSCmdlet.ShouldProcess("Should process?")){ if ($null -eq $MSIXInputFilePath -or $MSIXInputFilePath -eq "" -or (!(test-path $MSIXInputFilePath))) { Write-AlkanePSFOutput "**ERROR** Could not find input MSIX path." } $script:MSIXInputFilePath = $MSIXInputFilePath $script:MSIXOutputFilePath = $MSIXOutputFilePath if ($MSIXStagingFolderPath -notlike "*\") { $MSIXStagingFolderPath = "$MSIXStagingFolderPath\" } $script:MSIXStagingFolderPath = $MSIXStagingFolderPath if ($null -eq $MSIXCertificateFilePath -or $MSIXCertificateFilePath -eq "" -or (!(test-path $MSIXCertificateFilePath))) { Write-AlkanePSFOutput "**ERROR** Could not find certificate path." } $script:MSIXCertificateFilePath = $MSIXCertificateFilePath $pword = [System.Net.NetworkCredential]::new("", $MSIXCertificatePassword).Password if ($null -eq $pword -or $pword -eq "") { Write-AlkanePSFOutput "**ERROR** Certificate password cannot be empty." } $script:MSIXCertificatePassword = $pword $script:MSIXArchitecture = $MSIXArchitecture if ($null -eq $TimManganZipUrl -or $TimManganZipUrl -eq "" -or $TimManganZipUrl -notlike "https://github.com/TimMangan/MSIX-PackageSupportFramework/blob/develop/*") { Write-AlkanePSFOutput "**ERROR** Tim Mangan PSF url should be similar to (change version as required) https://github.com/TimMangan/MSIX-PackageSupportFramework/blob/develop/ZipRelease.zip-v2024-10-26.zip." } $script:TimManganZipUrl = $TimManganZipUrl $script:PSFType = $PSFType Write-AlkanePSFOutput "AlkanePSF configured." $script:alkanePSFConfigured = $true } } #******************************************* #******************************************** #function to start process, return exit code and output command line #******************************************* #******************************************** function Start-AlkanePSFProcess { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [string]$Exe, [array]$ExeArgs ) if($PSCmdlet.ShouldProcess("Should process?")){ $fullcommand = @($Exe,$ExeArgs) # Process each element in the array and add quotes if it contains spaces $quotedArgs = $fullcommand | ForEach-Object { if ($_ -match '\s') { "`"$_`"" # Add quotes around the argument if it contains spaces } else { $_ # Leave the argument as is if it doesn't contain spaces } } # Join the arguments into a single string $joinedArgs = $quotedArgs -join " " Write-AlkanePSFOutput "Command: $joinedArgs" #run command return (Start-Process -FilePath $Exe -ArgumentList $ExeArgs -Wait -Passthru).ExitCode } } #******************************************* #******************************************** #function to remove directories with long paths #******************************************* #******************************************** function Remove-LongPathDirectory { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [string]$FullPath ) if($PSCmdlet.ShouldProcess("Should process?")){ & cmd /c rmdir "$FullPath" /s /q } } #******************************************* #******************************************** #function to find an exe in the Windows SDK #******************************************* #******************************************** function Get-WindowsSDKExe { Param( [string]$ExeName ) try { $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\10\Bin\" if (!(test-path $sdkPath)) { Write-AlkanePSFOutput "Could not find $sdkPath" return "" } if($env:PROCESSOR_ARCHITECTURE -eq "x86") { $pathToExe = (Get-ChildItem $sdkPath -recurse -include $ExeName -ErrorAction SilentlyContinue | Where-Object FullName -like "*\x86\*" | Select-Object -First 1 -ExpandProperty FullName) } else { $pathToExe = (Get-ChildItem $sdkPath -recurse -include $ExeName -ErrorAction SilentlyContinue | Where-Object FullName -like "*\x64\*" | Select-Object -First 1 -ExpandProperty FullName) } return $pathToExe } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************** #******************************************** #function to install PSF prereqs #******************************************** #******************************************** function Install-AlkanePSFPrerequisite() { Param( [bool]$ForceReinstall=$false ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { #installs #https://developer.microsoft.com/en-gb/windows/downloads/windows-sdk/ #with #Windows SDK Signing Tools for Desktop Apps #Windows SDK for UWP Managed Apps #Windows SDK for UWP C++ Apps #Windows SDK for UWP Apps Localization $makeAppxPath = Get-WindowsSDKExe -ExeName "makeappx.exe" if (($null -eq $makeAppxPath -or $makeAppxPath -eq "") -or $ForceReinstall) { Write-AlkanePSFOutput "Installing Windows SDK tools for MSIX." $downloadExePath = "$env:temp\wdksetup.exe" $url = "https://go.microsoft.com/fwlink/?linkid=2083338" if (test-path $downloadExePath) { #remove before downloading Write-AlkanePSFOutput "Removing $downloadExePath." remove-item -Path $downloadExePath -Force -Recurse -ErrorAction SilentlyContinue } Write-AlkanePSFOutput "Downloading Windows SDK from $url." (New-Object Net.WebClient).DownloadFile($url, $downloadExePath) if (test-path $downloadExePath) { Write-AlkanePSFOutput "Installing Windows SDK." $processArgs = @("/features","OptionId.SigningTools","OptionId.UWPManaged","OptionId.UWPCPP","OptionId.UWPLocalized","/quiet","/norestart") $exitcode = (Start-AlkanePSFProcess -Exe $downloadExePath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Windows SDK installed with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Windows SDK installed with exit code $exitcode." } #remove download Write-AlkanePSFOutput "Removing $downloadExePath." Remove-Item $downloadExePath -Force -Recurse -ErrorAction SilentlyContinue } else { Write-AlkanePSFOutput "**WARNING** Could not download Windows SDK from $url" } } else { Write-AlkanePSFOutput "Windows SDK already installed." } $nupkg = Get-Package | Where-Object Name -eq "Microsoft.PackageSupportFramework" | Select-Object -ExpandProperty Source if (($null -eq $nupkg) -or $ForceReinstall) { Write-AlkanePSFOutput "Installing Microsoft's Package Support Framework." $nuget = get-packagesource | Where-Object ProviderName -eq "Nuget" if ($null -eq $nuget) { Register-PackageSource -Name nuget.org -Location https://www.nuget.org/api/v2 -ProviderName NuGet -Trusted Install-Package -Name Microsoft.PackageSupportFramework -ProviderName Nuget -Force Write-AlkanePSFOutput "Microsoft's Package Support Framework installed." } else { $package = Get-Package | Where-Object Name -eq "Microsoft.PackageSupportFramework" if ($null -eq $package) { Install-Package -Name Microsoft.PackageSupportFramework -ProviderName Nuget -Force Write-AlkanePSFOutput "Microsoft's Package Support Framework installed." } else { if ($ForceReinstall) { Install-Package -Name Microsoft.PackageSupportFramework -ProviderName Nuget -Force Write-AlkanePSFOutput "Microsoft's Package Support Framework installed." } } } } else { Write-AlkanePSFOutput "Microsoft's Package Support Framework already installed." } $tmPsfLocation = "$env:temp\TMPSF" if ((!(test-path $tmPsfLocation)) -or $ForceReinstall) { Write-AlkanePSFOutput "Installing Tim Mangan's Package Support Framework." $extractFolder = "$env:temp\TMPSF" $downloadZipPath = "$env:temp\TMPSF.zip" if (test-path $downloadZipPath) { #remove before downloading Write-AlkanePSFOutput "Removing $downloadZipPath." remove-item -Path $downloadZipPath -Force -Recurse -ErrorAction SilentlyContinue } if (test-path $extractFolder) { #remove before extracting Write-AlkanePSFOutput "Removing $extractFolder." Remove-LongPathDirectory $extractFolder } Write-AlkanePSFOutput "Downloading Tim Mangan's PSF from $script:TimManganZipUrl." #download zip (New-Object Net.WebClient).DownloadFile($script:TimManganZipUrl, $downloadZipPath) #if zip downloaded if (test-path $downloadZipPath) { #extract it [IO.Compression.Zipfile]::ExtractToDirectory($downloadZipPath,$extractFolder); #if extracted ok, extract the release folder if (test-path "$extractFolder\ReleasePsf.zip") { [IO.Compression.Zipfile]::ExtractToDirectory("$extractFolder\ReleasePsf.zip",$extractFolder); Write-AlkanePSFOutput "Tim Mangen's Package Support Framework installed." } #remove download Write-AlkanePSFOutput "Removing $downloadZipPath." Remove-Item $downloadZipPath -Force -Recurse -ErrorAction SilentlyContinue } } else { Write-AlkanePSFOutput "Tim Mangan's Package Support Framework already installed." } Write-AlkanePSFOutput "Finished installing prerequisites." } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************** #******************************************** #function to extract/stage an MSIX package #******************************************** #******************************************** function Get-AlkanePSFApplicationId { $appxManifest = "$($script:MSIXStagingFolderPath)AppxManifest.xml" if (test-path $appxManifest) { [xml]$appInfo = Get-Content -Path $appxManifest $applications = $appInfo.Package.Applications.Application if ($null -ne $applications) { $availableApps = ($applications.id -join " ") Write-AlkanePSFOutput "Available App Id's are: $availableApps" } } } #******************************************** #******************************************** #function to extract/stage an MSIX package #******************************************** #******************************************** function New-AlkanePSFStagedPackage { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param() if($PSCmdlet.ShouldProcess("Should process?")){ try { if (!(test-path $script:MSIXInputFilePath)) { Write-AlkanePSFOutput "**WARNING** Could not find $script:MSIXInputFilePath" return } Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)" if (Test-Path $script:MSIXStagingFolderPath) { Remove-LongPathDirectory $script:MSIXStagingFolderPath } New-Item -Path $script:MSIXStagingFolderPath -ItemType Directory -Force | Out-Null Write-AlkanePSFOutput "Finding MakeAppx.exe." $makeAppxPath = Get-WindowsSDKExe -ExeName "makeappx.exe" if ($makeAppxPath -ne "") { Write-AlkanePSFOutput "Extracting (staging) package to $($script:MSIXStagingFolderPath)." $processArgs = @("unpack","/p","$script:MSIXInputFilePath","/d","$($script:MSIXStagingFolderPath)") $exitcode = (Start-AlkanePSFProcess -Exe $makeAppxPath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Package extracted (staged) to $($script:MSIXStagingFolderPath) with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Package extracted (staged) to $($script:MSIXStagingFolderPath) with exit code $exitcode." } } else { Write-AlkanePSFOutput "Cannot find MakeAppx.exe" } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } #******************************************** #******************************************** #function to get the name of a filename without the extension #******************************************** #******************************************** function Get-ExeNameForFixup { Param( [string]$PathToEXE ) try { if ($PathToEXE -like "*/*" -or $PathToEXE -like "*\*") { $PathToEXE = $PathToEXE.replace('\','/').split('/')[-1] } #keep full file name #if ($PathToEXE -like "*.*") { # $PathToEXE = $PathToEXE.split('.')[0] #} return $PathToEXE } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to get a PSF fixup #******************************************* #******************************************** function Get-AlkanePSFFixup { try { $allFixups = @() foreach ($fixup in $script:processesModel.fixups) { $allFixups += $fixup } return ($allFixups) } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } Function Get-OriginalExe { Param( [string]$configJson, [string]$applicationId ) if (test-path $configJson) { # Load JSON file $json = Get-Content -Path $configJson -Raw | ConvertFrom-Json # Loop through the objects in the JSON data foreach ($object in $json.PSObject.Properties | Where-Object Name -eq "applications") { $app = $($object.Value) if ($app.id -eq $applicationId) { return $app.executable } } } return "" } #******************************************* #******************************************** #Function to add a supported Fixup application #******************************************* #******************************************** Function Add-AlkanePSFApplication { Param( [string]$ApplicationId, [string]$WorkingDirectory, [array]$Arguments, [bool]$InPackageContext=$true, [string]$ScriptExecutionMode="-ExecutionPolicy Bypass" ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { if (!(test-path $script:MSIXInputFilePath)) { Write-AlkanePSFOutput "**WARNING** Could not find $script:MSIXInputFilePath" return } #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -ne $targetExe -and $targetExe -ne "") { Write-AlkanePSFOutput "Application $ApplicationId already exists. Will not add again." return } $appxManifest = "$($script:MSIXStagingFolderPath)AppxManifest.xml" if (!(test-path $appxManifest)) { Write-AlkanePSFOutput "**WARNING** Could not find $appxManifest" return } $exe="" [xml]$appInfo = Get-Content -Path $appxManifest $applications = $appInfo.Package.Applications.Application if ($null -ne $applications) { $exe = $applications | Where-Object id -eq "$ApplicationId" | Select-Object -ExpandProperty executable if ($exe -like "*psflauncher*") { $configJSON = "$($script:MSIXStagingFolderPath)config.json" $origExe = Get-OriginalExe $configJSON $ApplicationId if ($origExe -ne "") { #if manifest exe is psflauncher, we try to reinstate the original from the config.json $exe = $origExe } } $availableApps = ($applications.id -join " ") if ($null -eq $exe -or $exe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId in AppxManifest.xml. Available app id's are: $availableApps. Please change the application ID." return } else { $processArgs="" if($null -ne $Arguments -or $Arguments.Count -gt 0){ $processArgs = ($Arguments | ForEach-Object {"`"" + $_ + "`""}) -join " " } $script:applicationsModel += ,([ordered]@{ id=$ApplicationId; executable=$exe; workingDirectory=$WorkingDirectory; inPackageContext=$InPackageContext; arguments=$processArgs; ScriptExecutionMode=$ScriptExecutionMode; }) } } $exe = Get-ExeNameForFixup $exe $script:processesModel += ,([ordered]@{ executable=$exe }) } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #Function to add file redirection fixup #******************************************* #******************************************** Function Add-AlkanePSFFileRedirectionFixup { Param( [string]$ApplicationId, [ValidateSet("packageRelative","packageDriveRelative","knownFolders")] [string]$FixupType, [string]$FixupBase, [array]$FixupPatterns, [string]$FixupId ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding File Redirection Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $FileRedirectionFixupModel = [ordered]@{ dll=$fileRedirectionDllName; config=@{ } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $fileRedirectionDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($FileRedirectionFixupModel) } $fixupstype = (($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $fileRedirectionDllName).config.redirectedPaths.$FixupType if ($null -eq $fixupstype -or $fixupstype.Count -eq 0) { ($script:processesModel.fixups | Where-Object dll -eq $fileRedirectionDllName).config.redirectedPaths += @{$FixupType = $null } } switch ($FixupType) { "packageRelative" { ($script:processesModel.fixups | Where-Object dll -eq $fileRedirectionDllName).config.redirectedPaths.$FixupType += ,@{ base=$FixupBase; patterns=$FixupPatterns; } } "packageDriveRelative" { ($script:processesModel.fixups | Where-Object dll -eq $fileRedirectionDllName).config.redirectedPaths.$FixupType += ,@{ base=$FixupBase; patterns=$FixupPatterns; } } "knownFolders" { ($script:processesModel.fixups | Where-Object dll -eq $fileRedirectionDllName).config.redirectedPaths.$FixupType += ,@{ id=$FixupId; relativePaths = ,@{ base=$FixupBase; patterns=$FixupPatterns; } } } } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #Function to add reg legacy fixup #******************************************* #******************************************** Function Add-AlkanePSFRegLegacyFixup { Param( [string]$ApplicationId, [ValidateSet("ModifyKeyAccess","FakeDelete")] [string]$FixupType, [ValidateSet("HKCU","HKLM")] [string]$FixupHive, [ValidateSet("FULL2RW","FULL2R","Full2MaxAllowed","RW2R","RW2MaxAllowed")] [string]$FixupAccess, [array]$FixupPatterns ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding Reg Legacy Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $RegLegacyFixupModel = [ordered]@{ dll=$regLegacyDllName; config=@{ } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $regLegacyDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($RegLegacyFixupModel) } switch ($FixupType) { "ModifyKeyAccess" { ($script:processesModel.fixups | Where-Object dll -eq $regLegacyDllName).config.remediation += ,@{ type=$FixupType; hive=$FixupHive; access=$FixupAccess; patterns=$FixupPatterns; } } "FakeDelete" { ($script:processesModel.fixups | Where-Object dll -eq $regLegacyDllName).config.remediation += ,@{ type=$FixupType; hive=$FixupHive; access=$FixupAccess; patterns=$FixupPatterns; } } } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add env var fixup #******************************************* #******************************************** Function Add-AlkanePSFEnvVarFixup { Param( [string]$ApplicationId, [string]$FixupVarName, [string]$FixupVarValue, [bool]$FixupVarUseRegistry=$true ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding Env Var Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $EnvVarFixupModel = [ordered]@{ dll=$envVarDllName; config=@{ } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $envVarDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($EnvVarFixupModel) } ($script:processesModel.fixups | Where-Object dll -eq $envVarDllName).config.envVars += ,@{ name=$FixupVarName; value=$FixupVarValue; useRegistry=$FixupVarUseRegistry; } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #Function to add dynamic library fixup #******************************************* #******************************************** Function Add-AlkanePSFDynamicLibraryFixup { Param( [string]$ApplicationId, [string]$FixupDllName, [string]$FixupDllFilepath, [bool]$FixupForcePackageDllUse=$true ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding Dynamic Library Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $DynamicLibraryFixupModel = [ordered]@{ dll=$dynamicLibraryDllName; config=@{ forcePackageDllUse=$FixupForcePackageDllUse; } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $dynamicLibraryDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($DynamicLibraryFixupModel) } ($script:processesModel.fixups | Where-Object dll -eq $dynamicLibraryDllName).config.relativeDllPaths += ,@{ name=$FixupDllName; filepath=$FixupDllFilepath; } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #Function to add MFR fixup #******************************************* #******************************************** Function Add-AlkanePSFMFRFixup { Param( [string]$ApplicationId, [ValidateSet("OverrideLocalRedirections","OverrideTraditionalRedirections")] [string]$FixupType, [string]$FixupName, [string]$FixupMode, [bool]$FixupIlvAware=$false, [ValidateSet("disableAll","enablePe","default")] [string]$FixupOverrideCOW="default" ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding MFR Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $MFRFixupModel = [ordered]@{ dll=$mfrDllName; config=@{ overrideCOW=$FixupOverrideCOW; ilvAware=$FixupIlvAware; } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $mfrDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($MFRFixupModel) } switch ($FixupType) { "OverrideLocalRedirections" { ($script:processesModel.fixups | Where-Object dll -eq $mfrDllName).config.overrideLocalRedirections += ,[ordered]@{ name=$FixupName; mode=$FixupMode; } } "OverrideTraditionalRedirections" { ($script:processesModel.fixups | Where-Object dll -eq $mfrDllName).config.overrideTraditionalRedirections += ,[ordered]@{ name=$FixupName; mode=$FixupMode; } } } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add trace fixup #******************************************* #******************************************** Function Add-AlkanePSFTraceFixup { Param( [string]$ApplicationId, [ValidateSet("printf","eventlog","outputDebugString")] [string]$FixupTraceMethod, [bool]$FixupWaitForDebugger=$false, [bool]$FixupTraceFunctionEntry=$false, [bool]$FixupTraceCallingModule=$true, [bool]$FixupIgnoreDllLoad=$true, [ValidateSet("default","filesystem","registry","processAndThread","dynamicLinkLibrary")] [string]$FixupTraceLevelProperty, [ValidateSet("always","ignoreSuccess","allFailures","unexpectedFailures","ignore")] [string]$FixupTraceLevelValue, [ValidateSet("default","filesystem","registry","processAndThread","dynamicLinkLibrary")] [string]$FixupBreakOnProperty, [ValidateSet("always","ignoreSuccess","allFailures","unexpectedFailures","ignore")] [string]$FixupBreakOnValue ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding Trace Fixup for $ApplicationId" try { #check if we have an 'application' in our dynamic config.json $targetExe = ($script:applicationsModel | Where-Object id -eq "$ApplicationId").executable if ($null -eq $targetExe -or $targetExe -eq "") { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." return } #extract the process name without the extension $targetExe = Get-ExeNameForFixup -PathToEXE $targetExe $TraceFixupModel = [ordered]@{ dll=$traceDllName; config=@{ traceMethod=$FixupTraceMethod waitForDebugger=$FixupWaitForDebugger traceFunctionEntry=$FixupTraceFunctionEntry traceCallingModule=$FixupTraceCallingModule ignoreDllLoad=$FixupIgnoreDllLoad traceLevels=@{ $FixupTraceLevelProperty=$FixupTraceLevelValue } breakOn=@{ $FixupBreakOnProperty=$FixupBreakOnValue } } } #check if fixup has been added previously $fixups = ($script:processesModel | Where-Object executable -eq $targetExe).fixups | Where-Object dll -eq $traceDllName if ($null -eq $fixups -or $fixups.Count -eq 0) { ($script:processesModel | Where-Object executable -eq $targetExe).fixups += @($TraceFixupModel) } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add start script #******************************************* #******************************************** Function Add-AlkanePSFStartScript { Param( [string]$ApplicationId, [string]$ScriptSourcePath, [array]$ScriptArguments, [bool]$RunInVirtualEnvironment=$true, [bool]$ShowWindow=$false, [bool]$StopOnScriptError=$false, [bool]$WaitForScriptToFinish=$true, [int]$Timeout, [bool]$RunOnce=$true ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding Start Script for $ApplicationId" try { $foundApp = $false; if (!(test-path $ScriptSourcePath)) { Write-AlkanePSFOutput "**ERROR** Cannot find script $ScriptSourcePath" } #script found - add to global array for copying $script:scriptArray += $ScriptSourcePath $fileName = Split-Path -Path $ScriptSourcePath -Leaf foreach ($app in $script:applicationsModel) { if ($app.id -eq $ApplicationId) { $foundApp = $true $processArgs="" if($null -ne $ScriptArguments -or $ScriptArguments.Count -gt 0){ $processArgs = ($ScriptArguments | ForEach-Object {"`"" + $_ + "`""}) -join " " } $app.startScript = @{ scriptPath=$fileName; scriptArguments=$processArgs; runInVirtualEnvironment=$RunInVirtualEnvironment; showWindow=$ShowWindow; stopOnScriptError=$StopOnScriptError; waitForScriptToFinish=$WaitForScriptToFinish; timeout=$Timeout; runOnce=$RunOnce; } } } if (!$foundApp) { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add end script #******************************************* #******************************************** Function Add-AlkanePSFEndScript { Param( [string]$ApplicationId, [string]$ScriptSourcePath, [array]$ScriptArguments, [bool]$RunInVirtualEnvironment=$true, [bool]$ShowWindow=$false, [bool]$StopOnScriptError=$false, [bool]$WaitForScriptToFinish=$true, [int]$Timeout, [bool]$RunOnce=$true ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Adding End Script for $ApplicationId" try { $foundApp = $false; if (!(test-path $ScriptSourcePath)) { Write-AlkanePSFOutput "**ERROR** Cannot find script $ScriptSourcePath" } #script found - add to global array for copying $script:scriptArray += $ScriptSourcePath $fileName = Split-Path -Path $ScriptSourcePath -Leaf foreach ($app in $script:applicationsModel) { if ($app.id -eq $ApplicationId) { $foundApp = $true $processArgs="" if($null -ne $ScriptArguments -or $ScriptArguments.Count -gt 0){ $processArgs = ($ScriptArguments | ForEach-Object {"`"" + $_ + "`""}) -join " " } $app.endScript = @{ scriptPath=$fileName; scriptArguments=$processArgs; runInVirtualEnvironment=$RunInVirtualEnvironment; showWindow=$ShowWindow; stopOnScriptError=$StopOnScriptError; waitForScriptToFinish=$WaitForScriptToFinish; timeout=$Timeout; runOnce=$RunOnce; } } } if (!$foundApp) { Write-AlkanePSFOutput "**WARNING** Could not find application ID: $ApplicationId. Please change the application ID or run: Add-AlkanePSFApplication -ApplicationId `"$ApplicationId`"." } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to remove application #******************************************* #******************************************** function Remove-AlkanePSFApplication { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [string]$ApplicationId ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } if($PSCmdlet.ShouldProcess("Should process?")){ try { $script:removeApplicationArray += $ApplicationId } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } #******************************************* #******************************************** #function to remove shortcut #******************************************* #******************************************** function Remove-AlkanePSFShortcut { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [string]$ShortcutName ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } if($PSCmdlet.ShouldProcess("Should process?")){ try { $script:removeShortcutArray += $ShortcutName } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } #******************************************* #******************************************** #function to add manifest when using PSFLauncher to elevate #******************************************* #******************************************** Function Add-AlkanePSFManifest { Param( [string]$ManifestPath ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } Write-AlkanePSFOutput "Generating PSF manifest for elevation" $xmlContent = "<?xml version=`"1.0`" encoding=`"UTF-8`" standalone=`"yes`"?>" + "`r`n" $xmlContent += "<assembly xmlns=`"urn:schemas-microsoft-com:asm.v1`" manifestVersion=`"1.0`">" + "`r`n" $xmlContent += " <trustInfo xmlns=`"urn:schemas-microsoft-com:asm.v3`">" + "`r`n" $xmlContent += " <security>" + "`r`n" $xmlContent += " <requestedPrivileges>" + "`r`n" $xmlContent += " <requestedExecutionLevel level=`"requireAdministrator`" uiAccess=`"false`" />" + "`r`n" $xmlContent += " </requestedPrivileges>" + "`r`n" $xmlContent += " </security>" + "`r`n" $xmlContent += " </trustInfo>" + "`r`n" $xmlContent += "</assembly>" $xmlContent | Out-File -FilePath $ManifestPath -Encoding UTF8 } #******************************************* #******************************************** #function to add capability #******************************************* #******************************************** Function Add-AlkanePSFCapability { Param( [string]$CapabilityName ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { $script:capabilitiesArray += $CapabilityName } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add dependency #******************************************* #******************************************** Function Add-AlkanePSFDependency { Param( [string]$DependencyName, [string]$DependencyMinVersion, [string]$DependencyPublisher ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { $script:dependenciesModel += ,([ordered]@{ name=$DependencyName; minversion=$DependencyMinVersion; publisher=$DependencyPublisher; }) } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add protocol #******************************************* #******************************************** Function Add-AlkanePSFProtocol { Param( [string]$ApplicationId, [string]$ProtocolName, [string]$ProtocolParameters, [string]$ProtocolDisplayName, [string]$ProtocolLogo ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { $script:protocolsModel += ,([ordered]@{ applicationid=$ApplicationId; name=$ProtocolName; parameters=$ProtocolParameters; displayName=$ProtocolDisplayName; logo=$ProtocolLogo }) } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #function to add protocol #******************************************* #******************************************** Function Add-AlkanePSFStringReplace { Param( [string]$FindRegex, [string]$ReplaceString ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } try { $script:stringReplaceModel += ,([ordered]@{ find=$FindRegex; replace=$ReplaceString; }) } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } #******************************************* #******************************************** #Function to apply fixups by updating AppxManifest.xml and generating config.json #******************************************* #******************************************** function Set-AlkanePSF { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param( [bool]$OverwriteConfigJson=$true, [bool]$OpenConfigJson=$false ) if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } if($PSCmdlet.ShouldProcess("Should process?")){ try { Write-AlkanePSFOutput "Applying fixups to AppxManifest.xml and config.json." if (!(test-path $script:MSIXInputFilePath)) { Write-AlkanePSFOutput "**WARNING** Could not find $script:MSIXInputFilePath" return } if (!(test-path $script:MSIXCertificateFilePath)) { Write-AlkanePSFOutput "**WARNING** Could not find $script:MSIXCertificateFilePath" return } if (!(test-path $script:MSIXStagingFolderPath)) { Write-AlkanePSFOutput "**WARNING** Could not find $($script:MSIXStagingFolderPath)" return } #set the location of config.json and appxmanifest.xml to the staging folder $configJSON = "$($script:MSIXStagingFolderPath)config.json" $appxManifest = "$($script:MSIXStagingFolderPath)AppxManifest.xml" #locate the bin folder containing PSF DLLs if ($PSFType -eq "MS") { Write-AlkanePSFOutput "Using Microsoft's PSF." $nupkg = Get-Package | Where-Object Name -eq "Microsoft.PackageSupportFramework" | Select-Object -ExpandProperty Source $psfLocation = (get-item $nupkg).Directory.FullName + "\bin" } else { Write-AlkanePSFOutput "Using Tim Mangan's PSF." $psfLocation = "$env:temp\TMPSF" } if (!(test-path $psfLocation)) { Write-AlkanePSFOutput "**ERROR** Could not find $psfLocation" return } #store all fixups $allfixups = Get-AlkanePSFFixup #psfLauncher files $psfLauncherExe = "PsfLauncher$script:MSIXArchitecture.exe" $psfLauncherDll = "PsfRuntime$script:MSIXArchitecture.dll" $psfRunExe = "PsfRunDll$script:MSIXArchitecture.exe" #fixup files $dynamicLibraryFixupDll = "DynamicLibraryFixup$script:MSIXArchitecture.dll" $fileRedirectionFixupDll = "FileRedirectionFixup$script:MSIXArchitecture.dll" $regLegacyFixupDll = "RegLegacyFixups$script:MSIXArchitecture.dll" $envVarFixupDll = "EnvVarFixup$script:MSIXArchitecture.dll" $mfrFixupDll = "MFRFixup$script:MSIXArchitecture.dll" $traceFixupDll = "TraceFixup$script:MSIXArchitecture.dll" #copy psfLauncher Copy-Item -Path "$psfLocation\$psfLauncherExe" -Destination "$($script:MSIXStagingFolderPath)" -Force Copy-Item -Path "$psfLocation\$psfLauncherDll" -Destination "$($script:MSIXStagingFolderPath)" -Force Copy-Item -Path "$psfLocation\$psfRunExe" -Destination "$($script:MSIXStagingFolderPath)" -Force #only used if we need elevation $psfManifest = "$($script:MSIXStagingFolderPath)PsfLauncher$script:MSIXArchitecture.exe.manifest" foreach($tempScript in $script:scriptArray) { if (test-path $tempScript) { Write-AlkanePSFOutput "Copying $tempScript to $($script:MSIXStagingFolderPath)." Copy-Item -Path "$tempScript" -Destination "$($script:MSIXStagingFolderPath)" -Force } } $pathToAppxManifest = "$($script:MSIXStagingFolderPath)AppxManifest.xml" #get a fresh AppxManifest in case we're running this multiple times try { Write-AlkanePSFOutput "Extracting a fresh AppxManifest from MSIX." $zip = [IO.Compression.ZipFile]::OpenRead($script:MSIXInputFilePath) $zip.Entries | Where-Object {$_.Name -eq 'AppxManifest.xml'} | ForEach-Object { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $pathToAppxManifest, $true) } $zip.Dispose() } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } #Need to make sure manifest publisher is the same as our certificate $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import($script:MSIXCertificateFilePath,$script:MSIXCertificatePassword,'DefaultKeySet') $certificateSubject = $cert.Subject #read from manifest [xml]$appInfo = Get-Content -Path $appxManifest $manifestChangesMade = $false #add capabilities #Define the namespace URI by retrieving it from the existing XML $namespaceURI = $appInfo.DocumentElement.GetNamespaceOfPrefix("rescap") foreach ($fixupCapability in $script:capabilitiesArray) { $existingCapability = $appInfo.Package.Capabilities.Capability | where-object Name -eq $fixupCapability if ($existingCapability) { Write-AlkanePSFOutput "Capability $fixupCapability already exists." break } # Create a new <rescap:Capability Name="xxx" /> element using the existing namespace $capabilityNode = $appInfo.CreateElement("rescap", "Capability", $namespaceURI) $capabilityNode.SetAttribute("Name", $fixupCapability) # Locate the <Capabilities> node, or create it if it doesn't exist $capabilitiesNode = $appInfo.DocumentElement.Capabilities if (-not $capabilitiesNode) { $capabilitiesNode = $appInfo.CreateElement("Capabilities") } $capabilitiesNode.AppendChild($capabilityNode) | Out-Null $appInfo.DocumentElement.AppendChild($capabilitiesNode) | Out-Null Write-AlkanePSFOutput "Added $fixupCapability Capability" if ($fixupCapability -eq "allowElevation") { Add-AlkanePSFManifest -ManifestPath $psfManifest } $manifestChangesMade = $true } #add dependencies $namespaceURI = $appInfo.DocumentElement.NamespaceURI foreach ($fixupdependency in $script:dependenciesModel) { $depName = $fixupdependency.name $depMinVersion = $fixupdependency.minversion $depPublisher = $fixupdependency.publisher $existingDependency = $appInfo.Package.Dependencies.PackageDependency | where-object Name -eq $depName if ($existingDependency) { Write-AlkanePSFOutput "Dependency $depName already exists." break } # Create a new <PackageDependency Name="xxx" /> element $dependencyNode = $appInfo.CreateElement("PackageDependency", $namespaceURI) $dependencyNode.SetAttribute("Name", $depName) $dependencyNode.SetAttribute("MinVersion", $depMinVersion) $dependencyNode.SetAttribute("Publisher", $depPublisher) # Locate the <Dependencies> node, or create it if it doesn't exist $dependenciesNode = $appInfo.DocumentElement.Dependencies if (-not $dependenciesNode) { $dependenciesNode = $appInfo.CreateElement("Dependencies") } $dependenciesNode.AppendChild($dependencyNode) | Out-Null $appInfo.DocumentElement.AppendChild($dependenciesNode) | Out-Null Write-AlkanePSFOutput "Added $depName Dependency" $manifestChangesMade = $true } #add protocols #Define the namespace URI by retrieving it from the existing XML $namespaceURIUAP3 = $appInfo.DocumentElement.GetNamespaceOfPrefix("uap3") $namespaceURIUAP = $appInfo.DocumentElement.GetNamespaceOfPrefix("uap") foreach ($fixupprotocol in $script:protocolsModel) { $proAppId = $fixupprotocol.applicationid $proName = $fixupprotocol.name $proParameters = $fixupprotocol.parameters $proDisplayName = $fixupprotocol.displayName $proLogo = $fixupprotocol.logo $existingApp = $appInfo.Package.Applications.Application | where-object Id -eq $proAppId if ($null -ne $existingApp) { #found app $existingProtocol = $existingApp.Extensions.Extension.Protocol | where-object Name -eq $proName if ($null -ne $existingProtocol) { Write-AlkanePSFOutput "Protocol $proName already exists." break } #not found protocol $extensionsNode = $existingApp.Extensions if (-not $extensionsNode) { $extensionsNode = $appInfo.CreateElement("Extensions",$existingApp.NamespaceURI) $existingApp.AppendChild($extensionsNode) | Out-Null } $extensionNode = $appInfo.CreateElement("uap3", "Extension", $namespaceURIUAP3) $extensionNode.SetAttribute("Category", "windows.protocol") | Out-Null $extensionsNode.AppendChild($extensionNode) | Out-Null $protocolNode = $appInfo.CreateElement("uap3", "Protocol", $namespaceURIUAP3) $protocolNode.SetAttribute("Name", $proName) | Out-Null if ($null -ne $proParameters -and $proParameters -ne "") { #cannot add if blank $protocolNode.SetAttribute("Parameters", $proParameters) | Out-Null } $extensionNode.AppendChild($protocolNode) | Out-Null $displayNameNode = $appInfo.CreateElement("uap", "DisplayName", $namespaceURIUAP) $displayNameNode.InnerText = $proDisplayName $protocolNode.AppendChild($displayNameNode) | Out-Null $logoNode = $appInfo.CreateElement("uap", "Logo", $namespaceURIUAP) $logoNode.InnerText = $proLogo $protocolNode.AppendChild($logoNode) | Out-Null Write-AlkanePSFOutput "Added $proName Protocol" $manifestChangesMade = $true } } #remove apps foreach ($appid in $script:removeApplicationArray) { $applicationNode = $appInfo.Package.Applications.Application | where-object Id -eq $appid if ($applicationNode) { # Get the parent node (Applications) $parentNode = $applicationNode.ParentNode # Remove the child node (the Application node) $parentNode.RemoveChild($applicationNode) | Out-Null Write-AlkanePSFOutput "Removed Application with Id $appid" $manifestChangesMade = $true } } foreach ($shortcutName in $script:removeShortcutArray) { #escape special chars. $shortcutName = $shortcutName.Replace("[", "``[").Replace("]", "``]").Replace("*", "``*").Replace("?", "``?") $venodes = $appInfo.Package.Applications.Application.VisualElements | Where-Object DisplayName -like "*$shortcutName*" if ($venodes) { foreach ($element in $venodes) { Write-AlkanePSFOutput "Removing visual element $($element.DisplayName)" $parentNode = $element.ParentNode $parentNode.RemoveChild($element) | Out-Null $manifestChangesMade = $true } } $enodes = $appInfo.Package.Applications.Application.Extensions.Extension.Shortcut | Where-Object File -like "*$shortcutName*" if ($enodes) { foreach ($element in $enodes) { Write-AlkanePSFOutput "Removing shortcut $($element.File)" $parentNode = $element.ParentNode $parentNode.RemoveChild($element) | Out-Null $manifestChangesMade = $true } } } $identity = $appInfo.Package.Identity $identity.Publisher = $certificateSubject #get app list for config.json from Appxmanifest $applications = $appInfo.Package.Applications.Application if ($null -ne $applications) { #change executable in manifest to point to the PsfLauncher foreach ($fixupapp in $script:applicationsModel) { $foundApp = $false foreach ($manifestapp in $applications){ if ($manifestapp.id -eq $fixupapp.id) { $foundApp = $true $manifestapp.Executable = "$psfLauncherExe" break } } if (!$foundApp) { $availableApps = ($applications.id -join " ") Write-AlkanePSFOutput "**WARNING** Could not find application ID: $($fixupapp.id). When using Add-AlkanePSFApplication, your app id must be one of: $availableApps" } } $manifestChangesMade = $true } if ($manifestChangesMade) { #save the appxmanifest.xml $appInfo.Save($appxManifest) } $manifestChangesMade = $false # Read the content of the file $fileContent = Get-Content -Path $appxManifest -Raw #string replacements foreach ($stringReplacement in $script:stringReplaceModel) { $findpattern = $stringReplacement.find $replace = $stringReplacement.replace try { # Perform the regex replace $fileContent = $fileContent -replace $findpattern, $replace Write-AlkanePSFOutput "Replaced $findpattern with $replace" $manifestChangesMade = $true } catch { Write-AlkanePSFOutput "**ERROR** Replacing $findpattern with $replace. $_.Message" } } if ($manifestChangesMade) { #save the appxmanifest.xml Set-Content -Path $appxManifest -Value $fileContent } #generate config.json #copy required fixup DLLs, and removed DLLs not required $ApplyDynamicLibraryFixup = $false $ApplyFileRedirectionFixup = $false $ApplyEnvVarFixup = $false $ApplyRegLegacyFixup = $false $ApplyMFRFixup = $false $ApplyTraceFixup = $false foreach($fu in $allfixups) { switch ($fu.dll) { $dynamicLibraryDllName {$fu.dll="$dynamicLibraryFixupDll";$ApplyDynamicLibraryFixup=$true;} $fileRedirectionDllName {$fu.dll="$fileRedirectionFixupDll";$ApplyFileRedirectionFixup=$true;} $envVarDllName {$fu.dll="$envVarFixupDll";$ApplyEnvVarFixup=$true;} $regLegacyDllName {$fu.dll="$regLegacyFixupDll";$ApplyRegLegacyFixup=$true;} $mfrDllName {$fu.dll="$mfrFixupDll";$ApplyMFRFixup=$true;} $traceDllName {$fu.dll="$traceFixupDll";$ApplyTraceFixup=$true;} } } if ($ApplyTraceFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$traceFixupDll." Copy-Item -Path "$psfLocation\$traceFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$traceFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$traceFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } if ($ApplyMFRFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$mfrFixupDll." Copy-Item -Path "$psfLocation\$mfrFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$mfrFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$mfrFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } if ($ApplyFileRedirectionFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$fileRedirectionFixupDll." Copy-Item -Path "$psfLocation\$fileRedirectionFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$fileRedirectionFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$fileRedirectionFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } if ($ApplyRegLegacyFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$regLegacyFixupDll." Copy-Item -Path "$psfLocation\$regLegacyFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$regLegacyFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$regLegacyFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } if ($ApplyEnvVarFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$EnvVarFixupDll." Copy-Item -Path "$psfLocation\$EnvVarFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$EnvVarFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$EnvVarFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } if ($ApplyDynamicLibraryFixup) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)$dynamicLibraryFixupDll." Copy-Item -Path "$psfLocation\$dynamicLibraryFixupDll" -Destination "$($script:MSIXStagingFolderPath)" -Force } else { Write-AlkanePSFOutput "Removing $($script:MSIXStagingFolderPath)$dynamicLibraryFixupDll." Remove-Item -Path "$($script:MSIXStagingFolderPath)$dynamicLibraryFixupDll" -Force -Recurse -ErrorAction SilentlyContinue } #copy script wrapper $scriptWrapper = "$psfLocation\StartingScriptWrapper.ps1" if (test-path($scriptWrapper)) { Write-AlkanePSFOutput "Adding $($script:MSIXStagingFolderPath)StartingScriptWrapper.ps1" Copy-Item -Path $scriptWrapper -Destination "$($script:MSIXStagingFolderPath)" -Force } $json = @{ 'applications' = $script:applicationsModel 'processes' = $script:processesModel } #delete config.json if exists if ($OverwriteConfigJson -and (test-path $configJSON)) { Write-AlkanePSFOutput "Removing $configJSON." Remove-Item $configJSON -Force -Recurse -ErrorAction SilentlyContinue #write config.json Write-AlkanePSFOutput "Creating $configJSON." $json | ConvertTo-Json -Depth 15 | Out-File -FilePath $configJSON } else { Write-AlkanePSFOutput "Will not overwrite $configJSON." } if ($OpenConfigJson) { Start-Process "notepad.exe" -ArgumentList $configJSON } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } #******************************************* #******************************************** #Function to create Resources.pri (when we manually add new PNG icons etc) #******************************************* #******************************************** function New-AlkanePSFResourcesPRI { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param() if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } if($PSCmdlet.ShouldProcess("Should process?")){ try { Write-AlkanePSFOutput "Generating new Resources.pri." Write-AlkanePSFOutput "Finding MakePri.exe." $makePriPath = Get-WindowsSDKExe -ExeName "makepri.exe" if ($makePriPath -ne "") { Write-AlkanePSFOutput "Found at $makePriPath." Write-AlkanePSFOutput "Generating PriConfig.xml..." #Language in <Resource> node: en-US #Build for Win 10 #make pri config $processArgs = @("createconfig","/cf","$($script:MSIXStagingFolderPath)PriConfig.xml","/dq","en-US","/pv","10.0.0","/o") $exitcode = (Start-AlkanePSFProcess -Exe $makePriPath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Generated $($script:MSIXStagingFolderPath)PriConfig.xml with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Generating $($script:MSIXStagingFolderPath)PriConfig.xml with exit code $exitcode." } Write-AlkanePSFOutput "Generating Resources.pri..." #make pri config $processArgs = @("new","/pr","$($script:MSIXStagingFolderPath)","/cf","$($script:MSIXStagingFolderPath)PriConfig.xml","/mn","$($script:MSIXStagingFolderPath)AppxManifest.xml","/of","$($script:MSIXStagingFolderPath)Resources.pri","/o") $exitcode = (Start-AlkanePSFProcess -Exe $makePriPath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Generated $($script:MSIXStagingFolderPath)Resources.pri with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Generating $($script:MSIXStagingFolderPath)Resources.pri with exit code $exitcode." } } else { Write-AlkanePSFOutput "Cannot find MakePri.exe." } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } #******************************************* #******************************************** #Function to create and sign a new MSIX #******************************************* #******************************************** function New-AlkanePSFMSIX { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] Param() if (!($script:alkanePSFConfigured)) { Write-AlkanePSFOutput "**ERROR** AlkanePSF not configured. You must first call Set-AlkanePSFConfiguration." return } if($PSCmdlet.ShouldProcess("Should process?")){ try { Write-AlkanePSFOutput "Compiling fixed MSIX package." if (!(test-path $script:MSIXCertificateFilePath)) { Write-AlkanePSFOutput "**WARNING** Could not find $script:MSIXCertificateFilePath" return } if (test-path $script:MSIXOutputFilePath) { Write-AlkanePSFOutput "Removing $script:MSIXOutputFilePath." Remove-Item $script:MSIXOutputFilePath -Force -Recurse -ErrorAction SilentlyContinue } Write-AlkanePSFOutput "Finding MakeAppx.exe." $makeAppxPath = Get-WindowsSDKExe -ExeName "makeappx.exe" if ($makeAppxPath -ne "") { Write-AlkanePSFOutput "Found at $makeAppxPath." Write-AlkanePSFOutput "Finding Signtool.exe." $signToolPath = Get-WindowsSDKExe -ExeName "signtool.exe" if ($signToolPath -ne "") { Write-AlkanePSFOutput "Found at $signToolPath." Write-AlkanePSFOutput "Compiling package to $script:MSIXOutputFilePath." # Extract the directory path from the file path $directoryPath = [System.IO.Path]::GetDirectoryName($script:MSIXOutputFilePath) # Check if the directory exists, and create it if it doesn’t if (!(Test-Path -Path $directoryPath)) { New-Item -ItemType Directory -Path $directoryPath -Force | Out-Null } $processArgs = @("pack","/p","$script:MSIXOutputFilePath","/d","$($script:MSIXStagingFolderPath)") $exitcode = (Start-AlkanePSFProcess -Exe $makeAppxPath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Package compiled to $script:MSIXOutputFilePath with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Package compiled to $script:MSIXOutputFilePath with exit code $exitcode." } Write-AlkanePSFOutput "Signing $script:MSIXOutputFilePath." $processArgs = @("sign","/tr","""http://timestamp.digicert.com""","/v","/fd","sha256","/td","sha256","/f","""$script:MSIXCertificateFilePath""","/p","""$script:MSIXCertificatePassword""","""$script:MSIXOutputFilePath""") $exitcode = (Start-AlkanePSFProcess -Exe $signToolPath -ExeArgs $processArgs) if ($exitcode -eq 0) { Write-AlkanePSFOutput "Signed $script:MSIXOutputFilePath with exit code $exitcode." } else { Write-AlkanePSFOutput "**WARNING** Signed $script:MSIXOutputFilePath with exit code $exitcode." } } else { Write-AlkanePSFOutput "Cannot find Signtool.exe." } } else { Write-AlkanePSFOutput "Cannot find MakeAppx.exe." } } catch { Write-AlkanePSFOutput "**ERROR** $($_)" } } } |