Correction d'encodage de caractère

Correction de fichier non trouvé
This commit is contained in:
benoit committed 2026-10-17 15:09:58 +02:00
1 parent 8d5014b4aa
commit 2019c565f5
5 files changed
+74 -30

No files matched your search

+2 -2
View File
@@ -250,8 +250,8 @@ foreach ($partitionProfile in $script:PartitionProfiles) {
throw "Le paramètre DeployCenter.ini '$configKey' requis par le profil '$($partitionProfile.Name)' est absent de la configuration."
}
$configuredSizeMB = [int]$script:Config[$configKey]
if ($configuredSizeMB -lt 990) {
throw "Le paramètre DeployCenter.ini '$configKey' doit être d’au moins 990 Mo."
if ($configuredSizeMB -le 0) {
throw "Le paramètre DeployCenter.ini '$configKey' doit être strictement positif."
}
$partition.Size.Value = $configuredSizeMB
$partition.Size.PreviewMB = $configuredSizeMB
+2 -2
View File
@@ -30,8 +30,8 @@ function Read-DeployCenterConfig {
$config.VncPassword = $value
} elseif ($section -ieq 'Partitioning' -and $key -ieq 'WinRESizeMB') {
$sizeMB = 0
if (-not [int]::TryParse($value, [ref]$sizeMB) -or $sizeMB -lt 990) {
throw "Le paramètre [Partitioning] WinRESizeMB doit être un entier supérieur ou égal à 990. Valeur reçue : '$value'."
if (-not [int]::TryParse($value, [ref]$sizeMB) -or $sizeMB -le 0) {
throw "Le paramètre [Partitioning] WinRESizeMB doit être un entier strictement positif. Valeur reçue : '$value'."
}
$config.WinRESizeMB = $sizeMB
}
+59 -22
View File
@@ -80,7 +80,7 @@ function New-DeployCenterDismSourceAccess {
Invoke-DeployCenterNativeCommand -FilePath $mountVolPath -Arguments @(("{0}:\" -f $letter), '/D') `
-TemporaryRoot $TemporaryRoot -LogAction $LogAction
} catch { }
throw "La source Windows n’est pas accessible après l’affectation temporaire de $letter`:."
throw "La source Windows n’est pas accessible après l’affectation temporaire de ${letter}:."
}
if ($mountError) {
Write-DeployCenterDeploymentLog -LogAction $LogAction `
@@ -184,9 +184,11 @@ function Invoke-DeployCenterNativeCommand {
$process.Refresh()
$exitCode = $process.ExitCode
$nativeOutputLines = New-Object System.Collections.Generic.List[string]
$nativeOutputEncoding = [System.Console]::OutputEncoding
if ($null -eq $nativeOutputEncoding) { $nativeOutputEncoding = [System.Text.Encoding]::Default }
foreach ($outputPath in @($stdoutPath, $stderrPath)) {
if (Test-Path -LiteralPath $outputPath -PathType Leaf) {
foreach ($line in [System.IO.File]::ReadAllLines($outputPath)) {
foreach ($line in [System.IO.File]::ReadAllLines($outputPath, $nativeOutputEncoding)) {
if (-not [string]::IsNullOrWhiteSpace($line)) {
[void]$nativeOutputLines.Add([string]$line)
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message ([string]$line) -Level 'INFO'
@@ -213,6 +215,23 @@ function Invoke-DeployCenterNativeCommand {
-Level 'WARNING'
return
}
if ($null -eq $exitCode -and [IO.Path]::GetFileName($FilePath) -ieq 'bcdboot.exe') {
$bootOutput = ($nativeOutputLines -join "`n").Normalize([System.Text.NormalizationForm]::FormD) -replace '\p{Mn}', ''
$bootFilesCreated = $bootOutput -match '(?i)boot files successfully created|fichiers de d\s*marrage.*bien.*cr'
$systemArgumentIndex = [array]::IndexOf($Arguments, '/s')
$bcdStoreExists = $false
if ($systemArgumentIndex -ge 0 -and $systemArgumentIndex + 1 -lt $Arguments.Count) {
$systemRoot = ([string]$Arguments[$systemArgumentIndex + 1]).TrimEnd('\')
$bcdStorePath = "${systemRoot}\EFI\Microsoft\Boot\BCD"
$bcdStoreExists = Test-Path -LiteralPath $bcdStorePath -PathType Leaf
}
if ($bootFilesCreated -or $bcdStoreExists) {
Write-DeployCenterDeploymentLog -LogAction $LogAction `
-Message "bcdboot a créé les fichiers de démarrage, mais WinPE n’a pas fourni de code de sortie exploitable. La création est confirmée." `
-Level 'WARNING'
return
}
}
if ($null -eq $exitCode -or [int]$exitCode -ne 0) {
$diagnostic = @()
if ($isDism -and (Test-Path -LiteralPath $dismLogPath -PathType Leaf)) {
@@ -221,7 +240,8 @@ function Invoke-DeployCenterNativeCommand {
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message ("DISM : {0}" -f $line) -Level 'ERROR'
}
}
throw "La commande '$FilePath' s'est terminée avec le code '$exitCode'. $(($diagnostic -join ' | '))"
$diagnosticMessage = $diagnostic -join ' | '
throw ("La commande {0} s'est terminée avec le code {1}. {2}" -f $FilePath, $exitCode, $diagnosticMessage)
}
} finally {
foreach ($outputPath in @($stdoutPath, $stderrPath)) {
@@ -271,7 +291,7 @@ function Get-DeployCenterTemporaryDriveLetters {
}
if (-not $selected) { throw "Aucune lettre de lecteur libre pour la partition '$($partition.Name)'." }
}
if ($selected -in $lettersByRole.Values) { throw "La lettre $selected`: est affectée à plusieurs partitions du profil." }
if ($selected -in $lettersByRole.Values) { throw "La lettre ${selected}: est affectée à plusieurs partitions du profil." }
$lettersByRole[[string]$partition.Role] = $selected
$occupied[$selected] = $true
}
@@ -357,10 +377,10 @@ function Invoke-DeployCenterWindowsDeployment {
)
if ($HostName -notmatch '^(?=.*[A-Za-z])[A-Za-z0-9](?:[A-Za-z0-9-]{0,13}[A-Za-z0-9])?$') {
throw "Le nom d'hôte doit respecter les règles NetBIOS et ne pas dépasser 15 caractères."
throw 'Le nom de machine doit respecter les règles NetBIOS et ne pas dépasser 15 caractères.'
}
if ([string]$Firmware -ne [string]$Profile.Firmware) {
throw "Le profil '$($Profile.Name)' exige le firmware $($Profile.Firmware), détecté : $Firmware."
throw ("Le profil {0} exige le firmware {1}, détecté : {2}." -f $Profile.Name, $Profile.Firmware, $Firmware)
}
foreach ($requiredRole in @('System','Reserved','Recovery','Windows')) {
$roleCount = @($Profile.Partitions | Where-Object { [string]$_.Role -eq $requiredRole }).Count
@@ -371,8 +391,8 @@ function Invoke-DeployCenterWindowsDeployment {
throw "Le profil doit définir une taille configurée pour la partition Recovery. Le disque n’a pas été modifié."
}
$winrePartitionSizeMB = [long]$recoveryDefinitions[0].Size.Value
if ($winrePartitionSizeMB -lt 990) {
throw "La partition WinRE doit être configurée à au moins 990 Mo. Le disque n’a pas été modifié."
if ($winrePartitionSizeMB -le 0) {
throw "La taille configurée de la partition WinRE doit être strictement positive. Le disque n’a pas été modifié."
}
if (-not $Edition.ImagePath -or -not (Test-Path -LiteralPath ([string]$Edition.ImagePath) -PathType Leaf)) {
throw "Le chemin de l’image Windows sélectionnée est introuvable."
@@ -399,8 +419,6 @@ function Invoke-DeployCenterWindowsDeployment {
if (-not (Test-Path -LiteralPath $dismPath -PathType Leaf)) { $dismPath = 'dism.exe' }
$bcdbootPath = Join-Path $env:WINDIR 'System32\bcdboot.exe'
if (-not (Test-Path -LiteralPath $bcdbootPath -PathType Leaf)) { $bcdbootPath = 'bcdboot.exe' }
$reagentcPath = Join-Path $env:WINDIR 'System32\reagentc.exe'
if (-not (Test-Path -LiteralPath $reagentcPath -PathType Leaf)) { $reagentcPath = 'reagentc.exe' }
$scriptLines = New-Object System.Collections.Generic.List[string]
$scriptLines.Add("select disk $($disk.Number)")
@@ -454,7 +472,7 @@ function Invoke-DeployCenterWindowsDeployment {
$letter = [string]$driveLetters[$role]
$driveRoot = '{0}:\' -f $letter
if (-not (Test-Path -LiteralPath $driveRoot -PathType Container)) {
throw "La lettre temporaire $letter`: n’est pas accessible après le partitionnement. L’installation est interrompue."
throw "La lettre temporaire ${letter}: n’est pas accessible après le partitionnement. L’installation est interrompue."
}
# WinPE peut ne pas exposer les partitions EFI/Recovery dans les
# requêtes Get-Partition par lettre. Leur ordre sur le disque suit
@@ -472,7 +490,7 @@ function Invoke-DeployCenterWindowsDeployment {
throw "Le profil doit définir les rôles System, Windows et Recovery pour permettre l’installation UEFI avec WinRE."
}
if ($ProgressAction) { & $ProgressAction "Application de Windows sur $windowsLetter`:…" }
if ($ProgressAction) { & $ProgressAction "Application de Windows sur ${windowsLetter}:…" }
Invoke-DeployCenterNativeCommand -FilePath $dismPath -Arguments @(
'/Apply-Image', "/ImageFile:$deploymentImagePath", "/Index:$([int]$Edition.Index)", "/ApplyDir:$windowsPath", '/CheckIntegrity'
) -TemporaryRoot $TemporaryRoot -LogAction $LogAction -ProgressAction $ProgressAction
@@ -486,23 +504,35 @@ function Invoke-DeployCenterWindowsDeployment {
$recoveryPartition = $partitionsByLetter[$recoveryLetter]
$recoveryDriveInfo = New-Object System.IO.DriveInfo -ArgumentList "${recoveryLetter}:\"
if (-not $recoveryDriveInfo.IsReady) {
throw "Le volume de récupération $recoveryLetter`: n’est pas prêt pour vérifier l’espace WinRE."
throw "Le volume de récupération ${recoveryLetter}: n’est pas prêt pour vérifier l’espace WinRE."
}
$minimumPartitionBytes = [long](990 * 1MB)
$minimumFreeBytes = [long]($winreFile.Length + (250 * 1MB))
$minimumFreeAfterCopyBytes = [long](250 * 1MB)
$recoveryFreeBytes = [long]$recoveryDriveInfo.AvailableFreeSpace
$expectedFreeBytes = $recoveryFreeBytes - [long]$winreFile.Length
if ([long]$recoveryPartition.Size -lt $minimumPartitionBytes -or
$recoveryFreeBytes -lt $minimumFreeBytes) {
$winreSizeForLog = [math]::Ceiling(([double]$winreFile.Length / 1MB))
$availableAfterCopyMB = [math]::Floor(([double]$expectedFreeBytes / 1MB))
$message = "La partition WinRE configurée à $winrePartitionSizeMB Mo ne respecte pas les prérequis Microsoft : taille de winre.wim $winreSizeForLog Mo, espace libre requis après copie 250 Mo, estimé $availableAfterCopyMB Mo. L’installation est interrompue avant l’activation de WinRE."
$winreSizeForLog = [math]::Ceiling(([double]$winreFile.Length / 1MB))
$availableFreeMB = [math]::Floor(([double]$recoveryFreeBytes / 1MB))
$availableAfterCopyMB = [math]::Floor(([double]$expectedFreeBytes / 1MB))
if ($recoveryFreeBytes -lt [long]$winreFile.Length) {
$message = "Espace insuffisant sur la partition WinRE pour copier winre.wim : fichier de $winreSizeForLog Mo, espace disponible $availableFreeMB Mo. Le déploiement ne peut pas configurer WinRE."
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message $message -Level 'ERROR'
throw $message
}
$winreSizeForLog = [math]::Ceiling(([double]$winreFile.Length / 1MB))
$availableAfterCopyMB = [math]::Floor(([double]$expectedFreeBytes / 1MB))
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message ("Vérification WinRE réussie : partition {0} Mo, winre.wim {1} Mo, espace restant estimé après copie {2} Mo." -f $winrePartitionSizeMB, $winreSizeForLog, $availableAfterCopyMB) -Level 'SUCCESS'
$recommendationWarnings = New-Object System.Collections.Generic.List[string]
if ([long]$recoveryPartition.Size -lt $minimumPartitionBytes) {
$actualPartitionMB = [math]::Floor(([double]$recoveryPartition.Size / 1MB))
[void]$recommendationWarnings.Add("taille de partition : $actualPartitionMB Mo, minimum recommandé : 990 Mo")
}
if ($expectedFreeBytes -lt $minimumFreeAfterCopyBytes) {
[void]$recommendationWarnings.Add("espace libre après copie : $availableAfterCopyMB Mo, minimum recommandé : 250 Mo")
}
if ($recommendationWarnings.Count -gt 0) {
$warningDetails = $recommendationWarnings -join '; '
$message = "Avertissement WinRE : les recommandations Microsoft ne sont pas toutes respectées ($warningDetails). La copie de winre.wim reste possible ; le déploiement continue."
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message $message -Level 'WARNING'
} else {
Write-DeployCenterDeploymentLog -LogAction $LogAction -Message ("Vérification WinRE réussie : partition {0} Mo, winre.wim {1} Mo, espace restant estimé après copie {2} Mo." -f $winrePartitionSizeMB, $winreSizeForLog, $availableAfterCopyMB) -Level 'SUCCESS'
}
if ($ProgressAction) { & $ProgressAction "Configuration du nom d’hôte…" }
$editionArchitecture = if ($Edition.Edition -and $Edition.Edition.Architecture) { [string]$Edition.Edition.Architecture } else { [string]$Edition.Architecture }
@@ -520,6 +550,13 @@ function Invoke-DeployCenterWindowsDeployment {
if ($ProgressAction) { & $ProgressAction 'Configuration de la partition de récupération WinRE…' }
New-Item -Path $recoveryPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
Copy-Item -LiteralPath $sourceWinre -Destination (Join-Path $recoveryPath 'winre.wim') -Force -ErrorAction Stop
$reagentcPath = Join-Path $windowsRoot 'System32\reagentc.exe'
if (-not (Test-Path -LiteralPath $reagentcPath -PathType Leaf)) {
$reagentcPath = Join-Path $env:WINDIR 'System32\reagentc.exe'
}
if (-not (Test-Path -LiteralPath $reagentcPath -PathType Leaf)) {
throw 'REAgentC.exe est introuvable dans Windows appliqué et dans WinPE.'
}
Invoke-DeployCenterNativeCommand -FilePath $reagentcPath -Arguments @(
'/SetReImage', '/Path', $recoveryPath, '/Target', $windowsRoot
) -TemporaryRoot $TemporaryRoot -LogAction $LogAction -ProgressAction $ProgressAction
+2 -2
View File
@@ -58,8 +58,8 @@ function Import-DeployCenterPartitionProfile {
}
'ConfiguredMB' {
if ([string]::IsNullOrWhiteSpace([string]$partition.Size.ConfigKey) -or
[int]$partition.Size.Value -lt 990 -or [int]$partition.Size.PreviewMB -lt 990) {
throw "La taille configurée de la partition '$($partition.Name)' du profil '$ProfileName' doit définir ConfigKey et une valeur d’au moins 990 Mo."
[int]$partition.Size.Value -le 0 -or [int]$partition.Size.PreviewMB -le 0) {
throw "La taille configurée de la partition '$($partition.Name)' du profil '$ProfileName' doit définir ConfigKey et des tailles strictement positives."
}
}
'UserInputGB' {
+9 -2
View File
@@ -48,7 +48,7 @@ Le mot de passe est stocké en clair dans `DeployCenter.ini` : utilisez cette op
## Taille de la partition WinRE
La taille configurée par défaut est de 1 Go (1024 Mo). Elle peut être remplacée dans `DeployCenter.ini`, sous `[Partitioning]`, par `WinRESizeMB=1536` par exemple. Les valeurs doivent être des entiers d’au moins 990 Mo. Après l’application de Windows, DeployCenter vérifie la taille réelle de `winre.wim` et estime l’espace restant après sa copie ; l’installation est interrompue si la partition ne conserve pas au moins 250 Mo libres ou si elle est plus petite que 990 Mo.
La taille configurée par défaut est de 1 Go (1024 Mo). Elle peut être remplacée dans `DeployCenter.ini`, sous `[Partitioning]`, par `WinRESizeMB=1536` par exemple. La valeur doit être un entier strictement positif. Après l’application de Windows, DeployCenter vérifie la taille réelle de `winre.wim` : le déploiement échoue seulement si l’espace libre ne permet pas de copier le fichier. Une partition plus petite que 990 Mo ou un espace libre après copie inférieur à 250 Mo génère un avertissement, puis le déploiement continue.
## Historique récent
@@ -63,6 +63,13 @@ La taille configurée par défaut est de 1 Go (1024 Mo). Elle peut être remplac
- La progression de l’application d’image lit maintenant les pourcentages émis par DISM pendant son exécution et les transmet à la barre WinForms. Si le code de sortie DISM est absent, le message de réussite explicite est aussi pris en compte.
- Le pourcentage DISM est aussi affiché dans le libellé de l’étape de progression, sans ajouter de ligne de journal à chaque mise à jour.
- Vérification de l’espace WinRE via `System.IO.DriveInfo` : `Get-Volume -DriveLetter` ne trouve pas toujours les volumes de récupération cachés dans WinPE, même lorsqu’ils sont montés et accessibles.
- Le déploiement échoue uniquement si l’espace libre est insuffisant pour copier `winre.wim`. Les recommandations Microsoft de 990 Mo pour la partition et 250 Mo libres après copie produisent un avertissement et n’interrompent pas l’installation.
- Sous certains WinPE, `bcdboot` peut confirmer la création des fichiers de démarrage sans que `Start-Process` fournisse son code de sortie. DeployCenter accepte alors le résultat si la sortie confirme la création ou si le magasin BCD est présent sur la partition EFI.
- Correction du message de confirmation `bcdboot` et simplification des messages natifs ; les erreurs de parseur signalées plus loin étaient des conséquences de cette chaîne non terminée.
- Rétablissement du BOM UTF-8 de `Deployment.psm1`, requis pour que Windows PowerShell 5.1 lise correctement les caractères accentués dans WinPE.
- Décodage des sorties natives avec l’encodage de la console WinPE pour préserver les accents ; REAgentC est désormais recherché dans l’image Windows hors ligne avant WinPE.
- Recherche de REAgentC reportée après l’application de Windows, lorsque `C:\Windows\System32\reagentc.exe` existe effectivement.
- La configuration `WinRESizeMB` et les profils acceptent désormais toute taille strictement positive ; le seuil recommandé de 990 Mo est évalué après application de Windows comme avertissement, sans bloquer la configuration.
### Correctifs v3
@@ -123,7 +130,7 @@ La gestion d'UltraVNC est isolée dans `Modules/VNC.psm1`. Le fichier est charg
Les fichiers PSD1 de `Partitioning/Profiles` décrivent les schémas de partitionnement comme des données, sans exécuter de code contenu dans les profils. Le profil par défaut « UEFI / GPT — WinRE avant C: » crée EFI (300 Mo), MSR (16 Mo), WinRE (taille définie dans `DeployCenter.ini`, 1 Go par défaut), puis Windows sur l’espace restant. Le profil « UEFI / GPT — WinRE, C: et D: » demande la taille de C: et attribue le reste à D:, dans l’ordre physique C: puis D:. Les propriétés de taille déclarées dans le profil alimentent un formulaire générique.
Le bouton « Installer » demande une confirmation explicite avant d’effacer le disque sélectionné. Le flux vérifie le firmware, crée les partitions définies par le profil, applique l’image avec DISM, vérifie ensuite que WinRE tient dans la partition configurée avec au moins 250 Mo libres, configure le nom d’hôte, crée les fichiers de démarrage UEFI et active WinRE. Les opérations longues affichent une fenêtre de progression et sont consignées dans le journal. Le moteur actuel prend en charge les profils UEFI/GPT et requiert une image contenant `Windows\System32\Recovery\winre.wim`.
Le bouton « Installer » demande une confirmation explicite avant d’effacer le disque sélectionné. Le flux vérifie le firmware, crée les partitions définies par le profil, applique l’image avec DISM, vérifie que WinRE tient dans sa partition, configure le nom d’hôte, crée les fichiers de démarrage UEFI et active WinRE. L’absence d’espace pour copier `winre.wim` interrompt le déploiement ; le non-respect des recommandations de taille et d’espace libre génère un avertissement. Les opérations longues affichent une fenêtre de progression et sont consignées dans le journal. Le moteur actuel prend en charge les profils UEFI/GPT et requiert une image contenant `Windows\System32\Recovery\winre.wim`.
## Historique des changements