一键运行根目录下所有bat,ps1启动项
一键运行根目录下所有bat,ps1启动项

一键运行根目录下所有bat,ps1启动项

批量启动.ps1 | BatchRunner.ps1

<#
.SYNOPSIS
    批处理文件运行器服务管理脚本
.DESCRIPTION
    用于启动当前目录下的所有bat/ps1文件和快捷方式,并提供服务注册和卸载功能
.PARAMETER Action
    操作类型: install (安装服务), uninstall (卸载服务), run (直接运行), start (启动服务), stop (停止服务)
#>

param(
    [Parameter(Mandatory=$false)]
    [ValidateSet('install', 'uninstall', 'run', 'start', 'stop', 'service')]
    [string]$Action = 'run'
)

# 设置控制台编码为UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

# 配置变量
$ServiceName = "BatchRunnerService"
$ServiceDisplayName = "Batch Files Runner Service"
$ServiceDescription = "Auto run all batch files and PowerShell scripts in specified directory"
$CurrentPath = $PSScriptRoot
if ([string]::IsNullOrEmpty($CurrentPath)) {
    $CurrentPath = Get-Location
}

$LogFile = Join-Path $CurrentPath "service.log"

#region 函数定义区域

# 函数: 写日志
function Write-Log {
    param([string]$Message)
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "$timestamp - $Message"
    try {
        # 使用UTF8编码写入日志
        Add-Content -Path $LogFile -Value $logMessage -Encoding UTF8 -ErrorAction SilentlyContinue
    }
    catch {
        # 忽略日志写入错误
    }
    Write-Host $logMessage
}

# 函数: 解析快捷方式文件
function Get-ShortcutTarget {
    param([string]$ShortcutPath)

    try {
        $shell = New-Object -ComObject WScript.Shell
        $shortcut = $shell.CreateShortcut($ShortcutPath)
        $targetPath = $shortcut.TargetPath
        $arguments = $shortcut.Arguments
        $workingDirectory = $shortcut.WorkingDirectory

        # 创建返回对象
        $result = @{
            TargetPath = $targetPath
            Arguments = $arguments
            WorkingDirectory = $workingDirectory
        }

        # 释放COM对象
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shortcut) | Out-Null
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
        [System.GC]::Collect()
        [System.GC]::WaitForPendingFinalizers()

        return $result
    }
    catch {
        Write-Log "解析快捷方式失败: $ShortcutPath - $_"
        return $null
    }
}

# 函数: 判断文件类型
function Get-ScriptType {
    param([string]$FilePath)

    if ($FilePath -match '\.bat$') {
        return 'BAT'
    }
    elseif ($FilePath -match '\.ps1$') {
        return 'PS1'
    }
    else {
        return 'UNKNOWN'
    }
}

# 函数: 运行所有脚本文件和快捷方式
function Run-AllBatchFiles {
    Write-Log "开始扫描目录: $CurrentPath"

    # 获取当前目录下所有bat文件
    $batFiles = Get-ChildItem -Path $CurrentPath -Filter "*.bat" -File

    # 获取当前目录下所有ps1文件(排除当前脚本本身)
    $ps1Files = Get-ChildItem -Path $CurrentPath -Filter "*.ps1" -File | Where-Object {
        $_.FullName -ne $MyInvocation.PSCommandPath -and
        $_.Name -ne 'BatchRunner.ps1' -and
        $_.Name -ne '批量启动.ps1'
    }

    # 获取当前目录下所有lnk文件(快捷方式)
    $lnkFiles = Get-ChildItem -Path $CurrentPath -Filter "*.lnk" -File

    Write-Log "扫描到 $($batFiles.Count) 个bat文件, $($ps1Files.Count) 个ps1文件, $($lnkFiles.Count) 个lnk文件"

    # 筛选出指向bat/ps1文件的快捷方式
    $scriptLnkFiles = @()
    foreach ($lnk in $lnkFiles) {
        Write-Log "正在解析快捷方式: $($lnk.Name)"
        $shortcutInfo = Get-ShortcutTarget -ShortcutPath $lnk.FullName

        if ($shortcutInfo -and -not [string]::IsNullOrEmpty($shortcutInfo.TargetPath)) {
            $scriptType = Get-ScriptType -FilePath $shortcutInfo.TargetPath

            Write-Log "  目标路径: $($shortcutInfo.TargetPath)"

            # 检查是否是bat或ps1文件
            if ($scriptType -eq 'BAT' -or $scriptType -eq 'PS1') {
                $scriptLnkFiles += @{
                    File = $lnk
                    Info = $shortcutInfo
                    Type = $scriptType
                }
                Write-Log "  识别为 $scriptType 快捷方式"
            }
            else {
                Write-Log "  不是bat/ps1文件,跳过"
            }
        }
        else {
            Write-Log "  无法解析快捷方式"
        }
    }

    $totalCount = $batFiles.Count + $ps1Files.Count + $scriptLnkFiles.Count

    if ($totalCount -eq 0) {
        Write-Log "未找到任何bat/ps1文件或快捷方式"
        return
    }

    Write-Log "总计: $($batFiles.Count) 个bat + $($ps1Files.Count) 个ps1 + $($scriptLnkFiles.Count) 个快捷方式 = $totalCount 个"
    Write-Log "========================================="

    # 执行每个bat文件
    foreach ($batFile in $batFiles) {
        Write-Log "正在启动bat文件: $($batFile.Name)"
        try {
            $process = Start-Process -FilePath $batFile.FullName `
                -WorkingDirectory $CurrentPath `
                -WindowStyle Normal `
                -PassThru
            Write-Log "  成功启动 (PID: $($process.Id))"
        }
        catch {
            Write-Log "  启动失败: $_"
        }
    }

    # 执行每个ps1文件
    foreach ($ps1File in $ps1Files) {
        Write-Log "正在启动ps1文件: $($ps1File.Name)"
        try {
            $process = Start-Process -FilePath "powershell.exe" `
                -ArgumentList "-ExecutionPolicy Bypass -NoProfile -File `"$($ps1File.FullName)`"" `
                -WorkingDirectory $CurrentPath `
                -WindowStyle Normal `
                -PassThru
            Write-Log "  成功启动 (PID: $($process.Id))"
        }
        catch {
            Write-Log "  启动失败: $_"
        }
    }

    # 执行每个快捷方式
    foreach ($lnkItem in $scriptLnkFiles) {
        $lnkFile = $lnkItem.File
        $shortcutInfo = $lnkItem.Info
        $scriptType = $lnkItem.Type

        Write-Log "正在启动快捷方式: $($lnkFile.Name) [$scriptType]"
        Write-Log "  目标: $($shortcutInfo.TargetPath)"

        try {
            # 检查目标文件是否存在
            if (-not (Test-Path $shortcutInfo.TargetPath)) {
                Write-Log "  错误: 目标文件不存在"
                continue
            }

            # 确定工作目录
            $workDir = $shortcutInfo.WorkingDirectory
            if ([string]::IsNullOrEmpty($workDir) -or -not (Test-Path $workDir)) {
                $workDir = Split-Path $shortcutInfo.TargetPath -Parent
                Write-Log "  使用目标文件目录作为工作目录: $workDir"
            }
            else {
                Write-Log "  工作目录: $workDir"
            }

            # 根据类型启动
            if ($scriptType -eq 'BAT') {
                # 启动bat文件
                $startParams = @{
                    FilePath = $shortcutInfo.TargetPath
                    WorkingDirectory = $workDir
                    WindowStyle = 'Normal'
                    PassThru = $true
                }

                # 如果有参数,添加参数
                if (-not [string]::IsNullOrEmpty($shortcutInfo.Arguments)) {
                    $startParams.ArgumentList = $shortcutInfo.Arguments
                    Write-Log "  参数: $($shortcutInfo.Arguments)"
                }

                $process = Start-Process @startParams
                Write-Log "  成功启动 (PID: $($process.Id))"
            }
            elseif ($scriptType -eq 'PS1') {
                # 启动ps1文件
                $psArgs = "-ExecutionPolicy Bypass -NoProfile -File `"$($shortcutInfo.TargetPath)`""

                # 如果有参数,添加到命令行
                if (-not [string]::IsNullOrEmpty($shortcutInfo.Arguments)) {
                    $psArgs += " $($shortcutInfo.Arguments)"
                    Write-Log "  参数: $($shortcutInfo.Arguments)"
                }

                $process = Start-Process -FilePath "powershell.exe" `
                    -ArgumentList $psArgs `
                    -WorkingDirectory $workDir `
                    -WindowStyle Normal `
                    -PassThru
                Write-Log "  成功启动 (PID: $($process.Id))"
            }
        }
        catch {
            Write-Log "  启动失败: $_"
        }
    }

    Write-Log "========================================="
    Write-Log "所有脚本文件已启动完成"
}

# 函数: 服务模式运行(持续运行)
function Run-AsService {
    try {
        Write-Log "=== 服务启动 ==="
        Write-Log "PowerShell 版本: $($PSVersionTable.PSVersion)"
        Write-Log "当前用户: $env:USERNAME"
        Write-Log "工作目录: $CurrentPath"

        # 运行所有脚本文件
        Run-AllBatchFiles

        Write-Log "所有脚本已启动,服务任务完成"
    }
    catch {
        Write-Log "服务运行异常: $_"
        Write-Log "堆栈跟踪: $($_.ScriptStackTrace)"
    }
}

# 函数: 安装Windows服务(使用任务计划程序)
function Install-Service {
    Write-Host "开始安装开机自启任务..." -ForegroundColor Cyan

    # 检查管理员权限
    if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Host "错误: 需要管理员权限来安装服务" -ForegroundColor Red
        Write-Host "请以管理员身份运行PowerShell" -ForegroundColor Yellow
        exit 1
    }

    # 检查任务是否已存在
    $existingTask = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
    if ($existingTask) {
        Write-Host "任务已存在,请先卸载" -ForegroundColor Yellow
        return
    }

    # 获取当前脚本的完整路径
    $scriptPath = $MyInvocation.PSCommandPath
    if ([string]::IsNullOrEmpty($scriptPath)) {
        # 如果无法自动获取,尝试在当前目录查找
        $possibleNames = @("BatchRunner.ps1", "批量启动.ps1")
        foreach ($name in $possibleNames) {
            $testPath = Join-Path $CurrentPath $name
            if (Test-Path $testPath) {
                $scriptPath = $testPath
                break
            }
        }
    }

    if ([string]::IsNullOrEmpty($scriptPath) -or -not (Test-Path $scriptPath)) {
        Write-Host "错误: 无法找到脚本文件" -ForegroundColor Red
        Write-Host "请确保脚本文件存在于: $CurrentPath" -ForegroundColor Yellow
        return
    }

    Write-Host "使用任务计划程序安装..." -ForegroundColor Cyan

    try {
        # 创建任务动作
        $action = New-ScheduledTaskAction -Execute "PowerShell.exe" `
            -Argument "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$scriptPath`" -Action service"

        # 创建任务触发器(系统启动时,延迟30秒)
        $trigger = New-ScheduledTaskTrigger -AtStartup
        $trigger.Delay = 'PT30S'  # 延迟30秒启动

        # 创建任务主体(使用当前用户账户运行)
        $principal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive -RunLevel Highest

        # 创建任务设置
        $settings = New-ScheduledTaskSettingsSet `
            -AllowStartIfOnBatteries `
            -DontStopIfGoingOnBatteries `
            -StartWhenAvailable `
            -ExecutionTimeLimit (New-TimeSpan -Minutes 10)

        # 注册任务
        Register-ScheduledTask `
            -TaskName $ServiceName `
            -Action $action `
            -Trigger $trigger `
            -Principal $principal `
            -Settings $settings `
            -Description $ServiceDescription | Out-Null

        Write-Host "`n任务创建成功!" -ForegroundColor Green
        Write-Host "  任务名称: $ServiceName" -ForegroundColor White
        Write-Host "  启动方式: 系统启动时(延迟30秒)" -ForegroundColor White
        Write-Host "  运行账户: $env:USERNAME" -ForegroundColor White
        Write-Host "  脚本路径: $scriptPath" -ForegroundColor White
        Write-Host "  工作目录: $CurrentPath" -ForegroundColor White
        Write-Host "  日志文件: $LogFile" -ForegroundColor White

        $startNow = Read-Host "`n是否立即运行任务测试? (Y/N)"
        if ($startNow -eq 'Y' -or $startNow -eq 'y') {
            Start-ScheduledTask -TaskName $ServiceName
            Write-Host "任务已启动" -ForegroundColor Green
            Start-Sleep -Seconds 2
            Write-Host "查看日志: Get-Content '$LogFile' -Tail 20" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "创建任务失败: $_" -ForegroundColor Red
    }
}

# 函数: 卸载服务
function Uninstall-Service {
    Write-Host "开始卸载服务..." -ForegroundColor Cyan

    # 检查管理员权限
    if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Host "错误: 需要管理员权限来卸载服务" -ForegroundColor Red
        Write-Host "请以管理员身份运行PowerShell" -ForegroundColor Yellow
        exit 1
    }

    $uninstalled = $false

    # 尝试删除任务计划
    $task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
    if ($task) {
        Write-Host "停止并删除任务计划..." -ForegroundColor Yellow
        try {
            Stop-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
            Start-Sleep -Seconds 1
        }
        catch {
            # 忽略停止错误
        }
        Unregister-ScheduledTask -TaskName $ServiceName -Confirm:$false
        Write-Host "任务已删除" -ForegroundColor Green
        $uninstalled = $true
    }

    if ($uninstalled) {
        Write-Host "`n卸载完成!" -ForegroundColor Green
    }
    else {
        Write-Host "未找到已安装的任务" -ForegroundColor Yellow
    }
}

# 函数: 启动服务
function Start-ServiceManual {
    if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Host "错误: 需要管理员权限" -ForegroundColor Red
        exit 1
    }

    # 检查任务是否存在
    $task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
    if ($task) {
        Write-Host "启动任务: $ServiceName" -ForegroundColor Cyan
        Start-ScheduledTask -TaskName $ServiceName
        Write-Host "任务已启动" -ForegroundColor Green
        Start-Sleep -Seconds 2
        Write-Host "查看日志: Get-Content '$LogFile' -Tail 20" -ForegroundColor Yellow
    }
    else {
        Write-Host "未找到已安装的任务,请先安装" -ForegroundColor Yellow
    }
}

# 函数: 停止服务
function Stop-ServiceManual {
    if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        Write-Host "错误: 需要管理员权限" -ForegroundColor Red
        exit 1
    }

    # 检查任务是否存在
    $task = Get-ScheduledTask -TaskName $ServiceName -ErrorAction SilentlyContinue
    if ($task) {
        Write-Host "停止任务: $ServiceName" -ForegroundColor Cyan
        Stop-ScheduledTask -TaskName $ServiceName
        Write-Host "任务已停止" -ForegroundColor Green
    }
    else {
        Write-Host "未找到已安装的任务" -ForegroundColor Yellow
    }
}

#endregion

# 主程序
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "  批处理文件批量启动工具" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "工作目录: $CurrentPath" -ForegroundColor White
Write-Host ""

switch ($Action.ToLower()) {
    'install' {
        Install-Service
    }
    'uninstall' {
        Uninstall-Service
    }
    'start' {
        Start-ServiceManual
    }
    'stop' {
        Stop-ServiceManual
    }
    'service' {
        # 这是服务模式,由任务计划调用
        Run-AsService
    }
    'run' {
        Run-AllBatchFiles
    }
    default {
        Write-Host "使用方法:" -ForegroundColor Yellow
        Write-Host "  .\BatchRunner.ps1                    # 直接运行所有脚本" -ForegroundColor White
        Write-Host "  .\BatchRunner.ps1 -Action install    # 安装为开机自启" -ForegroundColor White
        Write-Host "  .\BatchRunner.ps1 -Action uninstall  # 卸载开机自启" -ForegroundColor White
        Write-Host "  .\BatchRunner.ps1 -Action start      # 手动触发任务" -ForegroundColor White
        Write-Host "  .\BatchRunner.ps1 -Action stop       # 停止运行中的任务" -ForegroundColor White
        Write-Host ""
        Write-Host "支持的文件类型:" -ForegroundColor Yellow
        Write-Host "  - *.bat         # 批处理文件" -ForegroundColor White
        Write-Host "  - *.ps1         # PowerShell脚本" -ForegroundColor White
        Write-Host "  - *.lnk         # 指向bat/ps1的快捷方式" -ForegroundColor White
        Write-Host ""
        Write-Host "查看日志:" -ForegroundColor Yellow
        Write-Host "  Get-Content .\service.log -Tail 20" -ForegroundColor White
    }
}