跳转至

集合

环境变量

  • 直接[Environment]::SetEnvironmentVariable(name,value,scope) 会超级慢,因为每次都进行广播通知(几乎没什么程序会处理这个),因此一般情况直接设置注册表即可
function main() {
    $envs = @{
        ANTHROPIC_BASE_URL             = "https://api.deepseek.com/anthropic"
        ANTHROPIC_AUTH_TOKEN           = "sk-xxxx"
        ANTHROPIC_MODEL                = "deepseek-v4-flash[1m]"
        ANTHROPIC_DEFAULT_OPUS_MODEL   = "deepseek-v4-pro[1m]"
        ANTHROPIC_DEFAULT_SONNET_MODEL = "deepseek-v4-pro[1m]"
        ANTHROPIC_DEFAULT_HAIKU_MODEL  = "deepseek-v4-flash"
        CLAUDE_CODE_SUBAGENT_MODEL     = "deepseek-v4-flash"
        CLAUDE_CODE_EFFORT_LEVEL       = "max"
    }

    foreach ($name in $envs.Keys) {
        $value = $envs[$name]
        Set-EnvVar -Name $name -Value $value
        Write-Host "Set $name = $value"
    }
    # Send-EnvChangedBroadcast
}

function Set-EnvVar() {
    param (
        [string]$name,
        [string]$value,
        [string]$target = "User"  # 可选值: "User" 或 "Machine"
    )
    $path = if ($target -eq "Machine") {
        "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
    }
    else {
        "HKCU:\Environment"
    }
    Set-Item -Path "env:$name" -Value $value
    Set-ItemProperty -Path $path -Name $name -Value $value
}

function Remove-EnvVar() {
    param (
        [string]$name,
        [string]$target = "User"  # 可选值: "User" 或 "Machine"
    )
    $path = if ($target -eq "Machine") {
        "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
    }
    else {
        "HKCU:\Environment"
    }
    Remove-Item -Path "env:$name"
    Remove-ItemProperty -Path $path -Name $name -ErrorAction SilentlyContinue
}
function Send-EnvChangedBroadcast {
    $code = @"
using System;
using System.Runtime.InteropServices;
public class WinEnv {
    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, UIntPtr wParam,
        string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
}
"@
    Add-Type -TypeDefinition $code -ErrorAction SilentlyContinue
    $result = [UIntPtr]::Zero
    [WinEnv]::SendMessageTimeout(
        [IntPtr]0xFFFF,  # HWND_BROADCAST
        0x001A,          # WM_SETTINGCHANGE
        [UIntPtr]::Zero,
        "Environment",
        0x0002,          # SMTO_ABORTIFHUNG
        1000,
        [ref]$result
    ) | Out-Null
}



main

程序管理

  • 用pipe的方式等待优雅关闭,超时则强制关闭
## ── 配置 ──────────────────────────────────────────────
$exePath = ".\xxx.exe"
$exeName = (Get-Item $exePath).BaseName
$logRootDir = ".\logs\std"  # 日志根目录
## ──────────────────────────────────────────────────────

function Start-Serv {
    # 检查是否已在运行
    $existing = Get-Process -Name $exeName -ErrorAction SilentlyContinue
    if ($existing) {
        Write-Host "⚠ 程序已在运行中(PID: $($existing.Id)),跳过启动"
        return
    }

    # 2. 自动创建日志目录(不存在则创建,避免报错)
    if (-not (Test-Path -Path $logRootDir)) {
        New-Item -Path $logRootDir -ItemType Directory -Force | Out-Null
        Write-Host "日志目录已创建:$logRootDir"
    }

    # 3. 校验可执行文件是否存在(避免启动失败)
    if (-not (Test-Path -Path $exePath -PathType Leaf)) {
        Write-Error "错误:可执行文件不存在 → $exePath"
        exit 1
    }

    # # 扩展:自动清理7天前的日志(可选)
    # $maxLogAge = 7  # 保留7天内的日志
    # Get-ChildItem -Path $logRootDir -Filter "*.log" | Where-Object {
    #     $_.LastWriteTime -lt (Get-Date).AddDays(-$maxLogAge)
    # } | Remove-Item -Force -ErrorAction SilentlyContinue
    # Write-Host "已清理 $maxLogAge 天前的旧日志"

    # 4. 启动程序(后台运行,重定向日志)
    $timestamp = Get-Date -Format "yyyy-MM-dd"  # 生成时间戳(精确到秒)
    $stdoutLog = Join-Path -Path $logRootDir -ChildPath "${timestamp}_output.log"
    $stderrLog = Join-Path -Path $logRootDir -ChildPath "${timestamp}_error.log"
    try {
        Start-Process -FilePath $exePath `
            -RedirectStandardOutput $stdoutLog `
            -RedirectStandardError $stderrLog `
            -WindowStyle Hidden `
            -PassThru | Out-Null  # 返回进程对象但不输出,便于后续扩展

        Write-Host "✅ 程序已后台启动"
        Write-Host "标准输出日志:$stdoutLog"
        Write-Host "错误输出日志:$stderrLog"
    }
    catch {
        Write-Error "程序启动失败:$_"
        exit 1
    }
}

function Get-ServProc {
    Get-Process -Name $exeName -ErrorAction SilentlyContinue
}

function Stop-Serv {
    param(
        [switch]$f
    )

    $proc = Get-ServProc
    if (-not $proc) {
        Write-Host "⚠ 程序未在运行"
        return
    }

    if ($f) {
        $proc | Stop-Process -Force
        Write-Host "⏹ 已强制终止:$exeName(PID: $($proc.Id))"
        return
    }

    # 尝试优雅关闭
    # -WindowStyle必须不是Hidden, 比如Minimized
    # $proc.CloseMainWindow() | Out-Null

    $eventName = "shutdown_pid_$($proc.Id)"
    # $event = [System.Threading.EventWaitHandle]::OpenExisting($eventName)
    # $event.Set()
    # $event.Close()

    $pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", $eventName, [System.IO.Pipes.PipeDirection]::Out)
    $pipe.Connect(1000)
    $pipe.Close()


    $maxWait = 10
    for ($i = 1; $i -le $maxWait; $i++) {
        if ($proc.HasExited) { break }
        Write-Host "⏳ 第 $i/$maxWait 次等待($i 秒)..."
        Start-Sleep -Seconds 1
    }

    if (-not $proc.HasExited) {
        Write-Host "⚠ 程序未响应,强制终止..."
        $proc | Stop-Process -Force
    }

    Write-Host "⏹ 已停止:$exeName"
}

function Restart-Serv {
    param(
        [switch]$f
    )

    $proc = Get-ServProc
    if ($proc) {
        Stop-Serv -f:$f
        $proc.WaitForExit(10000) | Out-Null
    }
    Start-Serv
}

function Get-ServStatus {
    $proc = Get-Process -Name $exeName -ErrorAction SilentlyContinue
    if ($proc) {
        $elapsed = (Get-Date) - $proc.StartTime
        Write-Host "▶ 运行中"
        Write-Host "PID      : $($proc.Id)"
        Write-Host "启动时间 : $($proc.StartTime)"
        Write-Host "已运行   : $([int]$elapsed.TotalHours) 小时 $($elapsed.Minutes) 分钟"
        Write-Host "内存占用 : $([math]::Round($proc.WorkingSet64 / 1MB, 1)) MB"
    }
    else {
        Write-Host "⏹ 未运行"
    }
}

## ── 入口 ───────────────────────────────────────────────
switch ($args[0]) {
    "start" { Start-Serv }
    "stop" { Stop-Serv -f:($args -contains "-f") }
    "restart" { Restart-Serv -f:($args -contains "-f") }
    "status" { Get-ServStatus }
    default {
        Write-Host "用法: .\start.ps1 [start|stop|restart|status] [-f]"
    }
}

启动后台程序

  • 不要-NoNewWindow而是要 -WindowStyle Hidden
    • NoNewWindow会导致需要 taskkill /f才能停止进程
## 1. 定义基础配置(方便后续修改)
$exePath = ".\xxxx.exe"
$logRootDir = ".\logs\std"  # 日志根目录
$timestamp = Get-Date -Format "yyyy-MM-dd"  # 生成时间戳(精确到秒)
$stdoutLog = Join-Path -Path $logRootDir -ChildPath "${timestamp}_output.log"
$stderrLog = Join-Path -Path $logRootDir -ChildPath "${timestamp}_error.log"

## 2. 自动创建日志目录(不存在则创建,避免报错)
if (-not (Test-Path -Path $logRootDir)) {
    New-Item -Path $logRootDir -ItemType Directory -Force | Out-Null
    Write-Host "日志目录已创建:$logRootDir"
}

## 3. 校验可执行文件是否存在(避免启动失败)
if (-not (Test-Path -Path $exePath -PathType Leaf)) {
    Write-Error "错误:可执行文件不存在 → $exePath"
    exit 1
}

## # 扩展:自动清理7天前的日志(可选)
## $maxLogAge = 7  # 保留7天内的日志
## Get-ChildItem -Path $logRootDir -Filter "*.log" | Where-Object {
##     $_.LastWriteTime -lt (Get-Date).AddDays(-$maxLogAge)
## } | Remove-Item -Force -ErrorAction SilentlyContinue
## Write-Host "已清理 $maxLogAge 天前的旧日志"

## 4. 启动程序(后台运行,重定向日志)
try {
    Start-Process -FilePath $exePath `
                  -RedirectStandardOutput $stdoutLog `
                  -RedirectStandardError $stderrLog `
                  -WindowStyle Hidden `
                  -PassThru | Out-Null  # 返回进程对象但不输出,便于后续扩展

    Write-Host "start: 程序已后台启动!"
    Write-Host "标准输出日志:$stdoutLog"
    Write-Host "错误输出日志:$stderrLog"
}
catch {
    Write-Error "程序启动失败:$_"
    exit 1
}

自动提权管理员

function Invoke-Elevated {
    param(
        [string]$WorkDir,
        [string]$ScriptPath,
        [array]$Arguments
    )
    # 1. 检测当前是否为管理员权限
    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
    $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

    # 2. 非管理员则自动提权重启脚本
    if (-not $isAdmin) {
        $cmd = @"
cd "$WorkDir"; & '$ScriptPath' $($Arguments -join " ");
"@
        $escapedCmd = $cmd -replace '"', '\"'
        $arguments = @(
            "-NoExit",
            "-NoProfile",
            "-ExecutionPolicy Bypass",
            "-Command", "`"$escapedCmd`""
        ) -join " "

        # 创建管理员权限的进程启动信息
        $processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
        $processStartInfo.FileName = "powershell.exe"
        $processStartInfo.Arguments = $arguments
        $processStartInfo.Verb = "runas"  # 关键:指定以管理员身份运行
        $processStartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal
        try {
            # 启动提权进程,会弹出UAC用户账户控制弹窗
            [System.Diagnostics.Process]::Start($processStartInfo) | Out-Null
        }
        catch {
            Write-Host "❌ 提权失败!你拒绝了管理员权限请求,脚本无法执行。" -ForegroundColor Red
            Pause
            exit 1
        }
        # 原非管理员进程退出,仅保留提权后的新进程
        exit 0
    }
}

实时关闭windows实时保护

$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Host "⚠️ 请以管理员身份运行此脚本!" -ForegroundColor Red
    Pause
    exit
}

Write-Host "🔁 开始每 5 秒关闭一次 Windows Defender 实时保护..." -ForegroundColor Cyan

while ($true) {
    try {
        Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - 实时保护已关闭。"
    }
    catch {
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - 关闭失败: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    Start-Sleep -Seconds 5
}

<# 打开:

Set-MpPreference -DisableRealtimeMonitoring $false 

\#>