commit daaab5acab52d153c0661a4fb3f66932550c4180 Author: Stefan Hacker Date: Wed Feb 11 20:21:53 2026 +0100 first commit diff --git a/PrinterMigrator.bat b/PrinterMigrator.bat new file mode 100644 index 0000000..8bf89bb --- /dev/null +++ b/PrinterMigrator.bat @@ -0,0 +1,4 @@ +@echo off +:: Drucker-Migrationstool Starter +:: Startet das PowerShell-Skript mit den richtigen Parametern +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0PrinterMigrator.ps1" diff --git a/PrinterMigrator.ps1 b/PrinterMigrator.ps1 new file mode 100644 index 0000000..aa19f9e --- /dev/null +++ b/PrinterMigrator.ps1 @@ -0,0 +1,1272 @@ +<# +.SYNOPSIS + Drucker-Migrationstool - Drucker mit Treibern und Anschluessen sichern und wiederherstellen +.DESCRIPTION + Exportiert und importiert Windows-Drucker inklusive TCP/IP-Anschluesse und Treiber. + Loest das Problem, dass die Windows-Druckerverwaltung Anschluesse nicht korrekt migriert. + Alles wird in eine einzige .pmb-Datei gepackt (ZIP-Format). +.NOTES + Muss als Administrator ausgefuehrt werden. + Kompatibel mit Windows 10/11 (PowerShell 5.1+) +#> + +param( + [switch]$NoElevate +) + +# ============================================================ +# Auto-Elevation - startet sich selbst als Admin neu +# ============================================================ +if (-not $NoElevate) { + $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( + [Security.Principal.WindowsIdentity]::GetCurrent() + ) + if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + $scriptPath = if ($PSCommandPath) { $PSCommandPath } else { $MyInvocation.MyCommand.Path } + if ($scriptPath) { + Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`"" -Verb RunAs + } else { + Write-Host "Bitte starten Sie das Skript als Administrator." -ForegroundColor Red + } + exit + } +} + +# ============================================================ +# Voraussetzungen pruefen +# ============================================================ +if (-not (Get-Module -ListAvailable -Name PrintManagement -ErrorAction SilentlyContinue)) { + Add-Type -AssemblyName System.Windows.Forms + [System.Windows.Forms.MessageBox]::Show( + "Das PrintManagement-Modul ist nicht verfuegbar.`nDieses Tool benoetigt Windows 10/11 Pro oder Enterprise.", + "Fehler", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error + ) | Out-Null + exit 1 +} + +# ============================================================ +# Assemblies laden +# ============================================================ +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing +[System.Windows.Forms.Application]::EnableVisualStyles() + +# ============================================================ +# Globale Variablen +# ============================================================ +$script:LogBox = $null +$script:ProgressBar = $null +$script:StatusLabel = $null + +# ============================================================ +# Hilfsfunktionen +# ============================================================ +function Write-Log { + param( + [string]$Message, + [string]$Level = "INFO" + ) + $timestamp = Get-Date -Format "HH:mm:ss" + $logMessage = "[$timestamp] $Message" + if ($script:LogBox) { + $script:LogBox.AppendText("$logMessage`r`n") + $script:LogBox.SelectionStart = $script:LogBox.TextLength + $script:LogBox.ScrollToCaret() + [System.Windows.Forms.Application]::DoEvents() + } +} + +function Update-Progress { + param( + [int]$Current, + [int]$Total, + [string]$Status + ) + if ($script:ProgressBar -and $Total -gt 0) { + $percent = [math]::Min(100, [math]::Round(($Current / $Total) * 100)) + $script:ProgressBar.Value = $percent + } + if ($script:StatusLabel) { + $script:StatusLabel.Text = $Status + } + [System.Windows.Forms.Application]::DoEvents() +} + +# ============================================================ +# Datensammlung - Anschlussdetails ermitteln +# ============================================================ +function Get-PrinterPortDetails { + param([string]$PortName) + + $portInfo = @{ + Name = $PortName + Type = "Unknown" + } + + # TCP/IP-Anschluss per WMI abfragen (liefert die meisten Details) + try { + $tcpPort = Get-CimInstance -ClassName Win32_TCPIPPrinterPort ` + -Filter "Name='$($PortName -replace "'","''")'" -ErrorAction SilentlyContinue + if ($tcpPort) { + $portInfo.Type = "TCPIP" + $portInfo.HostAddress = $tcpPort.HostAddress + $portInfo.PortNumber = $tcpPort.PortNumber + $portInfo.Protocol = $tcpPort.Protocol # 1=RAW, 2=LPR + $portInfo.SNMPEnabled = $tcpPort.SNMPEnabled + $portInfo.SNMPCommunity = $tcpPort.SNMPCommunity + $portInfo.SNMPDevIndex = $tcpPort.SNMPDevIndex + $portInfo.Queue = $tcpPort.Queue + return $portInfo + } + } catch {} + + # Fallback: Anschluss-Typ anhand des Namens erkennen + try { + $port = Get-PrinterPort -Name $PortName -ErrorAction SilentlyContinue + if ($port) { + $portInfo.Description = $port.Description + + if ($PortName -match '^(LPT|COM)\d+:?$') { + $portInfo.Type = "Local" + } + elseif ($PortName -eq 'FILE:' -or $PortName -eq 'PORTPROMPT:') { + $portInfo.Type = "File" + } + elseif ($PortName -match '^USB') { + $portInfo.Type = "USB" + } + elseif ($PortName -match '^WSD') { + $portInfo.Type = "WSD" + } + elseif ($PortName -match '^TS\d') { + $portInfo.Type = "TerminalService" + } + elseif ($port.Description -match 'TCP' -or $PortName -match 'IP_') { + $portInfo.Type = "TCPIP" + if ($port.PrinterHostAddress) { + $portInfo.HostAddress = $port.PrinterHostAddress + $portInfo.PortNumber = if ($port.PortNumber) { $port.PortNumber } else { 9100 } + } + } + else { + $portInfo.Type = "Other" + } + } + } catch {} + + return $portInfo +} + +# ============================================================ +# Datensammlung - Treiberdetails ermitteln +# ============================================================ +function Get-PrinterDriverDetails { + param([string]$DriverName) + + $driverInfo = @{ + Name = $DriverName + InfPath = "" + InfFileName = "" + PackageDirectory = "" + PrinterEnvironment = "" + MajorVersion = 0 + Manufacturer = "" + PrintProcessor = "" + Files = @() + } + + try { + $driver = Get-PrinterDriver -Name $DriverName -ErrorAction SilentlyContinue + if ($driver) { + $driverInfo.InfPath = $driver.InfPath + $driverInfo.PrinterEnvironment = $driver.PrinterEnvironment + $driverInfo.MajorVersion = $driver.MajorVersion + $driverInfo.Manufacturer = $driver.Manufacturer + $driverInfo.PrintProcessor = $driver.PrintProcessor + + # Paketverzeichnis aus INF-Pfad ableiten + if ($driver.InfPath -and (Test-Path $driver.InfPath)) { + $driverInfo.PackageDirectory = Split-Path -Parent $driver.InfPath + $driverInfo.InfFileName = Split-Path -Leaf $driver.InfPath + } + + # Einzelne Treiberdateien sammeln + $filePaths = @() + @('DriverPath', 'ConfigFile', 'DataFile', 'HelpFile') | ForEach-Object { + if ($driver.$_) { $filePaths += $driver.$_ } + } + if ($driver.DependentFiles) { + $filePaths += $driver.DependentFiles + } + $driverInfo.Files = @($filePaths | Select-Object -Unique | Where-Object { $_ }) + } + } catch { + Write-Log " Warnung: Treiberdetails fuer '$DriverName' konnten nicht gelesen werden: $_" "WARN" + } + + return $driverInfo +} + +# ============================================================ +# Datensammlung - Alle Drucker erfassen +# ============================================================ +function Get-AllPrinterData { + $printers = @() + + try { + $installedPrinters = Get-Printer -ErrorAction Stop + $defaultPrinterName = "" + try { + $defaultObj = Get-CimInstance -ClassName Win32_Printer -Filter "Default=True" -ErrorAction SilentlyContinue + if ($defaultObj) { $defaultPrinterName = $defaultObj.Name } + } catch {} + + foreach ($printer in $installedPrinters) { + $portDetails = Get-PrinterPortDetails -PortName $printer.PortName + $driverDetails = Get-PrinterDriverDetails -DriverName $printer.DriverName + + # Druckkonfiguration abfragen + $duplexMode = "" + $color = $null + try { + $cfg = Get-PrintConfiguration -PrinterName $printer.Name -ErrorAction SilentlyContinue + if ($cfg) { + $duplexMode = [string]$cfg.DuplexingMode + $color = $cfg.Color + } + } catch {} + + $printerData = @{ + Name = $printer.Name + DriverName = $printer.DriverName + PortName = $printer.PortName + Shared = $printer.Shared + ShareName = $printer.ShareName + Comment = $printer.Comment + Location = $printer.Location + PrintProcessor = $printer.PrintProcessor + DataType = $printer.Datatype + IsDefault = ($printer.Name -eq $defaultPrinterName) + DuplexingMode = $duplexMode + Color = $color + Port = $portDetails + Driver = $driverDetails + } + + $printers += $printerData + } + } catch { + Write-Log "Fehler beim Lesen der Drucker: $_" "ERROR" + } + + return $printers +} + +# ============================================================ +# EXPORT - Sicherung erstellen +# ============================================================ +function Export-PrinterBackup { + param( + [array]$SelectedPrinters, + [string]$OutputPath + ) + + $tempDir = Join-Path $env:TEMP "PrinterMigrator_Export_$(Get-Date -Format 'yyyyMMdd_HHmmss')" + $driversDir = Join-Path $tempDir "drivers" + + try { + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + New-Item -ItemType Directory -Path $driversDir -Force | Out-Null + + $totalSteps = $SelectedPrinters.Count + 1 + $currentStep = 0 + $backedUpDrivers = @{} + + # Exportdaten vorbereiten (tiefe Kopie pro Drucker) + $exportPrinters = @() + + foreach ($printer in $SelectedPrinters) { + $currentStep++ + Update-Progress -Current $currentStep -Total $totalSteps ` + -Status "Verarbeite: $($printer.Name)" + + # Exportstruktur fuer diesen Drucker erstellen + $exportPrinter = @{ + Name = $printer.Name + DriverName = $printer.DriverName + PortName = $printer.PortName + Shared = $printer.Shared + ShareName = $printer.ShareName + Comment = $printer.Comment + Location = $printer.Location + PrintProcessor = $printer.PrintProcessor + DataType = $printer.DataType + IsDefault = $printer.IsDefault + DuplexingMode = $printer.DuplexingMode + Color = $printer.Color + Port = $printer.Port + Driver = @{ + Name = $printer.Driver.Name + InfFileName = $printer.Driver.InfFileName + PrinterEnvironment = $printer.Driver.PrinterEnvironment + MajorVersion = $printer.Driver.MajorVersion + Manufacturer = $printer.Driver.Manufacturer + PrintProcessor = $printer.Driver.PrintProcessor + BackupPackageDir = "" + BackupInfPath = "" + } + } + + $driverName = $printer.DriverName + + # Treiber nur einmal sichern (auch wenn mehrere Drucker ihn nutzen) + if (-not $backedUpDrivers.ContainsKey($driverName)) { + Write-Log " Treiber sichern: $driverName" + + $safeDirName = $driverName -replace '[\\/:*?"<>|]', '_' + $driverDir = Join-Path $driversDir $safeDirName + New-Item -ItemType Directory -Path $driverDir -Force | Out-Null + + $filesCopied = 0 + + # Ganzes Treiberpaket-Verzeichnis kopieren (bevorzugt) + $pkgDir = $printer.Driver.PackageDirectory + if ($pkgDir -and (Test-Path $pkgDir)) { + try { + $pkgDest = Join-Path $driverDir "package" + Copy-Item -Path $pkgDir -Destination $pkgDest -Recurse -Force -ErrorAction Stop + $filesCopied = (Get-ChildItem -Path $pkgDest -Recurse -File).Count + Write-Log " Treiberpaket kopiert ($filesCopied Dateien)" + + $backedUpDrivers[$driverName] = @{ + BackupPackageDir = "package" + BackupInfPath = if ($printer.Driver.InfFileName) { + "package\$($printer.Driver.InfFileName)" + } else { "" } + } + } catch { + Write-Log " Warnung: Treiberpaket konnte nicht kopiert werden: $_" + } + } + + # Einzelne Treiberdateien als Fallback kopieren + if ($filesCopied -eq 0 -and $printer.Driver.Files -and $printer.Driver.Files.Count -gt 0) { + $filesDir = Join-Path $driverDir "files" + New-Item -ItemType Directory -Path $filesDir -Force | Out-Null + foreach ($file in $printer.Driver.Files) { + if ($file -and (Test-Path $file)) { + try { + Copy-Item -Path $file -Destination $filesDir -Force -ErrorAction Stop + $filesCopied++ + } catch { + Write-Log " Warnung: $file konnte nicht kopiert werden" + } + } + } + if ($filesCopied -gt 0) { + Write-Log " $filesCopied Einzeldateien kopiert" + } + } + + if ($filesCopied -eq 0) { + Write-Log " Hinweis: Keine Treiberdateien gesichert - Treiber muss ggf. manuell installiert werden" + } + + if (-not $backedUpDrivers.ContainsKey($driverName)) { + $backedUpDrivers[$driverName] = @{ + BackupPackageDir = "" + BackupInfPath = "" + } + } + } + + # Backup-Pfade im Export setzen + $driverBackup = $backedUpDrivers[$driverName] + $exportPrinter.Driver.BackupPackageDir = $driverBackup.BackupPackageDir + $exportPrinter.Driver.BackupInfPath = $driverBackup.BackupInfPath + + Write-Log " Drucker '$($printer.Name)' verarbeitet." + $exportPrinters += $exportPrinter + } + + # JSON-Konfiguration schreiben + $exportData = @{ + ExportDate = (Get-Date -Format "o") + ComputerName = $env:COMPUTERNAME + OSVersion = [System.Environment]::OSVersion.VersionString + ToolVersion = "1.0" + Printers = $exportPrinters + } + + $jsonPath = Join-Path $tempDir "config.json" + $exportData | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonPath -Encoding UTF8 + + # ZIP erstellen + $currentStep++ + Update-Progress -Current $currentStep -Total $totalSteps ` + -Status "Sicherungsdatei erstellen..." + + if (Test-Path $OutputPath) { + Remove-Item $OutputPath -Force + } + + Compress-Archive -Path "$tempDir\*" -DestinationPath $OutputPath -Force + + $fileSize = [math]::Round((Get-Item $OutputPath).Length / 1MB, 2) + Write-Log "" + Write-Log "=== Sicherung erfolgreich ===" + Write-Log "Datei: $OutputPath" + Write-Log "Groesse: $fileSize MB" + Write-Log "$($SelectedPrinters.Count) Drucker gesichert." + + Update-Progress -Current $totalSteps -Total $totalSteps -Status "Fertig!" + return $true + + } catch { + Write-Log "Fehler bei der Sicherung: $_" "ERROR" + return $false + } finally { + if (Test-Path $tempDir) { + Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } + } +} + +# ============================================================ +# IMPORT - Sicherungsdatei laden +# ============================================================ +function Import-PrinterBackup { + param([string]$BackupPath) + + $tempDir = Join-Path $env:TEMP "PrinterMigrator_Import_$(Get-Date -Format 'yyyyMMdd_HHmmss')" + + try { + Expand-Archive -Path $BackupPath -DestinationPath $tempDir -Force + + $jsonPath = Join-Path $tempDir "config.json" + if (-not (Test-Path $jsonPath)) { + Write-Log "Fehler: config.json nicht in der Sicherungsdatei gefunden!" "ERROR" + return $null + } + + $importData = Get-Content -Path $jsonPath -Raw -Encoding UTF8 | ConvertFrom-Json + + Write-Log "Sicherung geladen:" + Write-Log " Erstellt am: $($importData.ExportDate)" + Write-Log " Computer: $($importData.ComputerName)" + Write-Log " $($importData.Printers.Count) Drucker in der Sicherung" + + return @{ + Data = $importData + TempDir = $tempDir + } + } catch { + Write-Log "Fehler beim Lesen der Sicherung: $_" "ERROR" + if (Test-Path $tempDir) { + Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } + return $null + } +} + +# ============================================================ +# IMPORT - Anschluss wiederherstellen +# ============================================================ +function Restore-PrinterPort { + param([PSCustomObject]$PortData) + + $portName = $PortData.Name + + # Pruefen ob Anschluss bereits existiert + $existing = Get-PrinterPort -Name $portName -ErrorAction SilentlyContinue + if ($existing) { + Write-Log " Anschluss '$portName' existiert bereits." + return $true + } + + try { + switch ($PortData.Type) { + "TCPIP" { + $address = $PortData.HostAddress + $portNumber = if ($PortData.PortNumber) { [int]$PortData.PortNumber } else { 9100 } + + if (-not $address) { + Write-Log " Fehler: Keine IP-Adresse fuer Anschluss '$portName'" "ERROR" + return $false + } + + # LPR-Protokoll (Protocol=2) oder RAW (Protocol=1, Standard) + if ($PortData.Protocol -eq 2) { + $queue = if ($PortData.Queue) { $PortData.Queue } else { "lp" } + Add-PrinterPort -Name $portName ` + -PrinterHostAddress $address ` + -LprHostAddress $address ` + -LprQueueName $queue ` + -LprByteCounting:$true -ErrorAction Stop + Write-Log " LPR-Anschluss erstellt: $portName -> $address (Queue: $queue)" + } + else { + Add-PrinterPort -Name $portName ` + -PrinterHostAddress $address ` + -PortNumber $portNumber -ErrorAction Stop + Write-Log " TCP/IP-Anschluss erstellt: $portName -> ${address}:${portNumber}" + } + + # SNMP konfigurieren falls aktiviert + if ($PortData.SNMPEnabled -eq $true) { + try { + $snmpCommunity = if ($PortData.SNMPCommunity) { $PortData.SNMPCommunity } else { "public" } + $snmpIndex = if ($PortData.SNMPDevIndex) { [int]$PortData.SNMPDevIndex } else { 1 } + $wmiPort = Get-CimInstance -ClassName Win32_TCPIPPrinterPort -Filter "Name='$($portName -replace "'","''")'" + if ($wmiPort) { + Set-CimInstance -InputObject $wmiPort -Property @{ + SNMPEnabled = $true + SNMPCommunity = $snmpCommunity + SNMPDevIndex = $snmpIndex + } + Write-Log " SNMP aktiviert ($snmpCommunity)" + } + } catch { + Write-Log " Warnung: SNMP konnte nicht konfiguriert werden" + } + } + + return $true + } + "Local" { + Write-Log " Lokaler Anschluss '$portName' (systemverwaltet)" + return $true + } + "File" { + Write-Log " Dateianschluss '$portName' (immer verfuegbar)" + return $true + } + "USB" { + Write-Log " USB-Anschluss '$portName' - wird beim Anschliessen des Geraets erstellt" + return $true + } + "WSD" { + Write-Log " WSD-Anschluss '$portName' - wird durch Netzwerkerkennung erstellt" + return $true + } + "TerminalService" { + Write-Log " TS-Anschluss '$portName' - wird durch Terminaldienste verwaltet" + return $true + } + default { + # Generischen Anschluss versuchen zu erstellen + try { + Add-PrinterPort -Name $portName -ErrorAction Stop + Write-Log " Anschluss erstellt: $portName" + return $true + } catch { + Write-Log " Warnung: Anschluss '$portName' konnte nicht erstellt werden: $_" + return $false + } + } + } + } catch { + Write-Log " Fehler beim Erstellen des Anschlusses '$portName': $_" "ERROR" + return $false + } +} + +# ============================================================ +# IMPORT - Treiber wiederherstellen +# ============================================================ +function Restore-PrinterDriver { + param( + [PSCustomObject]$DriverData, + [string]$BackupTempDir + ) + + $driverName = $DriverData.Name + + # Pruefen ob Treiber bereits installiert ist + $existing = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue + if ($existing) { + Write-Log " Treiber '$driverName' ist bereits installiert." + return $true + } + + # Treiber-Backup-Verzeichnis finden + $safeDirName = $driverName -replace '[\\/:*?"<>|]', '_' + $driverBackupDir = Join-Path $BackupTempDir "drivers" $safeDirName + + if (-not (Test-Path $driverBackupDir)) { + Write-Log " Fehler: Treiberdateien fuer '$driverName' nicht in der Sicherung!" "ERROR" + Write-Log " -> Bitte installieren Sie den Treiber manuell." + return $false + } + + # Methode 1: INF-basierte Installation (bevorzugt) + $infPath = $null + + if ($DriverData.BackupInfPath) { + $testInf = Join-Path $driverBackupDir $DriverData.BackupInfPath + if (Test-Path $testInf) { + $infPath = $testInf + } + } + + # INF-Datei im Backup suchen falls nicht gefunden + if (-not $infPath) { + $infFiles = Get-ChildItem -Path $driverBackupDir -Filter "*.inf" -Recurse -ErrorAction SilentlyContinue + if ($infFiles) { + $infPath = $infFiles[0].FullName + } + } + + if ($infPath) { + try { + Write-Log " Treiber installieren via INF: $(Split-Path -Leaf $infPath)" + + # Treiber zum DriverStore hinzufuegen + $pnpResult = & pnputil.exe /add-driver "$infPath" /install 2>&1 + $pnpOutput = ($pnpResult | Out-String).Trim() + if ($pnpOutput) { + Write-Log " pnputil: $pnpOutput" + } + + # Druckertreiber registrieren + Add-PrinterDriver -Name $driverName -InfPath $infPath -ErrorAction Stop + Write-Log " Treiber '$driverName' erfolgreich installiert." + return $true + } catch { + Write-Log " Warnung: INF-Installation fehlgeschlagen: $_" + # Pruefen ob pnputil den Treiber trotzdem installiert hat + $retryDriver = Get-PrinterDriver -Name $driverName -ErrorAction SilentlyContinue + if ($retryDriver) { + Write-Log " Treiber wurde dennoch gefunden - OK." + return $true + } + } + } + + Write-Log " Fehler: Treiber '$driverName' konnte nicht installiert werden." "ERROR" + Write-Log " -> Bitte installieren Sie den Treiber manuell und fuehren Sie die Wiederherstellung erneut aus." + return $false +} + +# ============================================================ +# IMPORT - Einzelnen Drucker wiederherstellen +# ============================================================ +function Restore-SinglePrinter { + param( + [PSCustomObject]$PrinterData, + [string]$BackupTempDir, + [bool]$Overwrite = $false + ) + + $printerName = $PrinterData.Name + Write-Log "" + Write-Log "--- Drucker: $printerName ---" + + # Pruefen ob Drucker bereits existiert + $existing = Get-Printer -Name $printerName -ErrorAction SilentlyContinue + if ($existing) { + if ($Overwrite) { + Write-Log " Vorhandener Drucker wird entfernt..." + Remove-Printer -Name $printerName -ErrorAction SilentlyContinue + Start-Sleep -Milliseconds 500 + } else { + Write-Log " Drucker existiert bereits - wird uebersprungen." + Write-Log " (Aktivieren Sie 'Vorhandene ueberschreiben' um zu ersetzen)" + return $true + } + } + + # Schritt 1: Anschluss erstellen + Write-Log " [1/3] Anschluss erstellen..." + $portOk = Restore-PrinterPort -PortData $PrinterData.Port + + # Pruefen ob Anschluss verfuegbar ist + $portExists = Get-PrinterPort -Name $PrinterData.PortName -ErrorAction SilentlyContinue + if (-not $portExists -and -not $portOk) { + Write-Log " ABBRUCH: Anschluss '$($PrinterData.PortName)' nicht verfuegbar!" "ERROR" + return $false + } + + # Schritt 2: Treiber installieren + Write-Log " [2/3] Treiber installieren..." + $driverOk = Restore-PrinterDriver -DriverData $PrinterData.Driver -BackupTempDir $BackupTempDir + + if (-not $driverOk) { + $driverExists = Get-PrinterDriver -Name $PrinterData.DriverName -ErrorAction SilentlyContinue + if (-not $driverExists) { + Write-Log " ABBRUCH: Treiber '$($PrinterData.DriverName)' nicht verfuegbar!" "ERROR" + return $false + } + } + + # Schritt 3: Drucker erstellen + Write-Log " [3/3] Drucker erstellen..." + try { + $addParams = @{ + Name = $printerName + DriverName = $PrinterData.DriverName + PortName = $PrinterData.PortName + } + + if ($PrinterData.Comment) { $addParams.Comment = $PrinterData.Comment } + if ($PrinterData.Location) { $addParams.Location = $PrinterData.Location } + + if ($PrinterData.Shared -eq $true) { + $addParams.Shared = $true + if ($PrinterData.ShareName) { $addParams.ShareName = $PrinterData.ShareName } + } + + Add-Printer @addParams -ErrorAction Stop + Write-Log " Drucker '$printerName' erstellt." + + # Standarddrucker setzen + if ($PrinterData.IsDefault -eq $true) { + try { + $escapedName = $printerName -replace "'","''" + $wmPrinter = Get-CimInstance -ClassName Win32_Printer -Filter "Name='$escapedName'" + if ($wmPrinter) { + Invoke-CimMethod -InputObject $wmPrinter -MethodName SetDefaultPrinter | Out-Null + Write-Log " Als Standarddrucker festgelegt." + } + } catch { + Write-Log " Warnung: Standarddrucker konnte nicht gesetzt werden" + } + } + + # Druckkonfiguration setzen + try { + if ($PrinterData.DuplexingMode -and $PrinterData.DuplexingMode -ne '') { + Set-PrintConfiguration -PrinterName $printerName ` + -DuplexingMode $PrinterData.DuplexingMode -ErrorAction SilentlyContinue + } + if ($PrinterData.Color -ne $null) { + Set-PrintConfiguration -PrinterName $printerName ` + -Color $PrinterData.Color -ErrorAction SilentlyContinue + } + } catch {} + + Write-Log " -> Erfolgreich!" + return $true + + } catch { + Write-Log " Fehler beim Erstellen des Druckers: $_" "ERROR" + return $false + } +} + +# ============================================================ +# IMPORT - Ausgewaehlte Drucker wiederherstellen +# ============================================================ +function Restore-SelectedPrinters { + param( + [array]$SelectedPrinters, + [string]$BackupTempDir, + [bool]$Overwrite = $false + ) + + $total = $SelectedPrinters.Count + $successCount = 0 + $failCount = 0 + + for ($i = 0; $i -lt $total; $i++) { + $printer = $SelectedPrinters[$i] + Update-Progress -Current ($i + 1) -Total $total ` + -Status "Wiederherstellen ($($i+1)/$total): $($printer.Name)" + + $result = Restore-SinglePrinter -PrinterData $printer ` + -BackupTempDir $BackupTempDir -Overwrite $Overwrite + + if ($result) { $successCount++ } else { $failCount++ } + } + + Update-Progress -Current $total -Total $total -Status "Fertig!" + Write-Log "" + Write-Log "=========================================" + Write-Log " Wiederherstellung abgeschlossen" + Write-Log " Erfolgreich: $successCount / $total" + if ($failCount -gt 0) { + Write-Log " Fehlgeschlagen: $failCount" + } + Write-Log "=========================================" +} + +# ============================================================ +# GUI - Hauptfenster +# ============================================================ +function Show-MainForm { + + $form = New-Object System.Windows.Forms.Form + $form.Text = "Drucker-Migrationstool" + $form.Size = New-Object System.Drawing.Size(780, 680) + $form.StartPosition = "CenterScreen" + $form.MinimumSize = New-Object System.Drawing.Size(650, 550) + $form.Font = New-Object System.Drawing.Font("Segoe UI", 9) + $form.BackColor = [System.Drawing.Color]::FromArgb(245, 245, 245) + + # Icon aus printui.exe laden + try { + $printUiPath = "$env:SystemRoot\System32\printui.exe" + if (Test-Path $printUiPath) { + $form.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($printUiPath) + } + } catch {} + + # ============================================================ + # OBERER BEREICH - Tabs mit Drucker-Listen + # ============================================================ + $topPanel = New-Object System.Windows.Forms.Panel + $topPanel.Dock = [System.Windows.Forms.DockStyle]::Fill + $topPanel.Padding = New-Object System.Windows.Forms.Padding(8, 8, 8, 0) + + $tabControl = New-Object System.Windows.Forms.TabControl + $tabControl.Dock = [System.Windows.Forms.DockStyle]::Fill + + # ---- TAB: SICHERN ---- + $tabExport = New-Object System.Windows.Forms.TabPage + $tabExport.Text = " Sichern " + $tabExport.Padding = New-Object System.Windows.Forms.Padding(12) + $tabExport.UseVisualStyleBackColor = $true + + $pnlExport = New-Object System.Windows.Forms.Panel + $pnlExport.Dock = [System.Windows.Forms.DockStyle]::Fill + + $lblExportTitle = New-Object System.Windows.Forms.Label + $lblExportTitle.Text = "Installierte Drucker - waehlen Sie aus, welche gesichert werden sollen:" + $lblExportTitle.Dock = [System.Windows.Forms.DockStyle]::Top + $lblExportTitle.Height = 25 + $lblExportTitle.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + + $lstExport = New-Object System.Windows.Forms.CheckedListBox + $lstExport.Dock = [System.Windows.Forms.DockStyle]::Fill + $lstExport.CheckOnClick = $true + $lstExport.BorderStyle = "FixedSingle" + $lstExport.IntegralHeight = $false + + $pnlExportButtons = New-Object System.Windows.Forms.Panel + $pnlExportButtons.Dock = [System.Windows.Forms.DockStyle]::Bottom + $pnlExportButtons.Height = 50 + $pnlExportButtons.Padding = New-Object System.Windows.Forms.Padding(0, 8, 0, 0) + + $btnExportAll = New-Object System.Windows.Forms.Button + $btnExportAll.Text = "Alle auswaehlen" + $btnExportAll.Location = New-Object System.Drawing.Point(0, 10) + $btnExportAll.Size = New-Object System.Drawing.Size(130, 30) + $btnExportAll.FlatStyle = "Flat" + + $btnExportNone = New-Object System.Windows.Forms.Button + $btnExportNone.Text = "Keine auswaehlen" + $btnExportNone.Location = New-Object System.Drawing.Point(140, 10) + $btnExportNone.Size = New-Object System.Drawing.Size(130, 30) + $btnExportNone.FlatStyle = "Flat" + + $btnExport = New-Object System.Windows.Forms.Button + $btnExport.Text = " Sicherung erstellen... " + $btnExport.Location = New-Object System.Drawing.Point(310, 6) + $btnExport.Size = New-Object System.Drawing.Size(210, 36) + $btnExport.FlatStyle = "Flat" + $btnExport.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 215) + $btnExport.ForeColor = [System.Drawing.Color]::White + $btnExport.Font = New-Object System.Drawing.Font("Segoe UI", 9.5, [System.Drawing.FontStyle]::Bold) + $btnExport.Cursor = [System.Windows.Forms.Cursors]::Hand + + $pnlExportButtons.Controls.AddRange(@($btnExportAll, $btnExportNone, $btnExport)) + + $pnlExport.Controls.Add($lstExport) + $pnlExport.Controls.Add($pnlExportButtons) + $pnlExport.Controls.Add($lblExportTitle) + $tabExport.Controls.Add($pnlExport) + + # ---- TAB: WIEDERHERSTELLEN ---- + $tabImport = New-Object System.Windows.Forms.TabPage + $tabImport.Text = " Wiederherstellen " + $tabImport.Padding = New-Object System.Windows.Forms.Padding(12) + $tabImport.UseVisualStyleBackColor = $true + + $pnlImport = New-Object System.Windows.Forms.Panel + $pnlImport.Dock = [System.Windows.Forms.DockStyle]::Fill + + # Dateiauswahl-Zeile + $pnlImportFile = New-Object System.Windows.Forms.Panel + $pnlImportFile.Dock = [System.Windows.Forms.DockStyle]::Top + $pnlImportFile.Height = 62 + + $lblImportFile = New-Object System.Windows.Forms.Label + $lblImportFile.Text = "Sicherungsdatei (.pmb) auswaehlen:" + $lblImportFile.Location = New-Object System.Drawing.Point(0, 0) + $lblImportFile.AutoSize = $true + $lblImportFile.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + + $txtImportPath = New-Object System.Windows.Forms.TextBox + $txtImportPath.Location = New-Object System.Drawing.Point(0, 22) + $txtImportPath.Size = New-Object System.Drawing.Size(550, 25) + $txtImportPath.Anchor = "Top,Left,Right" + $txtImportPath.ReadOnly = $true + $txtImportPath.BackColor = [System.Drawing.Color]::White + + $btnBrowse = New-Object System.Windows.Forms.Button + $btnBrowse.Text = "Durchsuchen..." + $btnBrowse.Location = New-Object System.Drawing.Point(558, 20) + $btnBrowse.Size = New-Object System.Drawing.Size(120, 28) + $btnBrowse.Anchor = "Top,Right" + $btnBrowse.FlatStyle = "Flat" + + $pnlImportFile.Controls.AddRange(@($lblImportFile, $txtImportPath, $btnBrowse)) + + # Druckerliste + $lblImportList = New-Object System.Windows.Forms.Label + $lblImportList.Text = "Drucker in der Sicherung:" + $lblImportList.Dock = [System.Windows.Forms.DockStyle]::Top + $lblImportList.Height = 22 + + $lstImport = New-Object System.Windows.Forms.CheckedListBox + $lstImport.Dock = [System.Windows.Forms.DockStyle]::Fill + $lstImport.CheckOnClick = $true + $lstImport.BorderStyle = "FixedSingle" + $lstImport.IntegralHeight = $false + + # Buttons unten + $pnlImportButtons = New-Object System.Windows.Forms.Panel + $pnlImportButtons.Dock = [System.Windows.Forms.DockStyle]::Bottom + $pnlImportButtons.Height = 50 + $pnlImportButtons.Padding = New-Object System.Windows.Forms.Padding(0, 8, 0, 0) + + $btnImportAll = New-Object System.Windows.Forms.Button + $btnImportAll.Text = "Alle auswaehlen" + $btnImportAll.Location = New-Object System.Drawing.Point(0, 10) + $btnImportAll.Size = New-Object System.Drawing.Size(130, 30) + $btnImportAll.FlatStyle = "Flat" + + $btnImportNone = New-Object System.Windows.Forms.Button + $btnImportNone.Text = "Keine auswaehlen" + $btnImportNone.Location = New-Object System.Drawing.Point(140, 10) + $btnImportNone.Size = New-Object System.Drawing.Size(130, 30) + $btnImportNone.FlatStyle = "Flat" + + $chkOverwrite = New-Object System.Windows.Forms.CheckBox + $chkOverwrite.Text = "Vorhandene ueberschreiben" + $chkOverwrite.Location = New-Object System.Drawing.Point(290, 14) + $chkOverwrite.AutoSize = $true + + $btnImport = New-Object System.Windows.Forms.Button + $btnImport.Text = " Wiederherstellen... " + $btnImport.Location = New-Object System.Drawing.Point(500, 6) + $btnImport.Size = New-Object System.Drawing.Size(200, 36) + $btnImport.Anchor = "Top,Right" + $btnImport.FlatStyle = "Flat" + $btnImport.BackColor = [System.Drawing.Color]::FromArgb(16, 124, 16) + $btnImport.ForeColor = [System.Drawing.Color]::White + $btnImport.Font = New-Object System.Drawing.Font("Segoe UI", 9.5, [System.Drawing.FontStyle]::Bold) + $btnImport.Cursor = [System.Windows.Forms.Cursors]::Hand + + $pnlImportButtons.Controls.AddRange(@($btnImportAll, $btnImportNone, $chkOverwrite, $btnImport)) + + $pnlImport.Controls.Add($lstImport) + $pnlImport.Controls.Add($lblImportList) + $pnlImport.Controls.Add($pnlImportButtons) + $pnlImport.Controls.Add($pnlImportFile) + $tabImport.Controls.Add($pnlImport) + + # Tabs zum TabControl hinzufuegen + $tabControl.TabPages.Add($tabExport) + $tabControl.TabPages.Add($tabImport) + $topPanel.Controls.Add($tabControl) + + # ============================================================ + # UNTERER BEREICH - Protokoll & Fortschritt + # ============================================================ + $bottomPanel = New-Object System.Windows.Forms.Panel + $bottomPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom + $bottomPanel.Height = 200 + $bottomPanel.Padding = New-Object System.Windows.Forms.Padding(8, 4, 8, 8) + + $lblLog = New-Object System.Windows.Forms.Label + $lblLog.Text = "Protokoll:" + $lblLog.Dock = [System.Windows.Forms.DockStyle]::Top + $lblLog.Height = 20 + $lblLog.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + + $logBox = New-Object System.Windows.Forms.TextBox + $logBox.Multiline = $true + $logBox.ReadOnly = $true + $logBox.ScrollBars = "Vertical" + $logBox.Dock = [System.Windows.Forms.DockStyle]::Fill + $logBox.Font = New-Object System.Drawing.Font("Consolas", 8.5) + $logBox.BackColor = [System.Drawing.Color]::FromArgb(30, 30, 30) + $logBox.ForeColor = [System.Drawing.Color]::FromArgb(220, 220, 220) + $logBox.WordWrap = $false + $script:LogBox = $logBox + + $statusPanel = New-Object System.Windows.Forms.Panel + $statusPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom + $statusPanel.Height = 42 + + $statusLabel = New-Object System.Windows.Forms.Label + $statusLabel.Text = "Bereit" + $statusLabel.Dock = [System.Windows.Forms.DockStyle]::Top + $statusLabel.Height = 18 + $script:StatusLabel = $statusLabel + + $progressBar = New-Object System.Windows.Forms.ProgressBar + $progressBar.Dock = [System.Windows.Forms.DockStyle]::Bottom + $progressBar.Height = 22 + $progressBar.Style = "Continuous" + $script:ProgressBar = $progressBar + + $statusPanel.Controls.Add($progressBar) + $statusPanel.Controls.Add($statusLabel) + + $bottomPanel.Controls.Add($logBox) + $bottomPanel.Controls.Add($lblLog) + $bottomPanel.Controls.Add($statusPanel) + + # Splitter zwischen oben und unten + $splitter = New-Object System.Windows.Forms.Splitter + $splitter.Dock = [System.Windows.Forms.DockStyle]::Bottom + $splitter.Height = 5 + $splitter.BackColor = [System.Drawing.Color]::FromArgb(200, 200, 200) + + $form.Controls.Add($topPanel) + $form.Controls.Add($splitter) + $form.Controls.Add($bottomPanel) + + # ============================================================ + # Datenhaltung + # ============================================================ + $script:AllPrinterData = @() + $script:ImportData = $null + $script:ImportTempDir = "" + + # ============================================================ + # EVENT HANDLER + # ============================================================ + + # --- Formular wird angezeigt: Drucker laden --- + $form.Add_Shown({ + Write-Log "Drucker werden geladen..." + $script:AllPrinterData = Get-AllPrinterData + + $lstExport.Items.Clear() + foreach ($p in $script:AllPrinterData) { + $portInfo = switch ($p.Port.Type) { + "TCPIP" { "$($p.Port.HostAddress)" + $(if ($p.Port.PortNumber -and $p.Port.PortNumber -ne 9100) { ":$($p.Port.PortNumber)" } else { "" }) } + default { $p.PortName } + } + $default = if ($p.IsDefault) { " *" } else { "" } + $display = "$($p.Name) | $portInfo | $($p.DriverName)$default" + $lstExport.Items.Add($display) | Out-Null + } + + Write-Log "$($script:AllPrinterData.Count) Drucker gefunden." + if ($script:AllPrinterData.Count -gt 0) { + Write-Log "(* = Standarddrucker)" + } + Write-Log "" + }) + + # --- Alle/Keine auswaehlen: Export --- + $btnExportAll.Add_Click({ + for ($i = 0; $i -lt $lstExport.Items.Count; $i++) { + $lstExport.SetItemChecked($i, $true) + } + }) + $btnExportNone.Add_Click({ + for ($i = 0; $i -lt $lstExport.Items.Count; $i++) { + $lstExport.SetItemChecked($i, $false) + } + }) + + # --- SICHERUNG ERSTELLEN --- + $btnExport.Add_Click({ + $checkedIndices = @($lstExport.CheckedIndices) + if ($checkedIndices.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte waehlen Sie mindestens einen Drucker aus.", + "Keine Auswahl", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) | Out-Null + return + } + + $saveDialog = New-Object System.Windows.Forms.SaveFileDialog + $saveDialog.Filter = "Drucker-Sicherung (*.pmb)|*.pmb|Alle Dateien (*.*)|*.*" + $saveDialog.DefaultExt = "pmb" + $saveDialog.FileName = "Drucker_$($env:COMPUTERNAME)_$(Get-Date -Format 'yyyy-MM-dd')" + $saveDialog.Title = "Sicherungsdatei speichern" + + if ($saveDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $selectedPrinters = @() + foreach ($idx in $checkedIndices) { + $selectedPrinters += $script:AllPrinterData[$idx] + } + + $script:ProgressBar.Value = 0 + Write-Log "=== SICHERUNG STARTEN ===" + Write-Log "Ausgewaehlt: $($selectedPrinters.Count) Drucker" + Write-Log "" + + $btnExport.Enabled = $false + $btnExportAll.Enabled = $false + $btnExportNone.Enabled = $false + + $result = Export-PrinterBackup -SelectedPrinters $selectedPrinters ` + -OutputPath $saveDialog.FileName + + $btnExport.Enabled = $true + $btnExportAll.Enabled = $true + $btnExportNone.Enabled = $true + + if ($result) { + [System.Windows.Forms.MessageBox]::Show( + "Sicherung erfolgreich erstellt!`n`n$($saveDialog.FileName)`n`nKopieren Sie diese Datei auf den Zielrechner und verwenden Sie dort den Tab 'Wiederherstellen'.", + "Sicherung erfolgreich", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Information + ) | Out-Null + } else { + [System.Windows.Forms.MessageBox]::Show( + "Bei der Sicherung sind Fehler aufgetreten.`nDetails siehe Protokoll.", + "Fehler", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error + ) | Out-Null + } + } + }) + + # --- DATEI DURCHSUCHEN --- + $btnBrowse.Add_Click({ + $openDialog = New-Object System.Windows.Forms.OpenFileDialog + $openDialog.Filter = "Drucker-Sicherung (*.pmb)|*.pmb|ZIP-Dateien (*.zip)|*.zip|Alle Dateien (*.*)|*.*" + $openDialog.Title = "Sicherungsdatei oeffnen" + + if ($openDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $txtImportPath.Text = $openDialog.FileName + + # Vorherigen Import aufraeumen + if ($script:ImportTempDir -and (Test-Path $script:ImportTempDir)) { + Remove-Item $script:ImportTempDir -Recurse -Force -ErrorAction SilentlyContinue + } + + Write-Log "" + Write-Log "=== SICHERUNG LADEN ===" + + $importResult = Import-PrinterBackup -BackupPath $openDialog.FileName + + $lstImport.Items.Clear() + + if ($importResult) { + $script:ImportData = $importResult.Data + $script:ImportTempDir = $importResult.TempDir + + foreach ($p in $script:ImportData.Printers) { + $portInfo = switch ($p.Port.Type) { + "TCPIP" { "$($p.Port.HostAddress)" + $(if ($p.Port.PortNumber -and $p.Port.PortNumber -ne 9100) { ":$($p.Port.PortNumber)" } else { "" }) } + default { $p.PortName } + } + $default = if ($p.IsDefault) { " *" } else { "" } + $exists = if (Get-Printer -Name $p.Name -ErrorAction SilentlyContinue) { " [vorhanden]" } else { "" } + $display = "$($p.Name) | $portInfo | $($p.DriverName)$default$exists" + $lstImport.Items.Add($display) | Out-Null + } + + Write-Log "" + } + } + }) + + # --- Alle/Keine auswaehlen: Import --- + $btnImportAll.Add_Click({ + for ($i = 0; $i -lt $lstImport.Items.Count; $i++) { + $lstImport.SetItemChecked($i, $true) + } + }) + $btnImportNone.Add_Click({ + for ($i = 0; $i -lt $lstImport.Items.Count; $i++) { + $lstImport.SetItemChecked($i, $false) + } + }) + + # --- WIEDERHERSTELLEN --- + $btnImport.Add_Click({ + $checkedIndices = @($lstImport.CheckedIndices) + if ($checkedIndices.Count -eq 0) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte waehlen Sie mindestens einen Drucker aus.", + "Keine Auswahl", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) | Out-Null + return + } + + if (-not $script:ImportData) { + [System.Windows.Forms.MessageBox]::Show( + "Bitte laden Sie zuerst eine Sicherungsdatei.", + "Keine Sicherung geladen", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) | Out-Null + return + } + + $confirmResult = [System.Windows.Forms.MessageBox]::Show( + "Moechten Sie $($checkedIndices.Count) Drucker wiederherstellen?`n`nDabei werden Anschluesse, Treiber und Drucker erstellt.", + "Wiederherstellung bestaetigen", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Question + ) + + if ($confirmResult -eq [System.Windows.Forms.DialogResult]::Yes) { + $selectedPrinters = @() + foreach ($idx in $checkedIndices) { + $selectedPrinters += $script:ImportData.Printers[$idx] + } + + $script:ProgressBar.Value = 0 + Write-Log "" + Write-Log "=== WIEDERHERSTELLUNG STARTEN ===" + Write-Log "Ausgewaehlt: $($selectedPrinters.Count) Drucker" + + $btnImport.Enabled = $false + $btnImportAll.Enabled = $false + $btnImportNone.Enabled = $false + $btnBrowse.Enabled = $false + + Restore-SelectedPrinters -SelectedPrinters $selectedPrinters ` + -BackupTempDir $script:ImportTempDir -Overwrite $chkOverwrite.Checked + + $btnImport.Enabled = $true + $btnImportAll.Enabled = $true + $btnImportNone.Enabled = $true + $btnBrowse.Enabled = $true + + [System.Windows.Forms.MessageBox]::Show( + "Wiederherstellung abgeschlossen!`nDetails siehe Protokoll.", + "Fertig", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Information + ) | Out-Null + } + }) + + # --- Aufraeumen beim Schliessen --- + $form.Add_FormClosing({ + if ($script:ImportTempDir -and (Test-Path $script:ImportTempDir)) { + Remove-Item $script:ImportTempDir -Recurse -Force -ErrorAction SilentlyContinue + } + }) + + # ============================================================ + # FORMULAR ANZEIGEN + # ============================================================ + [void]$form.ShowDialog() + $form.Dispose() +} + +# ============================================================ +# PROGRAMMSTART +# ============================================================ +Show-MainForm