von Holgi » 24.09.2026, 22:32
stimmt! da war ich auf dem Holzweg.
Wenn du eine Lösung hast, wäre es toll. Du könntest ja einen neuen Thread im Windows 11 Forum aufmachen.
das wäre mein Vorschlag:
Set_User_Account_Picture.ps1
Code: Alles auswählen
param(
[string]$ImagePath = (Join-Path $PSScriptRoot 'User.jpg')
)
$ErrorActionPreference = 'Stop'
$LogFile = Join-Path $env:ProgramData 'Set_User_Account_Picture.log'
function Write-Log {
param([string]$Text)
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $Text"
Add-Content -Path $LogFile -Value $line -Encoding UTF8
Write-Host $line
}
try {
Write-Log '--- Start V3 ---'
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
$isAdmin = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
throw 'Das Script muss als Administrator ausgefuehrt werden.'
}
if (-not (Test-Path -LiteralPath $ImagePath -PathType Leaf)) {
throw "Bild nicht gefunden: $ImagePath"
}
$userName = $identity.Name
$sid = $identity.User.Value
Write-Log "Benutzer: $userName"
Write-Log "SID: $sid"
Write-Log "Bild: $ImagePath"
# Eine aktivierte Richtlinie 'Apply the default account picture to all users'
# verhindert benutzerspezifische Kontobilder.
$policyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'
New-Item -Path $policyPath -Force | Out-Null
$useDefaultTile = (Get-ItemProperty -Path $policyPath -Name UseDefaultTile -ErrorAction SilentlyContinue).UseDefaultTile
if ($useDefaultTile -eq 1) {
Write-Log 'UseDefaultTile war 1 und wird auf 0 gesetzt.'
New-ItemProperty -Path $policyPath -Name UseDefaultTile -Value 0 -PropertyType DWord -Force | Out-Null
}
$publicFolder = Join-Path 'C:\Users\Public\AccountPictures' $sid
$roamingFolder = Join-Path $env:APPDATA 'Microsoft\Windows\AccountPictures'
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\$sid"
New-Item -Path $publicFolder -ItemType Directory -Force | Out-Null
# Windows merkt sich hier die zuletzt verwendeten Benutzerbilder.
# Vorhandene Bilder werden gesichert, damit ein alter Cache nach dem Neustart
# nicht wieder das bisherige Bild liefert.
if (Test-Path -LiteralPath $roamingFolder) {
$oldFiles = Get-ChildItem -LiteralPath $roamingFolder -File -ErrorAction SilentlyContinue
if ($oldFiles) {
$backupFolder = Join-Path (Split-Path $roamingFolder -Parent) ("AccountPictures_Backup_{0}" -f (Get-Date -Format '*****_HHmmss'))
New-Item -Path $backupFolder -ItemType Directory -Force | Out-Null
foreach ($file in $oldFiles) {
Copy-Item -LiteralPath $file.FullName -Destination $backupFolder -Force
Remove-Item -LiteralPath $file.FullName -Force
}
Write-Log "Alte AccountPictures gesichert nach: $backupFolder"
}
}
New-Item -Path $roamingFolder -ItemType Directory -Force | Out-Null
New-Item -Path $regPath -Force | Out-Null
Add-Type -AssemblyName System.Drawing
$src = [System.Drawing.Image]::FromFile($ImagePath)
# Unter Windows 11 anzutreffende Kontobildgroessen.
$sizes = @(32,40,48,64,96,192,200,208,240,424,448,1080)
foreach ($size in $sizes) {
$fileName = "Image$size.jpg"
$publicDest = Join-Path $publicFolder $fileName
$roamDest = Join-Path $roamingFolder $fileName
$bmp = New-Object System.Drawing.Bitmap($size, $size)
$g = [System.Drawing.Graphics]::FromImage($bmp)
try {
$g.Clear([System.Drawing.Color]::Black)
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
$g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
# Mittiger quadratischer Zuschnitt ohne Verzerrung.
$srcW = [double]$src.Width
$srcH = [double]$src.Height
$crop = [Math]::Min($srcW, $srcH)
$srcX = ($srcW - $crop) / 2
$srcY = ($srcH - $crop) / 2
$srcRect = New-Object System.Drawing.RectangleF([single]$srcX, [single]$srcY, [single]$crop, [single]$crop)
$dstRect = New-Object System.Drawing.RectangleF(0, 0, $size, $size)
$g.DrawImage($src, $dstRect, $srcRect, [System.Drawing.GraphicsUnit]::Pixel)
$bmp.Save($publicDest, [System.Drawing.Imaging.ImageFormat]::Jpeg)
}
finally {
$g.Dispose()
$bmp.Dispose()
}
Copy-Item -LiteralPath $publicDest -Destination $roamDest -Force
New-ItemProperty -Path $regPath -Name "Image$size" -Value $publicDest -PropertyType String -Force | Out-Null
Write-Log "Image$size -> $publicDest (+ Roaming-Kopie)"
}
$src.Dispose()
Write-Log 'FERTIG. Das Profilbild wurde gesetzt.'
Write-Log 'Ein Neustart wird empfohlen, aber NICHT automatisch ausgefuehrt.'
function Show-LogoutCountdown {
param([int]$Seconds = 10)
try {
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Profilbild geaendert'
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.ControlBox = $false
$form.TopMost = $true
$form.ClientSize = New-Object System.Drawing.Size(520, 230)
$title = New-Object System.Windows.Forms.Label
$title.AutoSize = $false
$title.TextAlign = 'MiddleCenter'
$title.Font = New-Object System.Drawing.Font('Segoe UI', 15, [System.Drawing.FontStyle]::Bold)
$title.Location = New-Object System.Drawing.Point(20, 18)
$title.Size = New-Object System.Drawing.Size(480, 38)
$title.Text = 'Profilbild wurde erfolgreich geaendert.'
$form.Controls.Add($title)
$restart = New-Object System.Windows.Forms.Label
$restart.AutoSize = $false
$restart.TextAlign = 'MiddleCenter'
$restart.Font = New-Object System.Drawing.Font('Segoe UI', 10)
$restart.Location = New-Object System.Drawing.Point(20, 65)
$restart.Size = New-Object System.Drawing.Size(480, 48)
$restart.Text = "Ein Neustart wird empfohlen.`r`nEr wird NICHT automatisch ausgefuehrt."
$form.Controls.Add($restart)
$countdown = New-Object System.Windows.Forms.Label
$countdown.AutoSize = $false
$countdown.TextAlign = 'MiddleCenter'
$countdown.Font = New-Object System.Drawing.Font('Segoe UI', 13, [System.Drawing.FontStyle]::Bold)
$countdown.Location = New-Object System.Drawing.Point(20, 130)
$countdown.Size = New-Object System.Drawing.Size(480, 50)
$countdown.Text = "Automatische Abmeldung in $Seconds Sekunden ..."
$form.Controls.Add($countdown)
$state = [PSCustomObject]@{ Remaining = $Seconds }
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
$state.Remaining--
if ($state.Remaining -le 0) {
$timer.Stop()
$countdown.Text = 'Abmeldung ...'
$form.Refresh()
Start-Sleep -Milliseconds 250
$form.Close()
}
else {
$countdown.Text = "Automatische Abmeldung in $($state.Remaining) Sekunden ..."
}
})
$form.Add_Shown({
$form.Activate()
$timer.Start()
})
[void]$form.ShowDialog()
$timer.Dispose()
$form.Dispose()
}
catch {
Write-Log ("Countdown-Fenster nicht verfuegbar; Konsolen-Countdown wird verwendet: " + $_.Exception.Message)
for ($i = $Seconds; $i -ge 1; $i--) {
Write-Host "Automatische Abmeldung in $i Sekunden ..." -ForegroundColor Yellow
Start-Sleep -Seconds 1
}
}
}
Show-LogoutCountdown -Seconds 10
Write-Log 'Automatische Abmeldung wird jetzt ausgefuehrt.'
Write-Log 'Hinweis: Ein Neustart wird weiterhin nur empfohlen und NICHT automatisch gestartet.'
Write-Log '--- Ende V3 ---'
# /l meldet den aktuellen Benutzer ab. Einen eingebauten Timeout gibt es
# fuer /l nicht, deshalb wurde der 10-Sekunden-Countdown oben selbst erzeugt.
Start-Process -FilePath "$env:SystemRoot\System32\shutdown.exe" -ArgumentList '/l'
}
catch {
Write-Log ("FEHLER: " + $_.Exception.Message)
Write-Log '--- Abbruch V3 ---'
throw
}
das User Picture (Bild) als .jpg Datei mit dem Namen User.jpg (wichtig!) im gleichen Ordner wie das Script ablegen und dann das Script starten. Am Ende wird man automatisch abgemeldet, um die Einstellungen zu übernehmen.
Ich starte das Script mit einem .bat Script (ebenfalls zusammen mit den beiden anderen Dateien in einem Ordner).
Set_User_Account_Picture.bat
Code: Alles auswählen
@ECHO OFF
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
stimmt! da war ich auf dem Holzweg.
Wenn du eine Lösung hast, wäre es toll. Du könntest ja einen neuen Thread im Windows 11 Forum aufmachen.
das wäre mein Vorschlag:
Set_User_Account_Picture.ps1
[code]param(
[string]$ImagePath = (Join-Path $PSScriptRoot 'User.jpg')
)
$ErrorActionPreference = 'Stop'
$LogFile = Join-Path $env:ProgramData 'Set_User_Account_Picture.log'
function Write-Log {
param([string]$Text)
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $Text"
Add-Content -Path $LogFile -Value $line -Encoding UTF8
Write-Host $line
}
try {
Write-Log '--- Start V3 ---'
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
$isAdmin = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
throw 'Das Script muss als Administrator ausgefuehrt werden.'
}
if (-not (Test-Path -LiteralPath $ImagePath -PathType Leaf)) {
throw "Bild nicht gefunden: $ImagePath"
}
$userName = $identity.Name
$sid = $identity.User.Value
Write-Log "Benutzer: $userName"
Write-Log "SID: $sid"
Write-Log "Bild: $ImagePath"
# Eine aktivierte Richtlinie 'Apply the default account picture to all users'
# verhindert benutzerspezifische Kontobilder.
$policyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer'
New-Item -Path $policyPath -Force | Out-Null
$useDefaultTile = (Get-ItemProperty -Path $policyPath -Name UseDefaultTile -ErrorAction SilentlyContinue).UseDefaultTile
if ($useDefaultTile -eq 1) {
Write-Log 'UseDefaultTile war 1 und wird auf 0 gesetzt.'
New-ItemProperty -Path $policyPath -Name UseDefaultTile -Value 0 -PropertyType DWord -Force | Out-Null
}
$publicFolder = Join-Path 'C:\Users\Public\AccountPictures' $sid
$roamingFolder = Join-Path $env:APPDATA 'Microsoft\Windows\AccountPictures'
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\$sid"
New-Item -Path $publicFolder -ItemType Directory -Force | Out-Null
# Windows merkt sich hier die zuletzt verwendeten Benutzerbilder.
# Vorhandene Bilder werden gesichert, damit ein alter Cache nach dem Neustart
# nicht wieder das bisherige Bild liefert.
if (Test-Path -LiteralPath $roamingFolder) {
$oldFiles = Get-ChildItem -LiteralPath $roamingFolder -File -ErrorAction SilentlyContinue
if ($oldFiles) {
$backupFolder = Join-Path (Split-Path $roamingFolder -Parent) ("AccountPictures_Backup_{0}" -f (Get-Date -Format '*****'))
New-Item -Path $backupFolder -ItemType Directory -Force | Out-Null
foreach ($file in $oldFiles) {
Copy-Item -LiteralPath $file.FullName -Destination $backupFolder -Force
Remove-Item -LiteralPath $file.FullName -Force
}
Write-Log "Alte AccountPictures gesichert nach: $backupFolder"
}
}
New-Item -Path $roamingFolder -ItemType Directory -Force | Out-Null
New-Item -Path $regPath -Force | Out-Null
Add-Type -AssemblyName System.Drawing
$src = [System.Drawing.Image]::FromFile($ImagePath)
# Unter Windows 11 anzutreffende Kontobildgroessen.
$sizes = @(32,40,48,64,96,192,200,208,240,424,448,1080)
foreach ($size in $sizes) {
$fileName = "Image$size.jpg"
$publicDest = Join-Path $publicFolder $fileName
$roamDest = Join-Path $roamingFolder $fileName
$bmp = New-Object System.Drawing.Bitmap($size, $size)
$g = [System.Drawing.Graphics]::FromImage($bmp)
try {
$g.Clear([System.Drawing.Color]::Black)
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
$g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
# Mittiger quadratischer Zuschnitt ohne Verzerrung.
$srcW = [double]$src.Width
$srcH = [double]$src.Height
$crop = [Math]::Min($srcW, $srcH)
$srcX = ($srcW - $crop) / 2
$srcY = ($srcH - $crop) / 2
$srcRect = New-Object System.Drawing.RectangleF([single]$srcX, [single]$srcY, [single]$crop, [single]$crop)
$dstRect = New-Object System.Drawing.RectangleF(0, 0, $size, $size)
$g.DrawImage($src, $dstRect, $srcRect, [System.Drawing.GraphicsUnit]::Pixel)
$bmp.Save($publicDest, [System.Drawing.Imaging.ImageFormat]::Jpeg)
}
finally {
$g.Dispose()
$bmp.Dispose()
}
Copy-Item -LiteralPath $publicDest -Destination $roamDest -Force
New-ItemProperty -Path $regPath -Name "Image$size" -Value $publicDest -PropertyType String -Force | Out-Null
Write-Log "Image$size -> $publicDest (+ Roaming-Kopie)"
}
$src.Dispose()
Write-Log 'FERTIG. Das Profilbild wurde gesetzt.'
Write-Log 'Ein Neustart wird empfohlen, aber NICHT automatisch ausgefuehrt.'
function Show-LogoutCountdown {
param([int]$Seconds = 10)
try {
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Profilbild geaendert'
$form.StartPosition = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.ControlBox = $false
$form.TopMost = $true
$form.ClientSize = New-Object System.Drawing.Size(520, 230)
$title = New-Object System.Windows.Forms.Label
$title.AutoSize = $false
$title.TextAlign = 'MiddleCenter'
$title.Font = New-Object System.Drawing.Font('Segoe UI', 15, [System.Drawing.FontStyle]::Bold)
$title.Location = New-Object System.Drawing.Point(20, 18)
$title.Size = New-Object System.Drawing.Size(480, 38)
$title.Text = 'Profilbild wurde erfolgreich geaendert.'
$form.Controls.Add($title)
$restart = New-Object System.Windows.Forms.Label
$restart.AutoSize = $false
$restart.TextAlign = 'MiddleCenter'
$restart.Font = New-Object System.Drawing.Font('Segoe UI', 10)
$restart.Location = New-Object System.Drawing.Point(20, 65)
$restart.Size = New-Object System.Drawing.Size(480, 48)
$restart.Text = "Ein Neustart wird empfohlen.`r`nEr wird NICHT automatisch ausgefuehrt."
$form.Controls.Add($restart)
$countdown = New-Object System.Windows.Forms.Label
$countdown.AutoSize = $false
$countdown.TextAlign = 'MiddleCenter'
$countdown.Font = New-Object System.Drawing.Font('Segoe UI', 13, [System.Drawing.FontStyle]::Bold)
$countdown.Location = New-Object System.Drawing.Point(20, 130)
$countdown.Size = New-Object System.Drawing.Size(480, 50)
$countdown.Text = "Automatische Abmeldung in $Seconds Sekunden ..."
$form.Controls.Add($countdown)
$state = [PSCustomObject]@{ Remaining = $Seconds }
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000
$timer.Add_Tick({
$state.Remaining--
if ($state.Remaining -le 0) {
$timer.Stop()
$countdown.Text = 'Abmeldung ...'
$form.Refresh()
Start-Sleep -Milliseconds 250
$form.Close()
}
else {
$countdown.Text = "Automatische Abmeldung in $($state.Remaining) Sekunden ..."
}
})
$form.Add_Shown({
$form.Activate()
$timer.Start()
})
[void]$form.ShowDialog()
$timer.Dispose()
$form.Dispose()
}
catch {
Write-Log ("Countdown-Fenster nicht verfuegbar; Konsolen-Countdown wird verwendet: " + $_.Exception.Message)
for ($i = $Seconds; $i -ge 1; $i--) {
Write-Host "Automatische Abmeldung in $i Sekunden ..." -ForegroundColor Yellow
Start-Sleep -Seconds 1
}
}
}
Show-LogoutCountdown -Seconds 10
Write-Log 'Automatische Abmeldung wird jetzt ausgefuehrt.'
Write-Log 'Hinweis: Ein Neustart wird weiterhin nur empfohlen und NICHT automatisch gestartet.'
Write-Log '--- Ende V3 ---'
# /l meldet den aktuellen Benutzer ab. Einen eingebauten Timeout gibt es
# fuer /l nicht, deshalb wurde der 10-Sekunden-Countdown oben selbst erzeugt.
Start-Process -FilePath "$env:SystemRoot\System32\shutdown.exe" -ArgumentList '/l'
}
catch {
Write-Log ("FEHLER: " + $_.Exception.Message)
Write-Log '--- Abbruch V3 ---'
throw
}
[/code]
das User Picture (Bild) als .jpg Datei mit dem Namen User.jpg (wichtig!) im gleichen Ordner wie das Script ablegen und dann das Script starten. Am Ende wird man automatisch abgemeldet, um die Einstellungen zu übernehmen.
Ich starte das Script mit einem .bat Script (ebenfalls zusammen mit den beiden anderen Dateien in einem Ordner).
Set_User_Account_Picture.bat
[code]@ECHO OFF
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
[/code]