mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Overhauls Store app installation with dependency resolution
Refactors the Store app installation script to be significantly more robust and intelligent. The new implementation automatically resolves application dependencies instead of relying on a simple folder structure. Key improvements include: - Pre-scans all application folders to create a central catalog of available dependencies. - Parses the `AppxManifest.xml` from each main app package (including from within bundles) to determine its true dependencies. - Resolves the required dependencies by finding the best available package from the catalog that meets version and OS architecture requirements. - Adds support for extracting zipped dependency packages. - Improves temporary file management and logging.
This commit is contained in:
@@ -1,57 +1,271 @@
|
|||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
# --- CONFIGURATION ---
|
||||||
|
# Base path where application folders are located. Each subfolder represents one application.
|
||||||
$basePath = "D:\MSStore"
|
$basePath = "D:\MSStore"
|
||||||
|
# Path for temporary files (e.g., for extracting archives). This will be created and cleaned up automatically.
|
||||||
|
$tempBasePath = Join-Path -Path $env:TEMP -ChildPath "StoreAppInstall"
|
||||||
|
|
||||||
|
# --- SCRIPT ---
|
||||||
|
|
||||||
|
# Helper function to clean up temporary files on exit or error
|
||||||
|
function Remove-TemporaryFiles {
|
||||||
|
if (Test-Path -Path $tempBasePath) {
|
||||||
|
Write-Host "Cleaning up temporary directory: $tempBasePath"
|
||||||
|
Remove-Item -Path $tempBasePath -Recurse -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure temp directory is clean before starting
|
||||||
|
Remove-TemporaryFiles
|
||||||
|
New-Item -Path $tempBasePath -ItemType Directory -Force | Out-Null
|
||||||
|
|
||||||
|
# 1. Determine applicable dependency architectures based on the OS architecture
|
||||||
|
$osArchitecture = $env:PROCESSOR_ARCHITECTURE
|
||||||
|
$applicableArchitectures = switch ($osArchitecture) {
|
||||||
|
"AMD64" { 'x64', 'x86' }
|
||||||
|
"x86" { 'x86' }
|
||||||
|
"ARM64" { 'arm64', 'arm' }
|
||||||
|
default { $osArchitecture.ToLower() }
|
||||||
|
}
|
||||||
|
Write-Host "Installing Store Apps: Detected OS Architecture: $osArchitecture."
|
||||||
|
Write-Host "Applicable dependency architectures: $($applicableArchitectures -join ', ')"
|
||||||
|
|
||||||
# Check if the base path exists
|
# Check if the base path exists
|
||||||
Write-Host "Installing Store Apps: Checking for $basePath"
|
|
||||||
if (-not (Test-Path -Path $basePath)) {
|
if (-not (Test-Path -Path $basePath)) {
|
||||||
Write-Host "Installing Store Apps: $basePath does not exist."
|
Write-Host "Installing Store Apps: Base path '$basePath' does not exist. Exiting."
|
||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
Write-Host "Installing Store Apps: $basePath exists, installing apps."
|
Write-Host "Installing Store Apps: Base path '$basePath' exists."
|
||||||
|
|
||||||
# Process each app folder in the base path
|
# 2. Pre-scan and catalog all available dependencies from all app folders
|
||||||
foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
Write-Host "Scanning for all available application dependencies..."
|
||||||
$folderPath = $appFolder.FullName
|
$allAvailableDependencies = @{}
|
||||||
$dependenciesFolder = Join-Path -Path $folderPath -ChildPath "Dependencies"
|
$dependencyFoldersToScan = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|
||||||
# Find main package - exclude Dependencies folder items and xml/yaml files
|
# Find all 'Dependencies' subfolders
|
||||||
$mainPackage = Get-ChildItem -Path $folderPath -File |
|
Get-ChildItem -Path $basePath -Directory | ForEach-Object {
|
||||||
Where-Object {
|
$dependenciesPath = Join-Path -Path $_.FullName -ChildPath "Dependencies"
|
||||||
$_.DirectoryName -ne $dependenciesFolder -and
|
if (Test-Path -Path $dependenciesPath) {
|
||||||
$_.Extension -ne ".xml" -and
|
$dependencyFoldersToScan.Add($dependenciesPath)
|
||||||
$_.Extension -ne ".yaml"
|
}
|
||||||
} | Select-Object -First 1
|
}
|
||||||
|
|
||||||
if ($mainPackage) {
|
# Handle zipped dependencies by extracting them to a temp location
|
||||||
# Build DISM command with main package
|
$dependencyFoldersToScan.ToArray() | ForEach-Object {
|
||||||
$dismParams = @(
|
$folder = $_
|
||||||
"/Online"
|
Get-ChildItem -Path $folder -Filter "*.zip" -File | ForEach-Object {
|
||||||
"/Add-ProvisionedAppxPackage"
|
$zipFile = $_
|
||||||
"/PackagePath:`"$($mainPackage.FullName)`""
|
$extractPath = Join-Path -Path $tempBasePath -ChildPath $zipFile.BaseName
|
||||||
"/Region:all"
|
Write-Host "Extracting zipped dependencies from '$($zipFile.FullName)' to '$extractPath'..."
|
||||||
"/StubPackageOption:installfull"
|
try {
|
||||||
)
|
Expand-Archive -Path $zipFile.FullName -DestinationPath $extractPath -Force
|
||||||
|
$script:dependencyFoldersToScan.Add($extractPath)
|
||||||
# Add dependency packages if they exist
|
}
|
||||||
if (Test-Path -Path $dependenciesFolder) {
|
catch {
|
||||||
$dependencies = Get-ChildItem -Path $dependenciesFolder -File
|
Write-Error "Failed to extract '$($zipFile.FullName)'. Error: $($_.Exception.Message)"
|
||||||
foreach ($dependency in $dependencies) {
|
}
|
||||||
$dismParams += "/DependencyPackagePath:`"$($dependency.FullName)`""
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Regex to parse package filenames: Name_Version_Architecture__PublisherID (PublisherID and other tags are optional)
|
||||||
|
$packageFileRegex = '^(?<Name>.+?)_(?<Version>(?:\d+\.){2,3}\d+)_(?:[^_]+_)*(?<Arch>x64|x86|arm|arm64|neutral)(?:__.*)?$'
|
||||||
|
|
||||||
|
# Catalog all package files found in the dependency folders
|
||||||
|
foreach ($folder in $dependencyFoldersToScan.ToArray() | Select-Object -Unique) {
|
||||||
|
Get-ChildItem -Path $folder -Recurse -File | Where-Object { $_.Extension -in '.appx', '.msix', '.appxbundle' } | ForEach-Object {
|
||||||
|
$file = $_
|
||||||
|
$match = $file.BaseName -imatch $packageFileRegex
|
||||||
|
if ($match) {
|
||||||
|
$dependencyName = $matches.Name
|
||||||
|
try {
|
||||||
|
$dependencyVersion = [System.Version]$matches.Version
|
||||||
|
$dependencyArch = $matches.Arch
|
||||||
|
|
||||||
|
if (-not $allAvailableDependencies.ContainsKey($dependencyName)) {
|
||||||
|
$allAvailableDependencies[$dependencyName] = [System.Collections.Generic.List[object]]::new()
|
||||||
|
}
|
||||||
|
$allAvailableDependencies[$dependencyName].Add([pscustomobject]@{
|
||||||
|
Name = $dependencyName
|
||||||
|
Version = $dependencyVersion
|
||||||
|
Arch = $dependencyArch
|
||||||
|
Path = $file.FullName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning "Could not parse version for file '$($file.Name)'. Skipping."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Look for license file and add appropriate parameter
|
|
||||||
$licenseFile = Get-ChildItem -Path $folderPath -Filter "*.xml" -File | Select-Object -First 1
|
|
||||||
if ($licenseFile) {
|
|
||||||
$dismParams += "/LicensePath:`"$($licenseFile.FullName)`""
|
|
||||||
} else {
|
|
||||||
$dismParams += "/SkipLicense"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Construct final command
|
|
||||||
$dismCommand = "DISM " + ($dismParams -join " ")
|
|
||||||
|
|
||||||
# Output and execute the command
|
|
||||||
Write-Output $dismCommand
|
|
||||||
Invoke-Expression -Command $dismCommand
|
|
||||||
Write-Output ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Write-Host "Dependency scan complete. Found $($allAvailableDependencies.Keys.Count) unique dependency packages."
|
||||||
|
Write-Output ""
|
||||||
|
|
||||||
|
# 3. Process and install each main application
|
||||||
|
Write-Host "Starting main application installation process..."
|
||||||
|
foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
||||||
|
Write-Host "--- Processing application in folder: $($appFolder.Name) ---"
|
||||||
|
|
||||||
|
# Find the main application package (.appx/.msix/.appxbundle) in the app's root folder
|
||||||
|
$mainPackage = Get-ChildItem -Path $appFolder.FullName -File |
|
||||||
|
Where-Object { $_.Extension -in '.appx', '.msix', '.appxbundle', '.msixbundle' } |
|
||||||
|
Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $mainPackage) {
|
||||||
|
Write-Warning "No main application package found in '$($appFolder.Name)'. Skipping."
|
||||||
|
Write-Output ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Write-Host "Found main package: $($mainPackage.Name)"
|
||||||
|
|
||||||
|
# Extract and parse AppxManifest.xml from the main package
|
||||||
|
$manifestTempPath = Join-Path -Path $tempBasePath -ChildPath "AppxManifest.xml"
|
||||||
|
if (Test-Path $manifestTempPath) { Remove-Item $manifestTempPath -Force }
|
||||||
|
|
||||||
|
$requiredDependencies = $null
|
||||||
|
try {
|
||||||
|
[System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
|
||||||
|
|
||||||
|
# Logic for handling bundles vs. single packages
|
||||||
|
if ($mainPackage.Extension -in '.appxbundle', '.msixbundle') {
|
||||||
|
Write-Host "Processing bundle. Searching for architecture-specific package..."
|
||||||
|
$bundleArchive = [System.IO.Compression.ZipFile]::OpenRead($mainPackage.FullName)
|
||||||
|
try {
|
||||||
|
# Find the best matching .appx/.msix package inside the bundle
|
||||||
|
$primaryArch = if ($osArchitecture -eq 'AMD64') { 'x64' } else { $osArchitecture.ToLower() }
|
||||||
|
$packageEntries = $bundleArchive.Entries | Where-Object { ($_.Name.EndsWith('.appx') -or $_.Name.EndsWith('.msix')) -and $_.Name -notlike "*_language-*" }
|
||||||
|
|
||||||
|
# Prioritize the primary architecture, then x86 (on x64), then neutral
|
||||||
|
$bestPackageEntry = $packageEntries | Where-Object { $_.Name -imatch "[._]${primaryArch}\.(appx|msix)$" } | Select-Object -First 1
|
||||||
|
if (-not $bestPackageEntry -and $primaryArch -eq 'x64') {
|
||||||
|
$bestPackageEntry = $packageEntries | Where-Object { $_.Name -imatch "[._]x86\.(appx|msix)$" } | Select-Object -First 1
|
||||||
|
}
|
||||||
|
if (-not $bestPackageEntry) {
|
||||||
|
$bestPackageEntry = $packageEntries | Where-Object { $_.Name -imatch "[._]neutral\.(appx|msix)$" } | Select-Object -First 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bestPackageEntry) {
|
||||||
|
Write-Host "Found inner package: $($bestPackageEntry.Name). Extracting to read its manifest."
|
||||||
|
$innerPackageTempPath = Join-Path -Path $tempBasePath -ChildPath $bestPackageEntry.Name
|
||||||
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($bestPackageEntry, $innerPackageTempPath, $true)
|
||||||
|
|
||||||
|
$innerPackageArchive = [System.IO.Compression.ZipFile]::OpenRead($innerPackageTempPath)
|
||||||
|
try {
|
||||||
|
$manifestEntry = $innerPackageArchive.Entries | Where-Object { $_.Name -eq 'AppxManifest.xml' } | Select-Object -First 1
|
||||||
|
if ($manifestEntry) {
|
||||||
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($manifestEntry, $manifestTempPath, $true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$innerPackageArchive.Dispose()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Error "Could not find a suitable architecture-specific package inside '$($mainPackage.Name)'."
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$bundleArchive.Dispose()
|
||||||
|
}
|
||||||
|
} else { # It's a regular .appx or .msix
|
||||||
|
$zipArchive = [System.IO.Compression.ZipFile]::OpenRead($mainPackage.FullName)
|
||||||
|
try {
|
||||||
|
$manifestEntry = $zipArchive.Entries | Where-Object { $_.Name -eq 'AppxManifest.xml' } | Select-Object -First 1
|
||||||
|
if ($manifestEntry) {
|
||||||
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($manifestEntry, $manifestTempPath, $true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$zipArchive.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Common manifest parsing logic
|
||||||
|
if (Test-Path $manifestTempPath) {
|
||||||
|
[xml]$manifest = Get-Content -Path $manifestTempPath
|
||||||
|
$nsm = [System.Xml.XmlNamespaceManager]::new($manifest.NameTable)
|
||||||
|
$nsm.AddNamespace("def", "http://schemas.microsoft.com/appx/manifest/foundation/windows10")
|
||||||
|
|
||||||
|
$dependenciesNode = $manifest.SelectSingleNode("//def:Dependencies", $nsm)
|
||||||
|
if ($dependenciesNode) {
|
||||||
|
$requiredDependencies = $dependenciesNode.SelectNodes("def:PackageDependency", $nsm)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Error "Could not find or extract AppxManifest.xml from '$($mainPackage.FullName)'."
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to read or parse manifest from '$($mainPackage.FullName)'. Error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $requiredDependencies) {
|
||||||
|
Write-Warning "Could not read dependencies from manifest for '$($mainPackage.Name)'. Proceeding without explicit dependencies."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve all required dependencies
|
||||||
|
$resolvedDependencyPaths = [System.Collections.Generic.List[string]]::new()
|
||||||
|
foreach ($req in $requiredDependencies) {
|
||||||
|
$reqName = $req.Name
|
||||||
|
$reqMinVersion = [System.Version]$req.MinVersion
|
||||||
|
Write-Host "Resolving dependency: $reqName (MinVersion: $reqMinVersion)"
|
||||||
|
|
||||||
|
if ($allAvailableDependencies.ContainsKey($reqName)) {
|
||||||
|
# Find all available packages that meet the minimum version and architecture requirements
|
||||||
|
$candidates = $allAvailableDependencies[$reqName] | Where-Object {
|
||||||
|
$_.Version -ge $reqMinVersion -and
|
||||||
|
$_.Arch -in ($applicableArchitectures + 'neutral')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($candidates) {
|
||||||
|
# Group by architecture and find the single latest version for each applicable arch
|
||||||
|
$bestCandidates = $candidates | Group-Object -Property Arch | ForEach-Object {
|
||||||
|
$_.Group | Sort-Object -Property Version -Descending | Select-Object -First 1
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($best in $bestCandidates) {
|
||||||
|
Write-Host " - Found best match: $($best.Path.Replace($basePath, '...'))"
|
||||||
|
$resolvedDependencyPaths.Add($best.Path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Warning " - No suitable package found for dependency '$reqName' with MinVersion '$reqMinVersion' for applicable architectures."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Warning " - Dependency '$reqName' not found in any scanned dependency folders."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the DISM command
|
||||||
|
$dismParams = @(
|
||||||
|
"/Online"
|
||||||
|
"/Add-ProvisionedAppxPackage"
|
||||||
|
"/PackagePath:`"$($mainPackage.FullName)`""
|
||||||
|
"/Region:all"
|
||||||
|
"/StubPackageOption:installfull"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add resolved dependencies, ensuring no duplicates
|
||||||
|
$resolvedDependencyPaths.ToArray() | Select-Object -Unique | ForEach-Object {
|
||||||
|
$dismParams += "/DependencyPackagePath:`"$_`""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find and add the license file, or skip if not found
|
||||||
|
$licenseFile = Get-ChildItem -Path $appFolder.FullName -Filter "*.xml" -File | Select-Object -First 1
|
||||||
|
if ($licenseFile) {
|
||||||
|
$dismParams += "/LicensePath:`"$($licenseFile.FullName)`""
|
||||||
|
} else {
|
||||||
|
$dismParams += "/SkipLicense"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute the DISM command
|
||||||
|
$dismCommand = "DISM.exe " + ($dismParams -join " ")
|
||||||
|
Write-Host "Constructed DISM command:"
|
||||||
|
Write-Output $dismCommand
|
||||||
|
|
||||||
|
try {
|
||||||
|
Invoke-Expression -Command $dismCommand -ErrorAction Stop
|
||||||
|
Write-Host "Successfully installed $($mainPackage.Name)."
|
||||||
|
} catch {
|
||||||
|
Write-Error "DISM command failed for $($mainPackage.Name). Error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
Write-Output ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Final cleanup
|
||||||
|
Write-Host "Installation process finished."
|
||||||
|
pause
|
||||||
|
Remove-TemporaryFiles
|
||||||
@@ -73,7 +73,7 @@ $allApps = @()
|
|||||||
|
|
||||||
# Read the WinGetWin32Apps.json file if it exists
|
# Read the WinGetWin32Apps.json file if it exists
|
||||||
if (Test-Path -Path $wingetAppsJsonFile) {
|
if (Test-Path -Path $wingetAppsJsonFile) {
|
||||||
Write-Host "Processing WinGetWin32Apps.json..."
|
Write-Host "Processing WinGetWin32Apps.json first..."
|
||||||
try {
|
try {
|
||||||
$wingetApps = Get-Content -Path $wingetAppsJsonFile -Raw -ErrorAction Stop | ConvertFrom-Json
|
$wingetApps = Get-Content -Path $wingetAppsJsonFile -Raw -ErrorAction Stop | ConvertFrom-Json
|
||||||
if ($wingetApps -is [array]) {
|
if ($wingetApps -is [array]) {
|
||||||
@@ -87,8 +87,6 @@ if (Test-Path -Path $wingetAppsJsonFile) {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "Failed to read or parse WinGetWin32Apps.json file: $_"
|
Write-Error "Failed to read or parse WinGetWin32Apps.json file: $_"
|
||||||
# Decide if execution should stop or continue
|
|
||||||
# exit 1
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Host "WinGetWin32Apps.json file not found. Skipping."
|
Write-Host "WinGetWin32Apps.json file not found. Skipping."
|
||||||
@@ -96,7 +94,7 @@ if (Test-Path -Path $wingetAppsJsonFile) {
|
|||||||
|
|
||||||
# Read the UserAppList.json file if it exists
|
# Read the UserAppList.json file if it exists
|
||||||
if (Test-Path -Path $userAppsJsonFile) {
|
if (Test-Path -Path $userAppsJsonFile) {
|
||||||
Write-Host "Processing UserAppList.json..."
|
Write-Host "Processing UserAppList.json next..."
|
||||||
try {
|
try {
|
||||||
$userApps = Get-Content -Path $userAppsJsonFile -Raw -ErrorAction Stop | ConvertFrom-Json
|
$userApps = Get-Content -Path $userAppsJsonFile -Raw -ErrorAction Stop | ConvertFrom-Json
|
||||||
if ($userApps -is [array]) {
|
if ($userApps -is [array]) {
|
||||||
@@ -110,8 +108,6 @@ if (Test-Path -Path $userAppsJsonFile) {
|
|||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "Failed to read or parse UserAppList.json file: $_"
|
Write-Error "Failed to read or parse UserAppList.json file: $_"
|
||||||
# Decide if execution should stop or continue
|
|
||||||
# exit 1
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Host "UserAppList.json file not found. Skipping."
|
Write-Host "UserAppList.json file not found. Skipping."
|
||||||
@@ -168,8 +164,6 @@ foreach ($app in $sortedApps) {
|
|||||||
Write-Host "$($app.Name) exited with exit code: $($result.ExitCode)`r`n"
|
Write-Host "$($app.Name) exited with exit code: $($result.ExitCode)`r`n"
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "Error occurred while installing $($app.Name): $_"
|
Write-Error "Error occurred while installing $($app.Name): $_"
|
||||||
# Decide if execution should stop or continue after an error
|
|
||||||
# exit 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -345,9 +345,6 @@ function Save-HPDriversTask {
|
|||||||
New-Item -Path $extractFolder -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
New-Item -Path $extractFolder -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
||||||
$arguments = "/s /e /f `"$extractFolder`""
|
$arguments = "/s /e /f `"$extractFolder`""
|
||||||
WriteLog "Extracting driver $driverFilePath with args: $arguments"
|
WriteLog "Extracting driver $driverFilePath with args: $arguments"
|
||||||
#DEBUG
|
|
||||||
# wrap $driverFilePath in quotes to handle spaces
|
|
||||||
# $driverFilePath = "`"$driverFilePath`""
|
|
||||||
WriteLog "Running HP Driver Extraction Command: $driverFilePath $arguments"
|
WriteLog "Running HP Driver Extraction Command: $driverFilePath $arguments"
|
||||||
Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments -ErrorAction Stop | Out-Null
|
Invoke-Process -FilePath $driverFilePath -ArgumentList $arguments -ErrorAction Stop | Out-Null
|
||||||
# Start-Process -FilePath $driverFilePath -ArgumentList $arguments -Wait -NoNewWindow -ErrorAction Stop | Out-Null
|
# Start-Process -FilePath $driverFilePath -ArgumentList $arguments -Wait -NoNewWindow -ErrorAction Stop | Out-Null
|
||||||
|
|||||||
Reference in New Issue
Block a user