Files
DeployCenter/Modules/UI.psm1
T
benoit e067721763 Ajout du support de unattend
Ajout du script cmd à utiliser pour lancer DeployCenter
2026-10-17 20:41:41 +02:00

1645 lines
78 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function Set-DeployCenterButtonStyle {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.Button]$Button,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[ValidateSet('Button','PrimaryButton','DangerButton')][string]$Role = 'Button'
)
$style = $Theme[$Role]
$Button.BackColor = $style.Background
$Button.ForeColor = $style.Foreground
$Button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$Button.UseVisualStyleBackColor = $false
$Button.FlatAppearance.BorderSize = 1
$Button.FlatAppearance.BorderColor = $style.Border
$Button.FlatAppearance.MouseOverBackColor = $style.HoverBackground
$Button.FlatAppearance.MouseDownBackColor = $style.PressedBackground
if (-not $Button.Enabled) {
$Button.BackColor = $style.DisabledBackground
$Button.ForeColor = $style.DisabledForeground
}
}
function New-DeployCenterComboBox {
param(
[Parameter(Mandatory=$true)][string]$Name,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
[Parameter(Mandatory=$true)][AllowEmptyCollection()][string[]]$Items
)
$comboBox = New-Object System.Windows.Forms.ComboBox
$comboBox.Name = $Name
$comboBox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
$comboBox.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$comboBox.BackColor = $Theme.PanelAlt
$comboBox.ForeColor = $Theme.Text
$comboBox.Font = $Font
$comboBox.IntegralHeight = $false
$comboBox.MaxDropDownItems = 12
foreach ($item in $Items) { [void]$comboBox.Items.Add($item) }
return $comboBox
}
function Set-DeployCenterComboBoxTheme {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme
)
$ComboBox.BackColor = $Theme.PanelAlt
$ComboBox.ForeColor = $Theme.Text
}
function New-DeployCenterPartitionProfileSelector {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
[Parameter(Mandatory=$true)][string[]]$ProfileNames,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$label = New-Object System.Windows.Forms.Label
$label.Text = 'Profil de partitionnement'
$label.Size = New-Object System.Drawing.Size(240, 17)
$label.ForeColor = $Theme.Text
$label.BackColor = [System.Drawing.Color]::Transparent
$label.Font = $Font
$comboBox = New-DeployCenterComboBox -Name 'cmbPartitionProfile' -Theme $Theme -Font $Font -Items $ProfileNames
$Parent.Controls.Add($label)
$Parent.Controls.Add($comboBox)
Set-DeployCenterPartitionProfileSelectorLayout -Label $label -ComboBox $comboBox -Width ($Parent.ClientSize.Width - 24)
return [pscustomobject]@{ Label = $label; ComboBox = $comboBox }
}
function Set-DeployCenterPartitionProfileSelectorLayout {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.Label]$Label,
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
[Parameter(Mandatory=$true)][int]$Width
)
$Label.Location = New-Object System.Drawing.Point(12, 139)
$ComboBox.Location = New-Object System.Drawing.Point(12, 157)
$ComboBox.Size = New-Object System.Drawing.Size([Math]::Max(250, $Width), 26)
}
function New-DeployCenterDeploymentControls {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
[Parameter(Mandatory=$true)][string[]]$ProfileNames,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$diskLabel = New-Object System.Windows.Forms.Label
$diskLabel.Text = 'Disque physique'
$diskLabel.Location = New-Object System.Drawing.Point(12, 20)
$diskLabel.Size = New-Object System.Drawing.Size(220, 18)
$diskLabel.Font = $Font
$diskLabel.ForeColor = $Theme.Text
$diskLabel.BackColor = [System.Drawing.Color]::Transparent
$diskCombo = New-DeployCenterComboBox -Name 'cmbDisk' -Theme $Theme -Font $Font -Items @()
$diskCombo.Location = New-Object System.Drawing.Point(12, 39)
$diskCombo.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 26)
$diskCombo.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$systemLabel = New-Object System.Windows.Forms.Label
$systemLabel.Text = 'Système à installer'
$systemLabel.Location = New-Object System.Drawing.Point(12, 71)
$systemLabel.Size = New-Object System.Drawing.Size(240, 18)
$systemLabel.Font = $Font
$systemLabel.ForeColor = $Theme.Text
$systemLabel.BackColor = [System.Drawing.Color]::Transparent
$chooseSystemButton = New-Object System.Windows.Forms.Button
$chooseSystemButton.Name = 'btnChooseWindows'
$chooseSystemButton.Text = 'Choisir…'
$chooseSystemButton.Size = New-Object System.Drawing.Size(132, 28)
$chooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67)
$chooseSystemButton.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
Set-DeployCenterButtonStyle -Button $chooseSystemButton -Theme $Theme -Role 'Button'
$selectedSystemLabel = New-Object System.Windows.Forms.Label
$selectedSystemLabel.Name = 'lblSelectedWindows'
$selectedSystemLabel.Text = 'Aucun système sélectionné'
$selectedSystemLabel.Location = New-Object System.Drawing.Point(12, 98)
$selectedSystemLabel.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 22)
$selectedSystemLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$selectedSystemLabel.Font = $Font
$selectedSystemLabel.AutoEllipsis = $true
$selectedSystemLabel.ForeColor = $Theme.Secondary
$selectedSystemLabel.BackColor = [System.Drawing.Color]::Transparent
$profileSelector = New-DeployCenterPartitionProfileSelector -Parent $Parent -ProfileNames $ProfileNames -Theme $Theme -Font $Font
$profileSelector.ComboBox.Enabled = $false
$unattendLabel = New-Object System.Windows.Forms.Label
$unattendLabel.Name = 'lblUnattendProfile'
$unattendLabel.Text = 'Configuration de première ouverture'
$unattendLabel.Location = New-Object System.Drawing.Point(12, 190)
$unattendLabel.Size = New-Object System.Drawing.Size(320, 18)
$unattendLabel.Font = $Font
$unattendLabel.ForeColor = $Theme.Text
$unattendLabel.BackColor = [System.Drawing.Color]::Transparent
$unattendCombo = New-DeployCenterComboBox -Name 'cmbUnattendProfile' -Theme $Theme -Font $Font `
-Items @('Avec OOBE')
$unattendCombo.Location = New-Object System.Drawing.Point(12, 207)
$unattendCombo.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 26)
$unattendCombo.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$unattendCombo.SelectedIndex = 0
$hostLabel = New-Object System.Windows.Forms.Label
$hostLabel.Name = 'lblHostName'
$hostLabel.Text = "Nom d’hôte"
$hostLabel.Location = New-Object System.Drawing.Point(12, 240)
$hostLabel.Size = New-Object System.Drawing.Size(220, 24)
$hostLabel.Font = $Font
$hostLabel.ForeColor = $Theme.Text
$hostLabel.BackColor = [System.Drawing.Color]::Transparent
$hostTextBox = New-Object System.Windows.Forms.TextBox
$hostTextBox.Name = 'txtHostName'
$hostTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 237)
$hostTextBox.Size = New-Object System.Drawing.Size(190, 26)
$hostTextBox.MaxLength = 15
$hostTextBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
$hostTextBox.BackColor = $Theme.PanelAlt
$hostTextBox.ForeColor = $Theme.Text
$hostTextBox.Font = $Font
$hostLabel.Visible = $false
$hostTextBox.Visible = $false
$Parent.Controls.AddRange(@($diskLabel, $diskCombo, $systemLabel, $chooseSystemButton, $selectedSystemLabel, $unattendLabel, $unattendCombo, $hostLabel, $hostTextBox))
return [pscustomobject]@{
DiskComboBox = $diskCombo
ChooseSystemButton = $chooseSystemButton
SelectedSystemLabel = $selectedSystemLabel
PartitionProfileLabel = $profileSelector.Label
PartitionProfileComboBox = $profileSelector.ComboBox
UnattendProfileLabel = $unattendLabel
UnattendProfileComboBox = $unattendCombo
HostNameLabel = $hostLabel
HostNameTextBox = $hostTextBox
}
}
function Set-DeployCenterUnattendHostNameVisibility {
param([Parameter(Mandatory=$true)]$Controls, [Parameter(Mandatory=$true)][bool]$ShowHostName)
$Controls.HostNameLabel.Visible = $ShowHostName
$Controls.HostNameTextBox.Visible = $ShowHostName
}
function Set-DeployCenterUnattendProfileChoices {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
[Parameter(Mandatory=$true)][AllowEmptyCollection()][object[]]$Profiles,
[AllowNull()][string]$EditionName,
[Parameter(Mandatory=$true)][string]$OobeChoice
)
$isServerEdition = -not [string]::IsNullOrWhiteSpace($EditionName) -and
$EditionName.IndexOf('server', [StringComparison]::OrdinalIgnoreCase) -ge 0
$target = if ($isServerEdition) { 'Server' } else { 'Workstation' }
$choices = @($OobeChoice)
if (-not [string]::IsNullOrWhiteSpace($EditionName)) {
$choices += @($Profiles | Where-Object { [string]$_.Target -eq $target } | ForEach-Object { [string]$_.Name })
}
$previousChoice = [string]$ComboBox.SelectedItem
$ComboBox.BeginUpdate()
try {
$ComboBox.Items.Clear()
foreach ($choice in $choices) { [void]$ComboBox.Items.Add($choice) }
$selectionIndex = $ComboBox.Items.IndexOf($previousChoice)
if ($selectionIndex -lt 0) { $selectionIndex = 0 }
$ComboBox.SelectedIndex = $selectionIndex
} finally {
$ComboBox.EndUpdate()
}
}
function Set-DeployCenterDeploymentControlsLayout {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
[Parameter(Mandatory=$true)]$Controls
)
$width = [Math]::Max(250, $Parent.ClientSize.Width - 24)
$Controls.DiskComboBox.Size = New-Object System.Drawing.Size($width, 26)
$Controls.ChooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67)
$Controls.SelectedSystemLabel.Size = New-Object System.Drawing.Size($width, 22)
Set-DeployCenterPartitionProfileSelectorLayout -Label $Controls.PartitionProfileLabel -ComboBox $Controls.PartitionProfileComboBox -Width $width
$Controls.UnattendProfileComboBox.Size = New-Object System.Drawing.Size($width, 26)
$Controls.HostNameTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 237)
$Controls.HostNameTextBox.Size = New-Object System.Drawing.Size(190, 26)
}
function New-DeployCenterLogWindow {
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Journal DeployCenter'
$form.StartPosition = 'CenterParent'
$form.Size = New-Object System.Drawing.Size(900, 500)
$form.MinimumSize = New-Object System.Drawing.Size(560, 300)
$form.ShowInTaskbar = $false
$form.BackColor = $Theme.Background
$form.ForeColor = $Theme.Text
$form.Font = $Font
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Name = 'txtLog'
$textBox.Location = New-Object System.Drawing.Point(12, 12)
$textBox.Size = New-Object System.Drawing.Size(860, 440)
$textBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
$textBox.Multiline = $true
$textBox.ReadOnly = $true
$textBox.WordWrap = $false
$textBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both
$textBox.BackColor = $Theme.PanelAlt
$textBox.ForeColor = $Theme.Text
$textBox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
$textBox.Font = [System.Drawing.Font]::new('Consolas', [single]9, [System.Drawing.FontStyle]::Regular)
$form.Controls.Add($textBox)
$form.Add_FormClosing({ if ($_.CloseReason -eq [System.Windows.Forms.CloseReason]::UserClosing) { $_.Cancel = $true; $form.Hide() } }.GetNewClosure())
return [pscustomobject]@{ Form = $form; TextBox = $textBox }
}
function Show-DeployCenterPartitionSizingDialog {
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
[Parameter(Mandatory=$true)][double]$AvailableGB,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
[System.Windows.Forms.Form]$Owner = $null,
[System.Collections.IDictionary]$InitialValues = @{}
)
$inputs = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'UserInputGB' })
$remainder = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'RemainderAfterInputs' })[0]
if ($inputs.Count -eq 0 -or $null -eq $remainder) { return $null }
$minimumRemainder = [double]$remainder.Size.MinimumGB
$minimumInputs = 0.0
foreach ($inputDefinition in $inputs) { $minimumInputs += [double]$inputDefinition.Size.MinimumGB }
if ($AvailableGB -lt ($minimumInputs + $minimumRemainder)) {
Show-DeployCenterMessage `
-Message "L'espace disponible sur ce disque est insuffisant pour le profil '$($Profile.Name)'." `
-Title 'Partitionnement impossible' -Icon Warning -Owner $Owner -Theme $Theme -Font $Font
return $null
}
$dialog = New-Object System.Windows.Forms.Form
$dialog.Text = 'Tailles des partitions'
$dialog.StartPosition = if ($Owner) { 'CenterParent' } else { 'CenterScreen' }
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$dialog.ShowInTaskbar = $false
$dialog.MinimizeBox = $false
$dialog.MaximizeBox = $false
$dialog.BackColor = $Theme.Background
$dialog.ForeColor = $Theme.Text
$dialog.Font = $Font
$dialog.ClientSize = New-Object System.Drawing.Size(470, (152 + ($inputs.Count * 42)))
$capacityLabel = New-Object System.Windows.Forms.Label
$capacityLabel.Location = New-Object System.Drawing.Point(18, 16)
$capacityLabel.Size = New-Object System.Drawing.Size(430, 34)
$recoveryPartition = @($Profile.Partitions | Where-Object { [string]$_.Role -eq 'Recovery' }) | Select-Object -First 1
$winreSizeMB = if ($recoveryPartition) { [int]$recoveryPartition.Size.Value } else { 1024 }
$capacityLabel.Text = "Capacité estimée après réservation EFI, MSR et WinRE : {0:N1} Go.`r`nTaille WinRE configurée : {1} Mo." -f $AvailableGB, $winreSizeMB
$capacityLabel.ForeColor = $Theme.Text
$capacityLabel.BackColor = [System.Drawing.Color]::Transparent
$capacityLabel.Font = $Font
$dialog.Controls.Add($capacityLabel)
$inputControls = [System.Collections.Generic.List[object]]::new()
for ($i = 0; $i -lt $inputs.Count; $i++) {
$definition = $inputs[$i]
$rowY = 60 + ($i * 42)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(18, ($rowY + 4))
$label.Size = New-Object System.Drawing.Size(250, 26)
$label.Text = [string]$definition.Size.Label
$label.ForeColor = $Theme.Text
$label.BackColor = [System.Drawing.Color]::Transparent
$label.Font = $Font
$dialog.Controls.Add($label)
$otherMinimums = 0.0
for ($j = 0; $j -lt $inputs.Count; $j++) {
if ($j -ne $i) { $otherMinimums += [double]$inputs[$j].Size.MinimumGB }
}
$maximum = [math]::Floor($AvailableGB - $minimumRemainder - $otherMinimums)
$minimum = [double]$definition.Size.MinimumGB
$numeric = New-Object System.Windows.Forms.NumericUpDown
$numeric.Location = New-Object System.Drawing.Point(292, $rowY)
$numeric.Size = New-Object System.Drawing.Size(150, 28)
$numeric.Minimum = [decimal]$minimum
$numeric.Maximum = [decimal]$maximum
$numeric.DecimalPlaces = 0
$numeric.Increment = 1
$numeric.BackColor = $Theme.PanelAlt
$numeric.ForeColor = $Theme.Text
$numeric.Font = $Font
$initialValue = $null
if ($InitialValues.Contains([string]$definition.Role)) { $initialValue = [double]$InitialValues[[string]$definition.Role] }
if ($null -eq $initialValue) {
$initialValue = [math]::Floor($AvailableGB * ([double]$definition.Size.DefaultPercent / 100))
}
$initialValue = [math]::Max($minimum, [math]::Min($maximum, $initialValue))
$numeric.Value = [decimal]$initialValue
$inputControls.Add([pscustomobject]@{ Role = [string]$definition.Role; Control = $numeric })
$dialog.Controls.Add($numeric)
}
$remainderLabel = New-Object System.Windows.Forms.Label
$remainderLabel.Location = New-Object System.Drawing.Point(18, (62 + ($inputs.Count * 42)))
$remainderLabel.Size = New-Object System.Drawing.Size(430, 26)
$remainderLabel.ForeColor = $Theme.Secondary
$remainderLabel.BackColor = [System.Drawing.Color]::Transparent
$remainderLabel.Font = $Font
$dialog.Controls.Add($remainderLabel)
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
$buttonPanel.Height = 58
$buttonPanel.BackColor = $Theme.PanelAlt
$dialog.Controls.Add($buttonPanel)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Text = 'Annuler'
$cancelButton.Location = New-Object System.Drawing.Point(258, 10)
$cancelButton.Size = New-Object System.Drawing.Size(92, 36)
$cancelButton.Font = $Font
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
Set-DeployCenterButtonStyle -Button $cancelButton -Theme $Theme -Role 'Button'
$buttonPanel.Controls.Add($cancelButton)
$validateButton = New-Object System.Windows.Forms.Button
$validateButton.Text = 'Valider'
$validateButton.Location = New-Object System.Drawing.Point(358, 10)
$validateButton.Size = New-Object System.Drawing.Size(92, 36)
$validateButton.Font = $Font
Set-DeployCenterButtonStyle -Button $validateButton -Theme $Theme -Role 'PrimaryButton'
$buttonPanel.Controls.Add($validateButton)
$dialog.Tag = [pscustomobject]@{ InputControls = $inputControls; AvailableGB = $AvailableGB }
$updateRemainder = {
$sum = 0.0
foreach ($inputControl in $dialog.Tag.InputControls) {
$inputValue = [double]$inputControl.Control.Value
$typedValue = 0.0
if ([double]::TryParse([string]$inputControl.Control.Text, [ref]$typedValue)) { $inputValue = $typedValue }
$sum += $inputValue
}
$remainingGB = $dialog.Tag.AvailableGB - $sum
$remainderDrive = [string]$remainder.DriveLetter
$remainderName = if ($remainderDrive) { "$remainderDrive`:" } else { [string]$remainder.Name }
$remainderLabel.Text = "{0} : {1:N1} Go (espace restant)" -f $remainderName, $remainingGB
$remainderLabel.ForeColor = if ($remainingGB -lt $minimumRemainder) { $Theme.Danger } else { $Theme.Secondary }
$validateButton.Enabled = ($remainingGB -ge $minimumRemainder)
}.GetNewClosure()
foreach ($inputControl in $inputControls) {
$inputControl.Control.Add_ValueChanged($updateRemainder)
$inputControl.Control.Add_TextChanged($updateRemainder)
}
& $updateRemainder
$validateButton.Add_Click({
$inputValues = @{}
foreach ($inputControl in $dialog.Tag.InputControls) { $inputValues[$inputControl.Role] = [double]$inputControl.Control.Value }
$sum = 0.0
foreach ($value in $inputValues.Values) { $sum += [double]$value }
$dialog.Tag = [pscustomobject]@{
Inputs = $inputValues
RemainderRole = [string]$remainder.Role
RemainderGB = [math]::Round(($dialog.Tag.AvailableGB - $sum), 1)
AvailableGB = $dialog.Tag.AvailableGB
}
$dialog.DialogResult = [System.Windows.Forms.DialogResult]::OK
$dialog.Close()
})
$dialog.AcceptButton = $validateButton
$dialog.CancelButton = $cancelButton
if ($Owner) { $result = $dialog.ShowDialog($Owner) } else { $result = $dialog.ShowDialog() }
$selection = if ($result -eq [System.Windows.Forms.DialogResult]::OK) { $dialog.Tag } else { $null }
$dialog.Dispose()
return $selection
}
function Show-DeployCenterMessage {
param(
[Parameter(Mandatory=$true)][string]$Message,
[Parameter(Mandatory=$true)][string]$Title,
[ValidateSet('Information','Warning','Error')][string]$Icon = 'Information',
[System.Windows.Forms.Form]$Owner = $null,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$messageForm = New-Object System.Windows.Forms.Form
$messageForm.Text = $Title
$messageForm.StartPosition = if ($Owner) { 'CenterParent' } else { 'CenterScreen' }
$messageForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$messageForm.ShowInTaskbar = $false
$messageForm.MinimizeBox = $false
$messageForm.MaximizeBox = $false
$messageForm.BackColor = $Theme.Background
$messageForm.ForeColor = $Theme.Text
$messageForm.Font = $Font
$messageWidth = 390
$textSize = [System.Windows.Forms.TextRenderer]::MeasureText(
$Message,
$Font,
(New-Object System.Drawing.Size($messageWidth, 0)),
[System.Windows.Forms.TextFormatFlags]::WordBreak
)
$messageHeight = [Math]::Max(42, $textSize.Height + 8)
$messageForm.ClientSize = New-Object System.Drawing.Size(470, ($messageHeight + 94))
$iconLabel = New-Object System.Windows.Forms.Label
$iconLabel.Location = New-Object System.Drawing.Point(18, 24)
$iconLabel.Size = New-Object System.Drawing.Size(34, 34)
$iconLabel.Text = if ($Icon -eq 'Information') { 'i' } else { '!' }
$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', [single]16, [System.Drawing.FontStyle]::Bold)
$iconLabel.ForeColor = if ($Icon -eq 'Error') { $Theme.Danger } else { $Theme.Accent }
$iconLabel.BackColor = [System.Drawing.Color]::Transparent
$messageForm.Controls.Add($iconLabel)
$messageLabel = New-Object System.Windows.Forms.Label
$messageLabel.Location = New-Object System.Drawing.Point(64, 20)
$messageLabel.Size = New-Object System.Drawing.Size($messageWidth, $messageHeight)
$messageLabel.Text = $Message
$messageLabel.ForeColor = $Theme.Text
$messageLabel.BackColor = [System.Drawing.Color]::Transparent
$messageLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$messageLabel.Font = $Font
$messageForm.Controls.Add($messageLabel)
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
$buttonPanel.Height = 58
$buttonPanel.BackColor = $Theme.PanelAlt
$messageForm.Controls.Add($buttonPanel)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Text = 'OK'
$okButton.Location = New-Object System.Drawing.Point(362, 10)
$okButton.Size = New-Object System.Drawing.Size(92, 36)
$okButton.Font = $Font
$okButton.Cursor = [System.Windows.Forms.Cursors]::Hand
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
Set-DeployCenterButtonStyle -Button $okButton -Theme $Theme -Role 'PrimaryButton'
$buttonPanel.Controls.Add($okButton)
$messageForm.AcceptButton = $okButton
$messageForm.CancelButton = $okButton
if ($Owner) {
[void]$messageForm.ShowDialog($Owner)
} else {
[void]$messageForm.ShowDialog()
}
$messageForm.Dispose()
}
function Show-DeployCenterDeploymentComplete {
param(
[Parameter(Mandatory=$true)][string]$Message,
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$dialog = New-Object System.Windows.Forms.Form
$dialog.Text = 'Déploiement terminé'
$dialog.StartPosition = 'CenterParent'
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$dialog.ShowInTaskbar = $false
$dialog.MinimizeBox = $false
$dialog.MaximizeBox = $false
$dialog.BackColor = $Theme.Background
$dialog.ForeColor = $Theme.Text
$dialog.Font = $Font
$messageWidth = 390
$textSize = [System.Windows.Forms.TextRenderer]::MeasureText(
$Message, $Font, (New-Object System.Drawing.Size($messageWidth, 0)),
[System.Windows.Forms.TextFormatFlags]::WordBreak
)
$messageHeight = [Math]::Max(42, $textSize.Height + 8)
$dialog.ClientSize = New-Object System.Drawing.Size(470, ($messageHeight + 126))
$iconLabel = New-Object System.Windows.Forms.Label
$iconLabel.Location = New-Object System.Drawing.Point(18, 24)
$iconLabel.Size = New-Object System.Drawing.Size(34, 34)
$iconLabel.Text = 'i'
$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', [single]16, [System.Drawing.FontStyle]::Bold)
$iconLabel.ForeColor = $Theme.Accent
$iconLabel.BackColor = [System.Drawing.Color]::Transparent
$dialog.Controls.Add($iconLabel)
$messageLabel = New-Object System.Windows.Forms.Label
$messageLabel.Location = New-Object System.Drawing.Point(64, 20)
$messageLabel.Size = New-Object System.Drawing.Size($messageWidth, $messageHeight)
$messageLabel.Text = $Message
$messageLabel.ForeColor = $Theme.Text
$messageLabel.BackColor = [System.Drawing.Color]::Transparent
$messageLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$messageLabel.Font = $Font
$dialog.Controls.Add($messageLabel)
$restartCheckBox = New-Object System.Windows.Forms.CheckBox
$restartCheckBox.Text = 'Redémarrer cet ordinateur après fermeture'
$restartCheckBox.Location = New-Object System.Drawing.Point(64, ($messageHeight + 34))
$restartCheckBox.Size = New-Object System.Drawing.Size(380, 26)
$restartCheckBox.Checked = $true
$restartCheckBox.ForeColor = $Theme.Text
$restartCheckBox.BackColor = [System.Drawing.Color]::Transparent
$restartCheckBox.Font = $Font
$dialog.Controls.Add($restartCheckBox)
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
$buttonPanel.Height = 58
$buttonPanel.BackColor = $Theme.PanelAlt
$dialog.Controls.Add($buttonPanel)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Text = 'OK'
$okButton.Location = New-Object System.Drawing.Point(362, 10)
$okButton.Size = New-Object System.Drawing.Size(92, 36)
$okButton.Font = $Font
$okButton.Cursor = [System.Windows.Forms.Cursors]::Hand
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
Set-DeployCenterButtonStyle -Button $okButton -Theme $Theme -Role 'PrimaryButton'
$buttonPanel.Controls.Add($okButton)
$dialog.AcceptButton = $okButton
$dialog.CancelButton = $okButton
try {
$result = $dialog.ShowDialog($Owner)
return ($result -eq [System.Windows.Forms.DialogResult]::OK -and $restartCheckBox.Checked)
} finally {
$dialog.Dispose()
}
}
function Show-DeployCenterConfirmation {
param(
[Parameter(Mandatory=$true)][string]$Title,
[Parameter(Mandatory=$true)][string]$Message,
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$dialog = New-Object System.Windows.Forms.Form
$dialog.Text = $Title
$dialog.StartPosition = 'CenterParent'
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$dialog.ShowInTaskbar = $false
$dialog.MinimizeBox = $false
$dialog.MaximizeBox = $false
$dialog.ClientSize = New-Object System.Drawing.Size(540, 210)
$dialog.BackColor = $Theme.Background
$dialog.ForeColor = $Theme.Text
$dialog.Font = $Font
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(18, 18)
$label.Size = New-Object System.Drawing.Size(504, 120)
$label.Text = $Message
$label.ForeColor = $Theme.Text
$label.BackColor = [System.Drawing.Color]::Transparent
$label.Font = $Font
$dialog.Controls.Add($label)
$cancel = New-Object System.Windows.Forms.Button
$cancel.Text = 'Annuler'
$cancel.Location = New-Object System.Drawing.Point(310, 158)
$cancel.Size = New-Object System.Drawing.Size(100, 36)
$cancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
Set-DeployCenterButtonStyle -Button $cancel -Theme $Theme -Role 'Button'
$dialog.Controls.Add($cancel)
$confirm = New-Object System.Windows.Forms.Button
$confirm.Text = 'Effacer et installer'
$confirm.Location = New-Object System.Drawing.Point(420, 158)
$confirm.Size = New-Object System.Drawing.Size(102, 36)
$confirm.DialogResult = [System.Windows.Forms.DialogResult]::Yes
Set-DeployCenterButtonStyle -Button $confirm -Theme $Theme -Role 'DangerButton'
$dialog.Controls.Add($confirm)
$dialog.CancelButton = $cancel
$dialog.AcceptButton = $confirm
return ($dialog.ShowDialog($Owner) -eq [System.Windows.Forms.DialogResult]::Yes)
}
function New-DeployCenterDeploymentProgress {
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Déploiement Windows'
$form.StartPosition = 'CenterParent'
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$form.ControlBox = $false
$form.ShowInTaskbar = $false
$form.ClientSize = New-Object System.Drawing.Size(520, 116)
$form.BackColor = $Theme.Background
$form.ForeColor = $Theme.Text
$form.Font = $Font
$status = New-Object System.Windows.Forms.Label
$status.Location = New-Object System.Drawing.Point(16, 16)
$status.Size = New-Object System.Drawing.Size(488, 28)
$status.Text = 'Préparation du déploiement…'
$status.AutoEllipsis = $true
$status.ForeColor = $Theme.Text
$status.BackColor = [System.Drawing.Color]::Transparent
$status.Font = $Font
$form.Controls.Add($status)
$bar = New-Object System.Windows.Forms.ProgressBar
$bar.Location = New-Object System.Drawing.Point(16, 56)
$bar.Size = New-Object System.Drawing.Size(488, 24)
$bar.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
$bar.MarqueeAnimationSpeed = 25
$form.Controls.Add($bar)
return [pscustomobject]@{ Form = $form; StatusLabel = $status; ProgressBar = $bar; Action = $null }
}
function Set-DeployCenterFormSizing {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Form,
[switch]$Resizable
)
if ($Resizable) {
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable
$Form.MaximizeBox = $true
} else {
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.MaximizeBox = $false
}
}
function Add-SessionFooter {
param(
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$TargetForm,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
[AllowNull()][string]$IPv4,
[AllowNull()][string]$VncPassword,
[bool]$ShowFooter = $true,
[bool]$ShowPassword = $true
)
if (-not $ShowFooter) { return }
$footer = New-Object System.Windows.Forms.Label
$footer.Name = 'DeployCenterSessionFooter'
$footer.Text = if ($IPv4) { "IP de déploiement : $IPv4" } else { 'IP de déploiement indisponible' }
if ($ShowPassword -and -not [string]::IsNullOrWhiteSpace($VncPassword)) {
$footer.Text += " | Mot de passe VNC : $VncPassword"
}
$footer.Height = 26
$footer.Dock = [System.Windows.Forms.DockStyle]::Bottom
$footer.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$footer.Padding = New-Object System.Windows.Forms.Padding(12, 0, 0, 0)
$footer.BackColor = $Theme.PanelAlt
$footer.ForeColor = $Theme.Text
$footer.Font = $Font
$TargetForm.Controls.Add($footer)
$footer.BringToFront()
}
function New-Label {
param(
[Parameter(Mandatory=$true)][string]$Text,
[Parameter(Mandatory=$true)][int]$X,
[Parameter(Mandatory=$true)][int]$Y,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
[int]$Width = 180,
[int]$Height = 24,
[AllowNull()]$Color = $null
)
$label = New-Object System.Windows.Forms.Label
$label.Text = $Text
$label.Location = New-Object System.Drawing.Point($X, $Y)
$label.Size = New-Object System.Drawing.Size($Width, $Height)
$label.AutoEllipsis = $true
$label.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$label.BackColor = [System.Drawing.Color]::Transparent
$label.ForeColor = if ($null -eq $Color) { $Theme.Text } else { $Color }
$label.Font = $Font
return $label
}
function New-GroupBox {
param(
[Parameter(Mandatory=$true)][string]$Text,
[Parameter(Mandatory=$true)][int]$X,
[Parameter(Mandatory=$true)][int]$Y,
[Parameter(Mandatory=$true)][int]$Width,
[Parameter(Mandatory=$true)][int]$Height,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
)
$group = New-Object DeployCenter.ThemedGroupBox
$group.Text = $Text
$group.Location = New-Object System.Drawing.Point($X, $Y)
$group.Size = New-Object System.Drawing.Size($Width, $Height)
$group.ForeColor = $Theme.Text
$group.BackColor = $Theme.Panel
$group.PanelColor = $Theme.Panel
$group.BorderColor = $Theme.Border
$group.TextColor = $Theme.Text
$group.Font = $Font
return $group
}
function New-Button {
param(
[Parameter(Mandatory=$true)][string]$Text,
[Parameter(Mandatory=$true)][int]$X,
[Parameter(Mandatory=$true)][int]$Y,
[Parameter(Mandatory=$true)][int]$Width,
[Parameter(Mandatory=$true)][int]$Height,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
[ValidateSet('Button','PrimaryButton','DangerButton')][string]$Role = 'Button'
)
$button = New-Object System.Windows.Forms.Button
$button.Text = $Text
$button.Location = New-Object System.Drawing.Point($X, $Y)
$button.Size = New-Object System.Drawing.Size($Width, $Height)
$button.Tag = $Role
Set-DeployCenterButtonStyle -Button $button -Theme $Theme -Role $Role
$button.Font = $Font
$button.Cursor = [System.Windows.Forms.Cursors]::Hand
$button.AutoSize = $false
$button.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
return $button
}
function Get-DeployCenterLogoPathForTheme {
param(
[Parameter(Mandatory=$true)][string]$ThemeName,
[Parameter(Mandatory=$true)][string]$LogoPath,
[Parameter(Mandatory=$true)][string]$LogoDarkPath
)
if ($ThemeName -eq 'Dark' -and (Test-Path -LiteralPath $LogoDarkPath -PathType Leaf)) { return $LogoDarkPath }
if (Test-Path -LiteralPath $LogoPath -PathType Leaf) { return $LogoPath }
if (Test-Path -LiteralPath $LogoDarkPath -PathType Leaf) { return $LogoDarkPath }
return $null
}
function Update-DeployCenterLogo {
param(
[Parameter(Mandatory=$true)][AllowNull()][System.Windows.Forms.PictureBox]$PictureBox,
[Parameter(Mandatory=$true)][string]$ThemeName,
[Parameter(Mandatory=$true)][string]$LogoPath,
[Parameter(Mandatory=$true)][string]$LogoDarkPath
)
if ($null -eq $PictureBox) { return }
$selectedLogoPath = Get-DeployCenterLogoPathForTheme -ThemeName $ThemeName -LogoPath $LogoPath -LogoDarkPath $LogoDarkPath
if ([string]::IsNullOrWhiteSpace($selectedLogoPath)) { return }
try {
$newImage = [System.Drawing.Image]::FromFile($selectedLogoPath)
$oldImage = $PictureBox.Image
$PictureBox.Image = $newImage
if ($null -ne $oldImage) { $oldImage.Dispose() }
$PictureBox.Visible = $true
} catch {
# Conserver le logo déjà affiché si le nouveau fichier est absent ou invalide.
}
}
# Workflows de l'interface principale ; dot-sourcés pour partager explicitement l'état de session DeployCenter.
function Set-DiskSelectorWidth {
if ($null -eq $script:cmbDisk -or $null -eq $script:groupDisks) { return }
Set-DeployCenterDeploymentControlsLayout -Parent $script:groupDisks -Controls $deploymentControls
}
function Set-ControlTheme {
param([System.Windows.Forms.Control]$Control)
# Le formulaire principal doit lui aussi changer de fond.
if ($Control -is [System.Windows.Forms.Form]) {
$Control.BackColor = $script:Theme.Background
$Control.ForeColor = $script:Theme.Text
}
elseif ($Control -is [DeployCenter.ThemedGroupBox]) {
$Control.BackColor = $script:Theme.Panel
$Control.PanelColor = $script:Theme.Panel
$Control.BorderColor = $script:Theme.Border
$Control.TextColor = $script:Theme.Text
$Control.ForeColor = $script:Theme.Text
$Control.Invalidate()
}
elseif ($Control -is [System.Windows.Forms.Label]) {
$Control.ForeColor = $script:Theme.Text
if ($Control.Name -eq 'lblSelectedWindows') {
$Control.ForeColor = if ($script:SelectedWindowsEdition) { $script:Theme.Success } else { $script:Theme.Secondary }
}
# Le pied de page VNC suit lui aussi la palette du thème actif.
if ($Control.Name -eq 'DeployCenterSessionFooter') {
$Control.BackColor = $script:Theme.PanelAlt
$Control.ForeColor = $script:Theme.Secondary
$Control.Invalidate()
}
}
elseif ($Control -is [System.Windows.Forms.ListView]) {
$Control.BackColor = $script:Theme.PanelAlt
$Control.ForeColor = $script:Theme.Text
}
elseif ($Control -is [System.Windows.Forms.ComboBox]) {
Set-DeployCenterComboBoxTheme -ComboBox $Control -Theme $script:Theme
}
elseif ($Control -is [System.Windows.Forms.TextBox]) {
$Control.BackColor = $script:Theme.PanelAlt
$Control.ForeColor = $script:Theme.Text
}
foreach ($child in $Control.Controls) {
Set-ControlTheme -Control $child
}
}
function Set-DeployCenterTheme {
param([Parameter(Mandatory=$true)][string]$ThemeName)
$availableThemes = @(Get-DeployCenterThemeNames -ThemesRoot $script:ThemesRoot)
if ($availableThemes -notcontains $ThemeName) { return }
try {
$script:Theme = Import-DeployCenterTheme -ThemesRoot $script:ThemesRoot -ThemeName $ThemeName
} catch {
& $script:SourceLogAction ("Impossible de charger le theme '$ThemeName' : " + $_.Exception.Message) "ERROR"
return
}
$script:CurrentTheme = $ThemeName
$script:cmbTheme.BackColor = $script:Theme.Panel
$script:cmbTheme.ForeColor = $script:Theme.Text
$script:cmbTheme.Invalidate()
$script:cmbTheme.Refresh()
Set-ControlTheme -Control $script:form
Set-ControlTheme -Control $script:logForm
# Met a jour le logo si une variante propre au theme existe.
Update-DeployCenterLogo -PictureBox $script:pictureLogo -ThemeName $script:CurrentTheme `
-LogoPath $script:LogoPath -LogoDarkPath $script:LogoDarkPath
$script:lblTitle.ForeColor = $script:Theme.Text
$script:lblSubtitle.ForeColor = $script:Theme.Secondary
Set-DeployCenterButtonStyle -Button $script:btnChooseWindows -Theme $script:Theme -Role 'Button'
Set-DeployCenterButtonStyle -Button $script:btnLog -Theme $script:Theme -Role 'Button'
Set-DeployCenterButtonStyle -Button $script:btnRefresh -Theme $script:Theme -Role 'PrimaryButton'
Set-DeployCenterButtonStyle -Button $script:btnDeploy -Theme $script:Theme -Role 'PrimaryButton'
Set-DeployCenterButtonStyle -Button $script:btnClose -Theme $script:Theme -Role 'DangerButton'
# Actualise explicitement le pied de page principal lors du changement de thème.
foreach ($footer in $script:form.Controls.Find('DeployCenterSessionFooter', $true)) {
$footer.BackColor = $script:Theme.PanelAlt
$footer.ForeColor = $script:Theme.Secondary
$footer.Invalidate()
}
$script:cmbTheme.BackColor = $script:Theme.Panel
$script:cmbTheme.ForeColor = $script:Theme.Text
$script:cmbTheme.Invalidate()
$script:groupPC.Invalidate()
$script:groupDisks.Invalidate()
$script:cmbDisk.BackColor = $script:Theme.PanelAlt
$script:cmbDisk.ForeColor = $script:Theme.Text
$script:cmbDisk.Invalidate()
# Le bouton reste au-dessus des autres controles de l'en-tete.
$script:cmbTheme.BringToFront()
# Forcer le rafraichissement apres le changement de palette.
$script:form.SuspendLayout()
try {
foreach ($control in $script:form.Controls) {
$control.Invalidate($true)
}
$script:form.Invalidate($true)
$script:form.Refresh()
}
finally {
$script:form.ResumeLayout($true)
}
}
function Show-WindowsSourcesDialog {
$dlg = New-Object System.Windows.Forms.Form
$dlg.Text = 'Sources Windows - lecteur DeployCenter et partage SMB'
$dlg.StartPosition = 'CenterParent'
$dlg.Size = New-Object System.Drawing.Size(1100, 700)
$dlg.MinimumSize = New-Object System.Drawing.Size(700, 450)
Set-DeployCenterFormSizing -Form $dlg -Resizable
# Ouvrir directement en plein écran pour exploiter toute la surface disponible.
$dlg.WindowState = [System.Windows.Forms.FormWindowState]::Maximized
$dlg.ShowInTaskbar = $false
$dlg.BackColor = $script:Theme.Background
$dlg.ForeColor = $script:Theme.Text
$dlg.Font = $script:FontRegular
$lblPath = New-Object System.Windows.Forms.Label
$lblPath.Text = 'Chemins à analyser (séparés par ;) :'
$lblPath.Location = New-Object System.Drawing.Point(16, 16)
$lblPath.Size = New-Object System.Drawing.Size(260, 24)
$lblPath.ForeColor = $script:Theme.Text
$dlg.Controls.Add($lblPath)
$txtPath = New-Object System.Windows.Forms.TextBox
$txtPath.Location = New-Object System.Drawing.Point(16, 42)
$txtPath.Size = New-Object System.Drawing.Size(560, 26)
$txtPath.Anchor = 'Top,Left,Right'
$defaultSourceRoot = $script:DeployVolumeRoot
if ([string]::IsNullOrWhiteSpace($defaultSourceRoot)) { $defaultSourceRoot = $script:DeployRoot }
$configuredPaths = [string]$script:Config.SourcePaths
if ([string]::IsNullOrWhiteSpace($configuredPaths)) {
$txtPath.Text = $defaultSourceRoot
} else {
$txtPath.Text = $configuredPaths.Replace("%DEPLOY_VOLUME%", $defaultSourceRoot)
}
$txtPath.BackColor = $script:Theme.PanelAlt
$txtPath.ForeColor = $script:Theme.Text
$dlg.Controls.Add($txtPath)
$btnScan = New-Object System.Windows.Forms.Button
$btnScan.Text = 'Rechercher'
$btnScan.Location = New-Object System.Drawing.Point(650, 40)
$btnScan.Size = New-Object System.Drawing.Size(100, 30)
$btnScan.Anchor = 'Top,Right'
$btnScan.UseVisualStyleBackColor = $false
$btnScan.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$btnScan.FlatAppearance.BorderColor = $script:Theme.Border
$btnScan.BackColor = $script:Theme.Accent
$btnScan.ForeColor = [System.Drawing.Color]::White
$dlg.Controls.Add($btnScan)
$btnEditions = New-Object System.Windows.Forms.Button
$btnEditions.Text = 'Afficher les éditions'
$btnEditions.Location = New-Object System.Drawing.Point(760, 40)
$btnEditions.Size = New-Object System.Drawing.Size(150, 30)
$btnEditions.Anchor = 'Top,Right'
$btnEditions.UseVisualStyleBackColor = $false
$btnEditions.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$btnEditions.FlatAppearance.BorderColor = $script:Theme.Border
$btnEditions.BackColor = $script:Theme.Panel
$btnEditions.ForeColor = $script:Theme.Text
$dlg.Controls.Add($btnEditions)
$list = New-Object System.Windows.Forms.ListView
$list.Location = New-Object System.Drawing.Point(16, 84)
$list.Size = New-Object System.Drawing.Size(770, 420)
$list.Anchor = 'Top,Bottom,Left,Right'
$list.View = 'Details'; $list.FullRowSelect = $true; $list.GridLines = $true
$list.MultiSelect = $false
$list.HideSelection = $true
$list.BackColor = $script:Theme.PanelAlt
$list.ForeColor = $script:Theme.Text
$list.OwnerDraw = $true
[void]$list.Columns.Add('Type', 135)
[void]$list.Columns.Add('Nom', 260)
[void]$list.Columns.Add('Taille', 80)
[void]$list.Columns.Add('Chemin', 250)
# La colonne Nom est prioritaire : elle absorbe l'espace libre.
$resizeSourceColumns = {
if ($list.Columns.Count -ge 4) {
$availableWidth = $list.ClientSize.Width - $list.Columns[0].Width - $list.Columns[2].Width - $list.Columns[3].Width - 4
if ($list.Items.Count -gt 0 -and $list.Items.Count -gt [math]::Floor($list.ClientSize.Height / 20)) {
$availableWidth -= [System.Windows.Forms.SystemInformation]::VerticalScrollBarWidth
}
$list.Columns[1].Width = [Math]::Max(180, $availableWidth)
}
}
$list.Add_SizeChanged($resizeSourceColumns)
$list.Add_HandleCreated($resizeSourceColumns)
# Dessin personnalise des en-tetes et des lignes pour que le ListView
# respecte le theme actif au lieu d'utiliser les en-tetes natifs blancs.
$list.Add_DrawColumnHeader({
param($sender, $e)
$headerColor = if ($script:Theme.Contains('TableHeader')) { $script:Theme.TableHeader } else { $script:Theme.PanelAlt }
$headerBrush = New-Object System.Drawing.SolidBrush($headerColor)
$textBrush = New-Object System.Drawing.SolidBrush($script:Theme.Text)
$borderPen = New-Object System.Drawing.Pen($script:Theme.Border)
try {
$e.Graphics.FillRectangle($headerBrush, $e.Bounds)
$e.Graphics.DrawRectangle($borderPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
$flags = [System.Windows.Forms.TextFormatFlags]::Left -bor [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor [System.Windows.Forms.TextFormatFlags]::EndEllipsis
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, $e.Header.Text, $list.Font, $e.Bounds, $script:Theme.Text, $flags)
} finally {
$headerBrush.Dispose(); $textBrush.Dispose(); $borderPen.Dispose()
}
})
$list.Add_DrawItem({
param($sender, $e)
if ($list.View -ne [System.Windows.Forms.View]::Details) {
$e.DrawDefault = $true
}
})
$list.Add_DrawSubItem({
param($sender, $e)
$rowBack = if (($e.Item.Index % 2) -eq 0) { $script:Theme.TableRowEven } else { $script:Theme.TableRowOdd }
$rowFore = $script:Theme.Text
# Ne surligner que l'element réellement sélectionné, jamais toutes les lignes.
if ($e.Item.Selected -and $list.Focused) {
$rowBack = $script:Theme.Accent
$rowFore = [System.Drawing.Color]::White
}
$rowBrush = New-Object System.Drawing.SolidBrush($rowBack)
$textBrush = New-Object System.Drawing.SolidBrush($rowFore)
$borderPen = New-Object System.Drawing.Pen($script:Theme.Border)
try {
$e.Graphics.FillRectangle($rowBrush, $e.Bounds)
$e.Graphics.DrawRectangle($borderPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
$flags = [System.Windows.Forms.TextFormatFlags]::Left -bor [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor [System.Windows.Forms.TextFormatFlags]::EndEllipsis -bor [System.Windows.Forms.TextFormatFlags]::NoPrefix
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, $e.SubItem.Text, $list.Font, $e.Bounds, $rowFore, $flags)
} finally {
$rowBrush.Dispose(); $textBrush.Dispose(); $borderPen.Dispose()
}
})
# Filtre local : il ne relance jamais l'analyse des lecteurs ou partages.
$lblFilter = New-Object System.Windows.Forms.Label
$lblFilter.Text = 'Filtrer la liste :'
$lblFilter.Location = New-Object System.Drawing.Point(16, 80)
$lblFilter.Size = New-Object System.Drawing.Size(100, 24)
$lblFilter.ForeColor = $script:Theme.Text
$dlg.Controls.Add($lblFilter)
$txtFilter = New-Object System.Windows.Forms.TextBox
$txtFilter.Location = New-Object System.Drawing.Point(120, 78)
$txtFilter.Size = New-Object System.Drawing.Size(500, 26)
$txtFilter.Anchor = 'Top,Left,Right'
$txtFilter.BackColor = $script:Theme.PanelAlt
$txtFilter.ForeColor = $script:Theme.Text
$dlg.Controls.Add($txtFilter)
$list.Location = New-Object System.Drawing.Point(16, 112)
$dlg.Controls.Add($list)
# Etat partage explicite entre les gestionnaires d'evenements WinForms.
# En PowerShell, les callbacks peuvent s'executer dans une portee differente :
# une variable locale $allFound peut alors rester vide dans le filtre.
$sourceState = [hashtable]::Synchronized(@{ AllFound = @() })
$renderSourceList = {
param([string]$Query)
$previousSelection = $null
if ($list.SelectedItems.Count -gt 0) {
$previousSelection = [string]$list.SelectedItems[0].SubItems[3].Text
}
$needle = ([string]$Query).Trim()
$visibleEntries = New-Object System.Collections.Generic.List[object]
$entries = @($sourceState.AllFound)
foreach ($entry in $entries) {
if ([string]::IsNullOrWhiteSpace($needle)) {
[void]$visibleEntries.Add($entry)
continue
}
$searchable = '{0} {1} {2} {3} {4}' -f `
[string]$entry.Type, [string]$entry.Nom, [string]$entry.Chemin, `
[string]$entry.Taille, [string]$entry.Detail
if ($searchable.IndexOf($needle, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
[void]$visibleEntries.Add($entry)
}
}
$list.BeginUpdate()
try {
$list.Items.Clear()
foreach ($entry in $visibleEntries) {
$item = New-Object System.Windows.Forms.ListViewItem([string]$entry.Type)
[void]$item.SubItems.Add([string]$entry.Nom)
[void]$item.SubItems.Add([string]$entry.Taille)
[void]$item.SubItems.Add([string]$entry.Chemin)
if ($previousSelection -and ([string]$entry.Chemin -eq $previousSelection)) {
$item.Selected = $true
}
[void]$list.Items.Add($item)
}
} finally {
$list.EndUpdate()
}
if ($entries.Count -gt 0) {
$lblStatus.Text = ('{0} résultat(s) affiché(s) sur {1}. Filtre instantané ; aucun fichier modifié.' -f $list.Items.Count, $entries.Count)
} elseif ([string]::IsNullOrWhiteSpace($needle)) {
$lblStatus.Text = 'Aucune source ISO/WIM/ESD ou installation extraite detectee dans les chemins accessibles.'
} else {
$lblStatus.Text = ('Aucun resultat pour « {0} ». Efface le filtre pour revoir la liste.' -f $needle)
}
}
$txtFilter.Add_TextChanged({ & $renderSourceList $txtFilter.Text })
$lblStatus = New-Object System.Windows.Forms.Label
$lblStatus.Text = 'Analyse en lecture seule du lecteur de DeployCenter et/ou du partage SMB. Plusieurs chemins peuvent etre separes par un point-virgule.'
$lblStatus.Location = New-Object System.Drawing.Point(16, 515)
$lblStatus.Size = New-Object System.Drawing.Size(770, 36)
$lblStatus.Anchor = 'Bottom,Left,Right'
$lblStatus.AutoSize = $false
$lblStatus.ForeColor = $script:Theme.Secondary
$dlg.Controls.Add($lblStatus)
$progressAnalysis = New-Object System.Windows.Forms.ProgressBar
$progressAnalysis.Name = 'progressWindowsSourceAnalysis'
$progressAnalysis.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
$progressAnalysis.MarqueeAnimationSpeed = 30
$progressAnalysis.Visible = $false
$progressAnalysis.Anchor = 'Bottom,Right'
$dlg.Controls.Add($progressAnalysis)
# Recalculer les positions depuis la taille réelle de la fenêtre :
# le tableau occupe l'espace restant et le statut reste toujours visible en bas.
$resizeLayout = {
$clientWidth = $dlg.ClientSize.Width
$clientHeight = $dlg.ClientSize.Height
$rightMargin = 16
$gap = 12
$btnWidth = 88
$editionsWidth = 148
$left = 12
$right = [Math]::Max($left + $btnWidth, $clientWidth - $rightMargin)
$btnEditionsLeft = [Math]::Max($left, $right - $editionsWidth)
$btnScanLeft = [Math]::Max($left, $btnEditionsLeft - $gap - $btnWidth)
$txtPath.Width = [Math]::Max(100, $btnScanLeft - $left - $gap)
$btnEditions.Location = New-Object System.Drawing.Point($btnEditionsLeft, 40)
$btnEditions.Width = [Math]::Max(120, $right - $btnEditionsLeft)
$btnScan.Location = New-Object System.Drawing.Point($btnScanLeft, 40)
$btnScan.Width = [Math]::Max(80, $btnEditionsLeft - $gap - $btnScanLeft)
$btnScan.Anchor = 'Top,Right'
$btnEditions.Anchor = 'Top,Right'
$txtPath.Anchor = 'Top,Left,Right'
$lblFilter.Location = New-Object System.Drawing.Point(16, 80)
$txtFilter.Location = New-Object System.Drawing.Point(120, 78)
$txtFilter.Size = New-Object System.Drawing.Size([Math]::Max(180, $clientWidth - 152), 26)
$list.Location = New-Object System.Drawing.Point(16, 112)
$list.Size = New-Object System.Drawing.Size([Math]::Max(300, $clientWidth - 32), [Math]::Max(120, $clientHeight - 205))
$list.Anchor = 'Top,Bottom,Left,Right'
$lblStatus.Location = New-Object System.Drawing.Point(16, ($clientHeight - 52))
$lblStatus.Size = New-Object System.Drawing.Size([Math]::Max(150, $clientWidth - 164), 24)
$lblStatus.Anchor = 'Bottom,Left,Right'
$progressAnalysis.Location = New-Object System.Drawing.Point(($clientWidth - 136), ($clientHeight - 52))
$progressAnalysis.Size = New-Object System.Drawing.Size(120, 20)
}
$dlg.Add_Shown({
& $resizeLayout
# Lancer automatiquement la première analyse à l'ouverture.
# Le bouton Rechercher reste disponible si l'utilisateur change les chemins.
$btnScan.PerformClick()
})
$dlg.Add_SizeChanged({ & $resizeLayout })
$btnEditions.Add_Click({
if ($list.SelectedItems.Count -eq 0) {
Show-DeployCenterMessage -Message 'Sélectionne d''abord une source dans la liste.' -Title 'Éditions Windows' -Icon Information -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
return
}
$selectedPath = [string]$list.SelectedItems[0].SubItems[3].Text
$selectedName = [string]$list.SelectedItems[0].SubItems[1].Text
$resolvedImage = $null
$mountTransferred = $false
$btnEditions.Enabled = $false
$btnScan.Enabled = $false
$dlg.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
$lblStatus.Text = "Analyse des editions de $selectedName en cours..."
$progressAnalysis.Visible = $true
$progressAnalysis.BringToFront()
$dlg.Refresh()
try {
$resolvedImage = Resolve-InstallImagePath -SelectedPath $selectedPath -ExistingImage $script:SelectedWindowsEdition
$imagePath = [string]$resolvedImage.ImagePath
$editionResult = Get-WindowsImageEditions -ImagePath $imagePath -LogAction $script:SourceLogAction
$editions = @($editionResult.Editions)
$globalImageVersion = [string]$editionResult.GlobalVersion
$lines = foreach ($edition in $editions) {
$index = if ($edition.ImageIndex) { $edition.ImageIndex } elseif ($edition.Index) { $edition.Index } else { '?' }
$name = if ($edition.ImageName) { $edition.ImageName } elseif ($edition.Name) { $edition.Name } else { 'Nom non disponible' }
$arch = if ($edition.Architecture) { [string]$edition.Architecture } else { 'Architecture non disponible' }
$version = if ($edition.Version) { [string]$edition.Version } else { 'Version non disponible' }
"Index : $index`r`nNom : $name`r`nArchitecture : $arch`r`nVersion : $version"
}
# Le formulaire enfant est modal : actualiser le statut avant son affichage,
# sinon le formulaire parent reste visuellement sur « analyse en cours ».
$lblStatus.Text = "$($editions.Count) édition(s) détectée(s) dans $selectedName. Sélectionne une édition."
$dlg.Refresh()
# Tableau des editions ; l'index DISM selectionne est conserve pour la suite.
$resultForm = New-Object System.Windows.Forms.Form
$resultForm.Text = 'Éditions Windows disponibles'
$resultForm.StartPosition = 'CenterParent'
$resultForm.Size = New-Object System.Drawing.Size(700, 480)
$resultForm.MinimumSize = New-Object System.Drawing.Size(520, 320)
Set-DeployCenterFormSizing -Form $resultForm
$resultForm.ShowInTaskbar = $false
$resultForm.BackColor = $script:Theme.Background
$resultForm.ForeColor = $script:Theme.Text
$sourceLabel = New-Object System.Windows.Forms.Label
$sourceLabel.Text = "Source : $selectedPath"
$sourceLabel.Dock = [System.Windows.Forms.DockStyle]::Top
$sourceLabel.Height = 38
$sourceLabel.Padding = New-Object System.Windows.Forms.Padding(10, 8, 10, 4)
$sourceLabel.AutoEllipsis = $true
$sourceLabel.BackColor = $script:Theme.Background
$sourceLabel.ForeColor = $script:Theme.Text
$editionList = New-Object System.Windows.Forms.ListView
$editionList.View = [System.Windows.Forms.View]::Details
$editionList.FullRowSelect = $true
$editionList.GridLines = $true
$editionList.MultiSelect = $false
$editionList.HideSelection = $false
# Une seule édition peut être sélectionnée à la fois.
$editionList.Sorting = [System.Windows.Forms.SortOrder]::None
$editionList.Dock = [System.Windows.Forms.DockStyle]::Fill
$editionList.BackColor = $script:Theme.Panel
$editionList.ForeColor = $script:Theme.Text
[void]$editionList.Columns.Add('Index', 65)
[void]$editionList.Columns.Add('Édition', 260)
[void]$editionList.Columns.Add('Architecture', 100)
[void]$editionList.Columns.Add('Version', 105)
# Édition est la colonne prioritaire : Version conserve une largeur fixe.
$resizeEditionColumns = {
if ($editionList.Columns.Count -ge 4) {
$availableWidth = $editionList.ClientSize.Width - $editionList.Columns[0].Width - $editionList.Columns[2].Width - $editionList.Columns[3].Width - 4
if ($editionList.Items.Count -gt 0 -and $editionList.Items.Count -gt [Math]::Floor($editionList.ClientSize.Height / 20)) {
$availableWidth -= [System.Windows.Forms.SystemInformation]::VerticalScrollBarWidth
}
$editionList.Columns[1].Width = [Math]::Max(220, $availableWidth)
}
}
$editionList.Add_SizeChanged($resizeEditionColumns)
$resultForm.Add_Shown($resizeEditionColumns)
# Dessin personnalise des en-tetes et cellules pour que ListView
# respecte le theme Light/Dark au lieu de garder un en-tete blanc.
$editionList.OwnerDraw = $true
$editionList.Add_DrawColumnHeader({
param($sender, $e)
$headerColor = if ($script:Theme.Contains('TableHeader')) { $script:Theme.TableHeader } else { $script:Theme.PanelAlt }
$headerBrush = New-Object System.Drawing.SolidBrush($headerColor)
$headerPen = New-Object System.Drawing.Pen($script:Theme.Border)
try {
$e.Graphics.FillRectangle($headerBrush, $e.Bounds)
$e.Graphics.DrawRectangle($headerPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
[System.Windows.Forms.TextRenderer]::DrawText(
$e.Graphics, $e.Header.Text, $sender.Font, $e.Bounds,
$script:Theme.Text,
([System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor
[System.Windows.Forms.TextFormatFlags]::Left -bor
[System.Windows.Forms.TextFormatFlags]::EndEllipsis)
)
} finally {
$headerBrush.Dispose()
$headerPen.Dispose()
}
})
$editionList.Add_DrawItem({ param($sender, $e) })
$editionList.Add_DrawSubItem({
param($sender, $e)
$cellBack = if (($e.Item.Index % 2) -eq 0) { $script:Theme.TableRowEven } else { $script:Theme.TableRowOdd }
$cellFore = $script:Theme.Text
if ($e.Item.Selected) {
$cellBack = $script:Theme.Accent
$cellFore = [System.Drawing.Color]::White
}
$cellBrush = New-Object System.Drawing.SolidBrush($cellBack)
$cellPen = New-Object System.Drawing.Pen($script:Theme.Border)
try {
$e.Graphics.FillRectangle($cellBrush, $e.Bounds)
if ($sender.GridLines) {
$e.Graphics.DrawRectangle($cellPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
}
[System.Windows.Forms.TextRenderer]::DrawText(
$e.Graphics, $e.SubItem.Text, $sender.Font, $e.Bounds,
$cellFore,
([System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor
[System.Windows.Forms.TextFormatFlags]::Left -bor
[System.Windows.Forms.TextFormatFlags]::EndEllipsis)
)
} finally {
$cellBrush.Dispose()
$cellPen.Dispose()
}
})
foreach ($edition in $editions) {
$index = if ($edition.ImageIndex) { [string]$edition.ImageIndex } elseif ($edition.Index) { [string]$edition.Index } else { '?' }
$name = if ($edition.ImageName) { [string]$edition.ImageName } elseif ($edition.Name) { [string]$edition.Name } elseif ($edition.ImageDescription) { [string]$edition.ImageDescription } else { 'Nom non disponible' }
$archValue = $null
foreach ($propertyName in @('Architecture', 'ImageArchitecture')) {
if ($edition.PSObject.Properties[$propertyName] -and $edition.$propertyName) {
$archValue = $edition.$propertyName
break
}
}
$arch = if ($null -ne $archValue) {
switch ([string]$archValue) {
'0' { 'x86' }
'5' { 'ARM' }
'9' { 'x64' }
'12' { 'ARM64' }
default { [string]$archValue }
}
} else { 'Non disponible' }
$version = if ($edition.Version) { [string]$edition.Version } else { 'Non disponible' }
$row = New-Object System.Windows.Forms.ListViewItem($index)
[void]$row.SubItems.Add($name)
[void]$row.SubItems.Add($arch)
[void]$row.SubItems.Add($version)
$row.Tag = $edition
[void]$editionList.Items.Add($row)
}
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
$buttonPanel.Height = 52
$buttonPanel.BackColor = $script:Theme.Background
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Text = 'Annuler'
$cancelButton.Size = New-Object System.Drawing.Size(100, 30)
$cancelButton.Anchor = 'Right,Top'
$cancelButton.Location = New-Object System.Drawing.Point(0, 10)
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$validateButton = New-Object System.Windows.Forms.Button
$validateButton.Text = 'Valider l''édition'
$validateButton.Size = New-Object System.Drawing.Size(140, 30)
$validateButton.Anchor = 'Right,Top'
$validateButton.Location = New-Object System.Drawing.Point(0, 10)
$validateButton.Enabled = $false
$editionList.Add_SelectedIndexChanged({
$validateButton.Enabled = ($editionList.SelectedItems.Count -gt 0 -and $editionList.SelectedItems[0].SubItems[0].Text -ne '?')
})
$validateButton.Add_Click({
if ($editionList.SelectedItems.Count -gt 0) {
$resultForm.Tag = [pscustomobject]@{
SourcePath = $selectedPath
ImagePath = $imagePath
MountedImage = $resolvedImage.MountedImage
Edition = $editionList.SelectedItems[0].Tag
Index = [int]$editionList.SelectedItems[0].SubItems[0].Text
Name = $editionList.SelectedItems[0].SubItems[1].Text
}
$resultForm.DialogResult = [System.Windows.Forms.DialogResult]::OK
$resultForm.Close()
}
})
$editionList.Add_DoubleClick({
if ($editionList.SelectedItems.Count -gt 0 -and $validateButton.Enabled) {
$validateButton.PerformClick()
}
})
$buttonPanel.Controls.Add($cancelButton)
$buttonPanel.Controls.Add($validateButton)
$buttonPanel.Add_Resize({
$validateButton.Location = New-Object System.Drawing.Point(($buttonPanel.ClientSize.Width - $validateButton.Width - 12), 10)
$cancelButton.Location = New-Object System.Drawing.Point(($validateButton.Left - $cancelButton.Width - 8), 10)
})
$resultForm.Controls.Add($editionList)
$resultForm.Controls.Add($buttonPanel)
$resultForm.Controls.Add($sourceLabel)
$resultForm.AcceptButton = $validateButton
$resultForm.CancelButton = $cancelButton
Add-SessionFooter -TargetForm $resultForm -Theme $script:Theme -Font $script:FontSmall `
-IPv4 $script:DeployIPv4 -VncPassword $script:VncPassword -ShowFooter $false
$dialogResult = $resultForm.ShowDialog($dlg)
$chosenEdition = $resultForm.Tag
$resultForm.Dispose()
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK -and $chosenEdition) {
if ($script:SelectedWindowsEdition -and
-not [string]::Equals([string]$script:SelectedWindowsEdition.SourcePath, [string]$chosenEdition.SourcePath, [StringComparison]::OrdinalIgnoreCase)) {
[void](Dismount-DeployCenterInstallImage -Image $script:SelectedWindowsEdition)
}
$script:SelectedWindowsEdition = $chosenEdition
$mountTransferred = $true
$lblStatus.Text = "Édition sélectionnée : $($chosenEdition.Name) (index $($chosenEdition.Index))."
& $script:SourceLogAction "Edition selectionnee : $($chosenEdition.Name), index $($chosenEdition.Index), image $imagePath" 'SUCCESS'
$dlg.DialogResult = [System.Windows.Forms.DialogResult]::OK
$dlg.Close()
} else {
$lblStatus.Text = "$($editions.Count) édition(s) trouvée(s) dans $selectedName. Aucune nouvelle édition sélectionnée."
}
& $script:SourceLogAction "Analyse des editions terminee : $imagePath ($($editions.Count) edition(s))." 'SUCCESS'
} catch {
$lblStatus.Text = "Echec de l'analyse des editions de $selectedName."
& $script:SourceLogAction "Erreur analyse editions $selectedPath : $($_.Exception.Message)" 'ERROR'
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'Analyse des éditions' -Icon Warning -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
} finally {
if ($resolvedImage -and $resolvedImage.MountedImage -and -not $mountTransferred -and
(-not $script:SelectedWindowsEdition -or $resolvedImage.MountedImage -ne $script:SelectedWindowsEdition.MountedImage)) {
try { [void](Dismount-DeployCenterInstallImage -Image $resolvedImage) } catch {
& $script:SourceLogAction "Impossible de démonter l'ISO après l'analyse : $($_.Exception.Message)" 'WARNING'
}
}
$dlg.Cursor = [System.Windows.Forms.Cursors]::Default
$progressAnalysis.Visible = $false
$btnEditions.Enabled = $true
$btnScan.Enabled = $true
}
})
$list.Add_DoubleClick({
if ($list.SelectedItems.Count -gt 0 -and $btnEditions.Enabled) {
$btnEditions.PerformClick()
}
})
$btnScan.Add_Click({
$btnScan.Enabled = $false
$dlg.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
$lblStatus.Text = 'Recherche en cours sur les chemins indiques...'
$sourceState.AllFound = @()
$list.Items.Clear()
try {
# La recherche ne modifie jamais DeployCenter.ini.
$sourceState.AllFound = @(Find-WindowsSources -RootPath $txtPath.Text -LogAction $script:SourceLogAction)
& $renderSourceList $txtFilter.Text
if (@($sourceState.AllFound).Count -eq 0) {
$lblStatus.Text = 'Aucune source ISO/WIM/ESD ou installation extraite detectee dans les chemins accessibles.'
& $script:SourceLogAction 'Aucune source Windows detectee dans les chemins accessibles.' 'WARNING'
}
} catch {
$lblStatus.Text = 'Echec de la recherche. Consulte le message.'
& $script:SourceLogAction "Erreur detection sources : $($_.Exception.Message)" 'ERROR'
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'Détection des sources' -Icon Warning -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
} finally {
$dlg.Cursor = [System.Windows.Forms.Cursors]::Default
$btnScan.Enabled = $true
}
})
Add-SessionFooter -TargetForm $dlg -Theme $script:Theme -Font $script:FontSmall `
-IPv4 $script:DeployIPv4 -VncPassword $script:VncPassword
[void]$dlg.ShowDialog($script:form)
$dlg.Dispose()
}
function Update-HardwareView {
$script:btnRefresh.Enabled = $false
$script:form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
try {
if (-not (Get-Command Get-HardwareInfo -ErrorAction SilentlyContinue)) {
throw 'Le module HardwareInventory ne fournit pas Get-HardwareInfo.'
}
$info = Get-HardwareInfo
$script:lblManufacturer.Text = "Fabricant : $($info.Manufacturer)"
$script:lblModel.Text = "Modèle : $($info.Model)"
$script:lblSerial.Text = "Numéro de série : $($info.SerialNumber)"
$script:lblFirmware.Text = "Firmware : $($info.Firmware)"
$script:lblArchitecture.Text = "Architecture : $($info.Architecture)"
$script:HardwareInventory = $info
$previousDiskIndex = $null
if ($null -ne $script:cmbDisk.SelectedItem -and $null -ne $script:cmbDisk.SelectedItem.Tag) {
$previousDiskIndex = [string]$script:cmbDisk.SelectedItem.Tag
}
$script:RefreshingDiskList = $true
$script:cmbDisk.BeginUpdate()
try {
$script:cmbDisk.Items.Clear()
foreach ($disk in $info.Disks) {
$diskLabel = "Disque {0} - {1} - {2:N1} Go - {3} - {4}" -f $disk.Index, $disk.Model, $disk.SizeGB, $disk.BusType, $disk.Status
$diskChoice = [pscustomobject]@{ Text = $diskLabel; Tag = [string]$disk.Index }
[void]$script:cmbDisk.Items.Add($diskChoice)
}
$script:cmbDisk.DisplayMember = 'Text'
if ($script:cmbDisk.Items.Count -gt 0) {
$selectedIndex = -1
if ($null -ne $previousDiskIndex) {
for ($i = 0; $i -lt $script:cmbDisk.Items.Count; $i++) {
if ([string]$script:cmbDisk.Items[$i].Tag -eq $previousDiskIndex) { $selectedIndex = $i; break }
}
}
if ($selectedIndex -lt 0 -and $script:cmbDisk.Items.Count -eq 1) { $selectedIndex = 0 }
$script:cmbDisk.SelectedIndex = $selectedIndex
}
}
finally {
$script:cmbDisk.EndUpdate()
$script:RefreshingDiskList = $false
}
$script:SelectedDiskIndex = if ($script:cmbDisk.SelectedIndex -ge 0) { [string]$script:cmbDisk.SelectedItem.Tag } else { $null }
Write-DeployInventory -InventoryDir $script:InventoryDir -InventoryFile $script:InventoryFile -HardwareInfo $info -DeploymentIPv4 $script:DeployIPv4 -VncPassword $script:VncPassword -LogAction $script:SourceLogAction
& $script:SourceLogAction "Inventaire materiel actualise." "SUCCESS"
foreach ($errorMessage in $info.Errors) {
& $script:SourceLogAction $errorMessage "WARNING"
}
if ($info.Disks.Count -eq 0) {
& $script:SourceLogAction "Aucun disque physique detecte." "WARNING"
}
}
catch {
& $script:SourceLogAction "Erreur inventaire : $($_.Exception.Message)" "ERROR"
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'Erreur DeployCenter' -Icon Error -Owner $script:form -Theme $script:Theme -Font $script:FontRegular
}
finally {
$script:btnRefresh.Enabled = $true
$script:form.Cursor = [System.Windows.Forms.Cursors]::Default
}
}
function Get-PartitionProfileSizingSelection {
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
[Parameter(Mandatory=$true)]$DiskInfo,
[System.Collections.IDictionary]$InitialValues = @{}
)
$capacity = Get-DeployCenterPartitionSizingCapacity -Profile $Profile -DiskSizeBytes ([long]$DiskInfo.SizeBytes)
return Show-DeployCenterPartitionSizingDialog `
-Profile $Profile `
-AvailableGB $capacity.UsableGB `
-Theme $script:Theme `
-Font $script:FontRegular `
-Owner $script:form `
-InitialValues $InitialValues
}
function Set-PartitionProfileSizingSelection {
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
[Parameter(Mandatory=$true)]$DiskInfo,
[System.Collections.IDictionary]$InitialValues = @{}
)
$selection = Get-PartitionProfileSizingSelection -Profile $Profile -DiskInfo $DiskInfo -InitialValues $InitialValues
if ($null -eq $selection) { return $false }
$script:SelectedPartitionSizing = New-DeployCenterPartitionSizingState -Profile $Profile -DiskInfo $DiskInfo -Selection $selection
return $true
}
function Update-DeployCenterLayout {
if ($null -eq $script:form -or $script:form.IsDisposed) { return }
$w = $script:form.ClientSize.Width
$h = $script:form.ClientSize.Height
$m = 24
$contentWidth = [Math]::Max(300, $w - (2 * $m))
# En-tete : le selecteur de theme reste accroche au bord droit.
if ($null -ne $script:cmbTheme) {
$script:cmbTheme.Location = New-Object System.Drawing.Point([Math]::Max($m, $w - $m - $script:cmbTheme.Width), 25)
}
# Les deux panneaux superieurs conservent leur hauteur, mais s'etirent en largeur.
$script:groupPC.Location = New-Object System.Drawing.Point($m, 100)
$script:groupPC.Size = New-Object System.Drawing.Size($contentWidth, 90)
$script:groupDisks.Location = New-Object System.Drawing.Point($m, 205)
$script:groupDisks.Size = New-Object System.Drawing.Size($contentWidth, 270)
Set-DiskSelectorWidth
$footerHeight = 26
$buttonHeightLocal = 38
$buttonYLocal = $h - $footerHeight - 14 - $buttonHeightLocal
$buttonWidthLocal = 140
$buttonGapLocal = 12
$script:btnClose.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
$script:btnRefresh.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
$script:btnDeploy.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
$script:btnLog.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
$script:btnClose.Location = New-Object System.Drawing.Point([Math]::Max($m, $w - $m - $buttonWidthLocal), $buttonYLocal)
$script:btnDeploy.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnClose.Left - $buttonGapLocal - $buttonWidthLocal), $buttonYLocal)
$script:btnRefresh.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnDeploy.Left - $buttonGapLocal - $buttonWidthLocal), $buttonYLocal)
$script:btnLog.Location = New-Object System.Drawing.Point($m, $buttonYLocal)
Set-DiskSelectorWidth
}