常用Powershell脚本

视频缩略图广告清理

<#
.SYNOPSIS
清除视频文件内嵌的广告缩略图/封面图,递归处理指定文件夹
#>

#Requires -Version 5.1

param(
    [string]$FolderPath = ""
)

# -------------------------- 配置项 --------------------------
$videoExtensions = @(".mp4", ".mkv", ".avi", ".mov", ".m4v", ".flv", ".wmv", ".webm", ".rmvb", ".ts")
$deleteSidecarImages = $true   # 是否删除与视频同名的外部图片文件(广告封面)
$imageExtensions = @(".jpg", ".jpeg", ".png", ".bmp", ".webp")
$tempSuffix = "_clean_temp"    # 临时文件后缀
# -----------------------------------------------------------

# 检查 ffmpeg 是否可用
function Test-FFmpeg {
    try {
        $null = ffmpeg -version 2>&1
        return $true
    }
    catch {
        return $false
    }
}

# 主函数
function Invoke-VideoThumbnailCleaner {
    param([string]$RootFolder)

    if (-not (Test-Path -LiteralPath $RootFolder -PathType Container)) {
        Write-Host "错误:文件夹不存在 - $RootFolder" -ForegroundColor Red
        return
    }

    # 获取所有视频文件
    Write-Host "`n正在扫描视频文件..." -ForegroundColor Cyan
    $videoFiles = Get-ChildItem -LiteralPath $RootFolder -Recurse -File | 
                  Where-Object { $videoExtensions -contains $_.Extension.ToLower() }

    if ($videoFiles.Count -eq 0) {
        Write-Host "未找到任何视频文件。" -ForegroundColor Yellow
        return
    }

    Write-Host "找到 $($videoFiles.Count) 个视频文件,开始处理...`n" -ForegroundColor Green

    $successCount = 0
    $skipCount = 0
    $failCount = 0
    $deletedImageCount = 0

    foreach ($file in $videoFiles) {
        Write-Host "[$($successCount + $failCount + $skipCount + 1)/$($videoFiles.Count)] 处理: $($file.FullName)"

        $tempPath = Join-Path $file.DirectoryName ($file.BaseName + $tempSuffix + $file.Extension)

        try {
            # ffmpeg 重新封装:只保留第1个视频流 + 所有音频 + 所有字幕,丢弃附加图片流
            # -map 0:v:0  只取第一个视频流(主视频,排除缩略图附加流)
            # -map 0:a?   保留所有音频流
            # -map 0:s?   保留所有字幕流
            # -c copy     直接复制流,不重新编码
            $ffmpegArgs = @(
                "-i", "`"$($file.FullName)`"",
                "-map", "0:v:0",
                "-map", "0:a?",
                "-map", "0:s?",
                "-c", "copy",
                "-y",
                "`"$tempPath`""
            )

            $process = Start-Process -FilePath "ffmpeg" -ArgumentList $ffmpegArgs `
                -NoNewWindow -Wait -PassThru -RedirectStandardError "$env:TEMP\ffmpeg_err.log"

            if ($process.ExitCode -eq 0 -and (Test-Path $tempPath)) {
                # 替换原文件
                Remove-Item -LiteralPath $file.FullName -Force
                Move-Item -LiteralPath $tempPath -Destination $file.FullName -Force
                $successCount++
                Write-Host "  ✓ 清理完成" -ForegroundColor Green
            }
            else {
                $skipCount++
                Write-Host "  ⊘ 无需清理或处理失败,跳过" -ForegroundColor Yellow
                if (Test-Path $tempPath) { Remove-Item $tempPath -Force -ErrorAction SilentlyContinue }
            }
        }
        catch {
            $failCount++
            Write-Host "  ✗ 处理出错: $($_.Exception.Message)" -ForegroundColor Red
            if (Test-Path $tempPath) { Remove-Item $tempPath -Force -ErrorAction SilentlyContinue }
        }

        # 删除同名外部广告图片
        if ($deleteSidecarImages) {
            foreach ($imgExt in $imageExtensions) {
                $sidecarImg = Join-Path $file.DirectoryName ($file.BaseName + $imgExt)
                if (Test-Path -LiteralPath $sidecarImg -PathType Leaf) {
                    Remove-Item -LiteralPath $sidecarImg -Force
                    $deletedImageCount++
                    Write-Host "  🗑 删除外部图片: $($file.BaseName)$imgExt" -ForegroundColor DarkGray
                }
            }
        }
    }

    # 输出统计
    Write-Host "`n==================== 处理完成 ====================" -ForegroundColor Cyan
    Write-Host "总视频数: $($videoFiles.Count)"
    Write-Host "成功清理: $successCount" -ForegroundColor Green
    Write-Host "跳过/无需清理: $skipCount" -ForegroundColor Yellow
    Write-Host "处理失败: $failCount" -ForegroundColor Red
    if ($deleteSidecarImages) {
        Write-Host "删除外部图片: $deletedImageCount" -ForegroundColor DarkGray
    }
    Write-Host "==================================================" -ForegroundColor Cyan
}

# ====================== 主程序入口 ======================
Clear-Host
Write-Host "===== 视频缩略图广告清理工具 =====" -ForegroundColor Magenta
Write-Host "依赖: 系统需已安装 ffmpeg 并加入环境变量`n"

# 检查 ffmpeg
if (-not (Test-FFmpeg)) {
    Write-Host "错误:未检测到 ffmpeg,请先安装 ffmpeg 并添加到系统 PATH 环境变量。" -ForegroundColor Red
    Write-Host "下载地址: https://ffmpeg.org/download.html"
    Read-Host "按回车键退出"
    exit 1
}
Write-Host "✓ ffmpeg 检测正常`n" -ForegroundColor Green

# 获取文件夹路径
if ([string]::IsNullOrWhiteSpace($FolderPath)) {
    $FolderPath = Read-Host "请输入要处理的视频文件夹路径"
}

# 去除路径两端引号
$FolderPath = $FolderPath.Trim('"', "'")

# 执行处理
Invoke-VideoThumbnailCleaner -RootFolder $FolderPath

Write-Host "`n按回车键退出..."
Read-Host | Out-Null

Hosts文件管理GUI

# 检查是否以管理员身份运行
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

if (-not $isAdmin) {
    # 不是管理员:启动一个新的管理员 PowerShell 窗口,并传递当前脚本路径
    $scriptPath = $MyInvocation.MyCommand.Definition
    Start-Process powershell -ArgumentList "-NoExit", "-ExecutionPolicy Bypass", "-File `"$scriptPath`"" -Verb RunAs
    exit  # 原进程退出
}

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# 定义hosts文件路径(需要管理员权限)
$hostsPath = "$env:SystemRoot\System32\drivers\etc\hosts"

# 检查hosts文件是否存在,如果不存在则创建
if (-not (Test-Path $hostsPath)) {
    try {
        # 创建空的hosts文件
        Set-Content -Path $hostsPath -Value "# Hosts file created by Hosts Manager`n" -Encoding ASCII -Force
    }
    catch {
        [System.Windows.Forms.MessageBox]::Show("无法创建hosts文件。请以管理员身份运行此程序。", "权限错误", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        exit
    }
}

# 默认host组 - 可以随时在此添加或修改组
$defaultHostGroups = @{
    "图书发布" = @(
        "100.95.23.67 book1.cikaros.local",
        "100.95.23.67 book2.cikaros.local", 
        "100.95.23.67 book3.cikaros.local"
    )
    "Google" = @(
        "142.250.184.196 www.google.com",
        "142.250.184.196 google.com", 
        "142.250.184.196 www.google.com.hk"
    )
    "YouTube" = @(
        "142.250.184.206 www.youtube.com", 
        "142.250.184.206 youtube.com"
    )
    "Facebook" = @(
        "31.13.64.35 www.facebook.com", 
        "31.13.64.35 facebook.com"
    )
    "Twitter" = @(
        "104.244.42.1 www.twitter.com", 
        "104.244.42.1 twitter.com"
    )
    "GitHub" = @(
        "140.82.114.4 github.com", 
        "140.82.114.4 www.github.com",
        "140.82.114.3 api.github.com"
    )
    # 可以随时在这里添加新组
    "Microsoft" = @(
        "13.107.246.40 www.microsoft.com",
        "13.107.246.40 microsoft.com"
    )
}

# 创建主窗口
$form = New-Object System.Windows.Forms.Form
$form.Text = "Hosts文件管理器"
$form.Size = New-Object System.Drawing.Size(950, 750)
$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable
$form.Font = New-Object System.Drawing.Font("Microsoft YaHei", 9)
$form.MinimumSize = New-Object System.Drawing.Size(800, 600)

# 创建主布局容器
$mainTable = New-Object System.Windows.Forms.TableLayoutPanel
$mainTable.Dock = [System.Windows.Forms.DockStyle]::Fill
$mainTable.ColumnCount = 1
$mainTable.RowCount = 4
$mainTable.Padding = New-Object System.Windows.Forms.Padding(10)
$mainTable.CellBorderStyle = [System.Windows.Forms.TableLayoutPanelCellBorderStyle]::None

# 1. 列表视图 (占70%高度)
$hostsList = New-Object System.Windows.Forms.ListView
$hostsList.View = [System.Windows.Forms.View]::Details
$hostsList.FullRowSelect = $true
$hostsList.GridLines = $true
$hostsList.Dock = [System.Windows.Forms.DockStyle]::Fill
$hostsList.MultiSelect = $true
$hostsList.HeaderStyle = [System.Windows.Forms.ColumnHeaderStyle]::Nonclickable
# $hostsList.DoubleBuffered = $true  # 减少闪烁

# 添加列 - 关键修正:确保列定义正确
$ipColumn = New-Object System.Windows.Forms.ColumnHeader
$ipColumn.Text = "IP地址"
$ipColumn.Width = 200
$ipColumn.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Left

$domainColumn = New-Object System.Windows.Forms.ColumnHeader
$domainColumn.Text = "域名"
$domainColumn.Width = 650
$domainColumn.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Left

$hostsList.Columns.Add($ipColumn) | Out-Null
$hostsList.Columns.Add($domainColumn) | Out-Null

# 2. 操作按钮行 (占10%高度)
$buttonPanel1 = New-Object System.Windows.Forms.FlowLayoutPanel
$buttonPanel1.Dock = [System.Windows.Forms.DockStyle]::Fill
$buttonPanel1.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight
$buttonPanel1.WrapContents = $false
$buttonPanel1.Padding = New-Object System.Windows.Forms.Padding(0, 5, 0, 5)
$buttonPanel1.AutoSize = $true

$addButton = New-Object System.Windows.Forms.Button
$addButton.Text = "添加新记录"
$addButton.Size = New-Object System.Drawing.Size(120, 35)
$addButton.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$buttonPanel1.Controls.Add($addButton)

$removeButton = New-Object System.Windows.Forms.Button
$removeButton.Text = "删除选中记录"
$removeButton.Size = New-Object System.Drawing.Size(120, 35)
$removeButton.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$buttonPanel1.Controls.Add($removeButton)

# 3. 默认组区域 (占10%高度)
$groupPanel = New-Object System.Windows.Forms.FlowLayoutPanel
$groupPanel.Dock = [System.Windows.Forms.DockStyle]::Fill
$groupPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight
$groupPanel.WrapContents = $false
$groupPanel.Padding = New-Object System.Windows.Forms.Padding(0, 5, 0, 5)
$groupPanel.AutoSize = $true

$defaultGroupLabel = New-Object System.Windows.Forms.Label
$defaultGroupLabel.Text = "快速应用默认组:"
$defaultGroupLabel.Size = New-Object System.Drawing.Size(120, 25)
$defaultGroupLabel.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$defaultGroupLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$groupPanel.Controls.Add($defaultGroupLabel)

$defaultGroupCombo = New-Object System.Windows.Forms.ComboBox
$defaultGroupCombo.Size = New-Object System.Drawing.Size(150, 25)
$defaultGroupCombo.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$defaultGroupCombo.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList

# 关键修改:自动从$defaultHostGroups获取组名
$groupNames = $defaultHostGroups.Keys | Sort-Object  # 按字母顺序排序
if ($groupNames.Count -gt 0) {
    $defaultGroupCombo.Items.AddRange($groupNames)
    $defaultGroupCombo.SelectedIndex = 0  # 默认选择第一个组
}
else {
    $defaultGroupCombo.Items.Add("无可用组")
    $defaultGroupCombo.SelectedIndex = 0
}
$groupPanel.Controls.Add($defaultGroupCombo)

$applyButton = New-Object System.Windows.Forms.Button
$applyButton.Text = "应用所选组"
$applyButton.Size = New-Object System.Drawing.Size(120, 35)
$applyButton.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$groupPanel.Controls.Add($applyButton)

# 4. 保存/退出按钮行 (占10%高度)
$buttonPanel2 = New-Object System.Windows.Forms.FlowLayoutPanel
$buttonPanel2.Dock = [System.Windows.Forms.DockStyle]::Fill
$buttonPanel2.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft
$buttonPanel2.WrapContents = $false
$buttonPanel2.Padding = New-Object System.Windows.Forms.Padding(0, 5, 0, 5)
$buttonPanel2.AutoSize = $true

$saveButton = New-Object System.Windows.Forms.Button
$saveButton.Text = "保存更改"
$saveButton.Size = New-Object System.Drawing.Size(120, 35)
$saveButton.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$buttonPanel2.Controls.Add($saveButton)

$exitButton = New-Object System.Windows.Forms.Button
$exitButton.Text = "退出"
$exitButton.Size = New-Object System.Drawing.Size(120, 35)
$exitButton.Margin = New-Object System.Windows.Forms.Padding(5, 0, 5, 0)
$buttonPanel2.Controls.Add($exitButton)

# 添加所有控件到主布局
$mainTable.Controls.Add($hostsList, 0, 0)
$mainTable.Controls.Add($buttonPanel1, 0, 1)
$mainTable.Controls.Add($groupPanel, 0, 2)
$mainTable.Controls.Add($buttonPanel2, 0, 3)

# 设置行高比例
$mainTable.RowStyles.Clear()
$mainTable.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 70)))
$mainTable.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 10)))
$mainTable.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 10)))
$mainTable.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 10)))

# 添加主布局到窗体
$form.Controls.Add($mainTable)

# 关键修正:正确加载hosts文件
function Load-HostsFile {
    $hostsList.Items.Clear()
    try {
        $content = Get-Content $hostsPath
        
        foreach ($line in $content) {
            $trimmedLine = $line.Trim()
            
            # 跳过注释行和空行
            if ($trimmedLine -eq "" -or $trimmedLine.StartsWith("#")) {
                continue
            }
            
            # 使用正则表达式分割IP和域名
            # 先尝试按制表符分割
            $tabSplit = $trimmedLine -split "`t+", 2
            if ($tabSplit.Count -ge 2) {
                $ip = $tabSplit[0].Trim()
                $domain = $tabSplit[1].Trim()
            }
            else {
                # 按多个空格分割
                $spaceSplit = $trimmedLine -split "\s+", 2
                if ($spaceSplit.Count -ge 2) {
                    $ip = $spaceSplit[0].Trim()
                    $domain = $spaceSplit[1].Trim()
                }
                else {
                    # 如果无法正确分割,使用整个行作为IP
                    $ip = $trimmedLine
                    $domain = ""
                }
            }
            
            # 只添加有效的记录
            if ($ip -ne "" -and $ip -match "^(\d{1,3}\.){3}\d{1,3}$") {
                $item = New-Object System.Windows.Forms.ListViewItem($ip)
                # 关键修正:正确添加子项作为第二列
                $item.SubItems.Add($domain) | Out-Null
                $hostsList.Items.Add($item) | Out-Null
            }
        }
        
        # 状态反馈
        if ($hostsList.Items.Count -eq 0) {
            # 添加示例项以显示格式
            $item = New-Object System.Windows.Forms.ListViewItem("127.0.0.1")
            $item.SubItems.Add("localhost") | Out-Null
            $item.ForeColor = [System.Drawing.Color]::Gray
            $hostsList.Items.Add($item) | Out-Null
            
            $item = New-Object System.Windows.Forms.ListViewItem("127.0.0.1")
            $item.SubItems.Add("example.com") | Out-Null
            $item.ForeColor = [System.Drawing.Color]::Gray
            $hostsList.Items.Add($item) | Out-Null
        }
    }
    catch {
        [System.Windows.Forms.MessageBox]::Show("无法读取hosts文件: $_`n请确保以管理员身份运行此程序。", "错误", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
    }
}

# 应用默认组
$applyButton.Add_Click({
    $selectedGroup = $defaultGroupCombo.SelectedItem
    $groupEntries = $defaultHostGroups[$selectedGroup]
    
    $added = 0
    $existing = 0
    
    foreach ($entry in $groupEntries) {
        $parts = $entry -split "\s+", 2
        if ($parts.Count -ge 2) {
            $ip = $parts[0].Trim()
            $domain = $parts[1].Trim()
            
            # 检查是否已存在
            $exists = $false
            foreach ($item in $hostsList.Items) {
                # 关键修正:正确检查子项
                if ($item.Text -eq $ip -and $item.SubItems[1].Text -eq $domain) {
                    $exists = $true
                    break
                }
            }
            
            if (-not $exists) {
                $item = New-Object System.Windows.Forms.ListViewItem($ip)
                $item.SubItems.Add($domain) | Out-Null
                $hostsList.Items.Add($item) | Out-Null
                $added++
            }
            else {
                $existing++
            }
        }
    }
    
    if ($added -gt 0 -or $existing -gt 0) {
        $message = ""
        if ($added -gt 0) {
            $message += "$added 条新记录已添加。`n"
        }
        if ($existing -gt 0) {
            $message += "$existing 条记录已存在,未重复添加。"
        }
        [System.Windows.Forms.MessageBox]::Show($message, "操作完成", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    }
    else {
        [System.Windows.Forms.MessageBox]::Show("未添加任何新记录。", "提示", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    }
})

# 添加新记录
$addButton.Add_Click({
    $newRecordForm = New-Object System.Windows.Forms.Form
    $newRecordForm.Text = "添加新记录"
    $newRecordForm.Size = New-Object System.Drawing.Size(500, 200)
    $newRecordForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
    $newRecordForm.Font = $form.Font
    $newRecordForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
    $newRecordForm.MaximizeBox = $false
    $newRecordForm.MinimizeBox = $false
    $newRecordForm.ShowIcon = $false
    
    $tableLayoutPanel = New-Object System.Windows.Forms.TableLayoutPanel
    $tableLayoutPanel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.ColumnCount = 2
    $tableLayoutPanel.RowCount = 3
    $tableLayoutPanel.Padding = New-Object System.Windows.Forms.Padding(20, 15, 20, 15)
    $tableLayoutPanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 80)))
    $tableLayoutPanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 30)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 30)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 40)))
    
    $ipLabel = New-Object System.Windows.Forms.Label
    $ipLabel.Text = "IP地址:"
    $ipLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
    $ipLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.Controls.Add($ipLabel, 0, 0)
    
    $ipBox = New-Object System.Windows.Forms.TextBox
    $ipBox.Dock = [System.Windows.Forms.DockStyle]::Fill
    $ipBox.Text = "127.0.0.1"
    $tableLayoutPanel.Controls.Add($ipBox, 1, 0)
    
    $domainLabel = New-Object System.Windows.Forms.Label
    $domainLabel.Text = "域名:"
    $domainLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
    $domainLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.Controls.Add($domainLabel, 0, 1)
    
    $domainBox = New-Object System.Windows.Forms.TextBox
    $domainBox.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.Controls.Add($domainBox, 1, 1)
    
    $buttonFlow = New-Object System.Windows.Forms.FlowLayoutPanel
    $buttonFlow.Dock = [System.Windows.Forms.DockStyle]::Fill
    $buttonFlow.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft
    
    $okButton = New-Object System.Windows.Forms.Button
    $okButton.Text = "确定"
    $okButton.Size = New-Object System.Drawing.Size(80, 30)
    $okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
    $okButton.Enabled = $false
    
    $cancelButton = New-Object System.Windows.Forms.Button
    $cancelButton.Text = "取消"
    $cancelButton.Size = New-Object System.Drawing.Size(80, 30)
    $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    
    # 验证输入
    $validateInput = {
        $ip = $ipBox.Text.Trim()
        $domain = $domainBox.Text.Trim()
        $okButton.Enabled = ($ip -ne "" -and $domain -ne "" -and $ip -match "^(\d{1,3}\.){3}\d{1,3}$")
    }
    
    $ipBox.Add_TextChanged($validateInput)
    $domainBox.Add_TextChanged($validateInput)
    
    $buttonFlow.Controls.Add($okButton)
    $buttonFlow.Controls.Add($cancelButton)
    $tableLayoutPanel.Controls.Add($buttonFlow, 1, 2)
    
    $newRecordForm.Controls.Add($tableLayoutPanel)
    $newRecordForm.AcceptButton = $okButton
    $newRecordForm.CancelButton = $cancelButton
    
    $dialogResult = $newRecordForm.ShowDialog()
    
    if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK) {
        $ip = $ipBox.Text.Trim()
        $domain = $domainBox.Text.Trim()
        
        # 检查是否已存在
        $exists = $false
        foreach ($item in $hostsList.Items) {
            if ($item.Text -eq $ip -and $item.SubItems[1].Text -eq $domain) {
                $exists = $true
                break
            }
        }
        
        if (-not $exists) {
            $item = New-Object System.Windows.Forms.ListViewItem($ip)
            $item.SubItems.Add($domain) | Out-Null
            $hostsList.Items.Add($item) | Out-Null
        }
        else {
            [System.Windows.Forms.MessageBox]::Show("该记录已存在", "重复记录", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
        }
    }
})

# 删除选中记录
$removeButton.Add_Click({
    if ($hostsList.SelectedItems.Count -eq 0) {
        [System.Windows.Forms.MessageBox]::Show("请先选择要删除的记录。", "提示", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        return
    }
    
    $count = $hostsList.SelectedItems.Count
    $result = [System.Windows.Forms.MessageBox]::Show("确定要删除选中的 $count 条记录吗?", "确认删除", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Warning)
    if ($result -eq [System.Windows.Forms.DialogResult]::Yes) {
        # 创建副本避免在迭代时修改集合
        $itemsToRemove = New-Object System.Collections.ArrayList
        foreach ($item in $hostsList.SelectedItems) {
            $null = $itemsToRemove.Add($item)
        }
        
        foreach ($item in $itemsToRemove) {
            $hostsList.Items.Remove($item)
        }
        
        [System.Windows.Forms.MessageBox]::Show("$($itemsToRemove.Count) 条记录已删除。", "删除成功", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    }
})

# 保存更改
$saveButton.Add_Click({
    try {
        # 创建备份
        $backupPath = "$hostsPath.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
        Copy-Item $hostsPath $backupPath -Force
        
        # 准备新内容
        $newContent = @()
        $newContent += "# Hosts file managed by Hosts Manager"
        $newContent += "# Last updated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
        $newContent += ""
        
        # 添加所有记录
        foreach ($item in $hostsList.Items) {
            # 跳过示例项(灰色文本)
            if ($item.ForeColor -eq [System.Drawing.Color]::Gray) {
                continue
            }
            
            # 关键修正:正确获取子项
            $ip = $item.Text
            $domain = $item.SubItems[1].Text
            
            if ($ip -match "^(\d{1,3}\.){3}\d{1,3}$" -and $domain -ne "") {
                $newContent += "$ip`t$domain"
            }
        }
        
        # 保存文件
        $newContent | Set-Content $hostsPath -Encoding ASCII -Force
        
        [System.Windows.Forms.MessageBox]::Show("hosts文件已成功保存!`n备份已创建: $backupPath", "保存成功", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    }
    catch {
        [System.Windows.Forms.MessageBox]::Show("保存hosts文件时出错: $_`n请确保以管理员身份运行此程序。", "错误", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        
        # 尝试恢复备份
        if (Test-Path $backupPath) {
            try {
                Copy-Item $backupPath $hostsPath -Force
                [System.Windows.Forms.MessageBox]::Show("已自动恢复备份文件。", "恢复成功", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
            }
            catch {
                [System.Windows.Forms.MessageBox]::Show("无法恢复备份: $_", "恢复失败", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            }
        }
    }
})

# 退出应用
$exitButton.Add_Click({
    $dialogResult = [System.Windows.Forms.MessageBox]::Show("是否要退出程序?", "确认退出", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Question)
    if ($dialogResult -eq [System.Windows.Forms.DialogResult]::Yes) {
        $form.Close()
    }
})

# 添加双击编辑功能
$hostsList.Add_MouseDoubleClick({
    param($sender, $e)
    
    if ($hostsList.SelectedItems.Count -eq 0) {
        return
    }
    
    $selectedItem = $hostsList.SelectedItems[0]
    $originalIp = $selectedItem.Text
    $originalDomain = $selectedItem.SubItems[1].Text
    
    # 跳过示例项(灰色文本)
    if ($selectedItem.ForeColor -eq [System.Drawing.Color]::Gray) {
        [System.Windows.Forms.MessageBox]::Show("这是示例记录,不能编辑。", "提示", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        return
    }
    
    $editForm = New-Object System.Windows.Forms.Form
    $editForm.Text = "编辑记录"
    $editForm.Size = New-Object System.Drawing.Size(500, 200)
    $editForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
    $editForm.Font = $form.Font
    $editForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
    $editForm.MaximizeBox = $false
    $editForm.MinimizeBox = $false
    $editForm.ShowIcon = $false
    
    $tableLayoutPanel = New-Object System.Windows.Forms.TableLayoutPanel
    $tableLayoutPanel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.ColumnCount = 2
    $tableLayoutPanel.RowCount = 3
    $tableLayoutPanel.Padding = New-Object System.Windows.Forms.Padding(20, 15, 20, 15)
    $tableLayoutPanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 80)))
    $tableLayoutPanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 30)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 30)))
    $tableLayoutPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 40)))
    
    $ipLabel = New-Object System.Windows.Forms.Label
    $ipLabel.Text = "IP地址:"
    $ipLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
    $ipLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.Controls.Add($ipLabel, 0, 0)
    
    $ipBox = New-Object System.Windows.Forms.TextBox
    $ipBox.Dock = [System.Windows.Forms.DockStyle]::Fill
    $ipBox.Text = $originalIp
    $tableLayoutPanel.Controls.Add($ipBox, 1, 0)
    
    $domainLabel = New-Object System.Windows.Forms.Label
    $domainLabel.Text = "域名:"
    $domainLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
    $domainLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tableLayoutPanel.Controls.Add($domainLabel, 0, 1)
    
    $domainBox = New-Object System.Windows.Forms.TextBox
    $domainBox.Dock = [System.Windows.Forms.DockStyle]::Fill
    $domainBox.Text = $originalDomain
    $tableLayoutPanel.Controls.Add($domainBox, 1, 1)
    
    $buttonFlow = New-Object System.Windows.Forms.FlowLayoutPanel
    $buttonFlow.Dock = [System.Windows.Forms.DockStyle]::Fill
    $buttonFlow.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft
    
    $okButton = New-Object System.Windows.Forms.Button
    $okButton.Text = "确定"
    $okButton.Size = New-Object System.Drawing.Size(80, 30)
    $okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
    $okButton.Enabled = $false
    
    $cancelButton = New-Object System.Windows.Forms.Button
    $cancelButton.Text = "取消"
    $cancelButton.Size = New-Object System.Drawing.Size(80, 30)
    $cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    
    # 验证输入
    $validateInput = {
        $ip = $ipBox.Text.Trim()
        $domain = $domainBox.Text.Trim()
        $okButton.Enabled = ($ip -ne "" -and $domain -ne "" -and $ip -match "^(\d{1,3}\.){3}\d{1,3}$")
    }
    
    $ipBox.Add_TextChanged($validateInput)
    $domainBox.Add_TextChanged($validateInput)
    
    $buttonFlow.Controls.Add($okButton)
    $buttonFlow.Controls.Add($cancelButton)
    $tableLayoutPanel.Controls.Add($buttonFlow, 1, 2)
    
    $editForm.Controls.Add($tableLayoutPanel)
    $editForm.AcceptButton = $okButton
    $editForm.CancelButton = $cancelButton
    
    $dialogResult = $editForm.ShowDialog()
    
    if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK) {
        $ip = $ipBox.Text.Trim()
        $domain = $domainBox.Text.Trim()
        
        # 检查是否已存在(排除当前记录)
        $exists = $false
        foreach ($item in $hostsList.Items) {
            if ($item -ne $selectedItem -and $item.Text -eq $ip -and $item.SubItems[1].Text -eq $domain) {
                $exists = $true
                break
            }
        }
        
        if (-not $exists) {
            $selectedItem.Text = $ip
            $selectedItem.SubItems[1].Text = $domain
        }
        else {
            [System.Windows.Forms.MessageBox]::Show("该记录已存在", "重复记录", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
        }
    }
})

# 初始加载hosts文件
Load-HostsFile

# 显示关于框
$form.Add_Shown({
    [System.Windows.Forms.MessageBox]::Show("欢迎使用Hosts文件管理器!`n`n提示:`n• 双击记录可编辑`n• 请以管理员身份运行以保存更改`n• 每次保存会自动创建备份", "使用提示", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
    $form.Activate()
})

# 显示窗口
[void]$form.ShowDialog()


常用Powershell脚本
https://blog.cikaros.cn/archives/chang-yong-powershelljiao-ben
作者
Cikaros
发布于
2026年06月14日
许可协议