# =========================================================================
# ProcessController.ps1
#
# 功能:
# - 支持交互式控制和一次性参数化设置。
# - 按 Ctrl+C 或输入 'exit' 可随时安全退出并自动解锁。
#
# 用法:
# 1. 交互模式 (无参数运行):
# > .\ProcessController.ps1
#
# 2. 一次性模式 (提供 -Affinity 或 -Weight 参数):
# > .\ProcessController.ps1 -ProcessName "Game" -Affinity "0-7" -Weight 9
# > .\ProcessController.ps1 -ProcessId 12345 -Affinity "8 10 12-15" -Weight 1
#
# 警告:必须以管理员身份运行!
# =========================================================================
# 步骤 0: 定义脚本参数,以支持不同的启动模式
param(
# 用于一次性模式的目标选择参数
[string]$ProcessName,
[int]$ProcessId,
# 用于触发一次性模式的可选参数
[string]$Affinity,
[ValidateRange(1,9)]
[int]$Weight
)
# 步骤 1: 使用 Add-Type 定义所有需要的 Win32 API 签名、结构体和枚举
try {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public static class NativeMethods {
// --- Job Object API ---
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateJobObjectW(IntPtr lpJobAttributes, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECT_INFO_CLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
// --- 枚举定义 ---
public enum JOBOBJECT_INFO_CLASS {
JobObjectBasicLimitInformation = 2,
JobObjectCpuRateControlInformation = 15
}
[Flags]
public enum JOB_OBJECT_LIMIT_FLAGS : uint {
JOB_OBJECT_LIMIT_AFFINITY = 0x00000010,
JOB_OBJECT_CPU_RATE_CONTROL_ENABLE = 0x1,
JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED = 0x2
}
// --- 结构体定义 ---
[StructLayout(LayoutKind.Sequential)]
public struct JOBOBJECT_BASIC_LIMIT_INFORMATION {
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public JOB_OBJECT_LIMIT_FLAGS LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Explicit)]
public struct JOBOBJECT_CPU_RATE_CONTROL_INFORMATION {
[FieldOffset(0)] public JOB_OBJECT_LIMIT_FLAGS ControlFlags;
[FieldOffset(4)] public uint Weight;
}
}
"@ -PassThru -ErrorAction Stop | Out-Null
} catch {
Write-Host "C# 编译失败: $($_.Exception.Message)" -ForegroundColor Red
return
}
# --- 步骤 2: 辅助函数 ---
# 解析复杂的亲和性字符串 (例如 "2 5 8-11")
function Parse-AffinityString {
param([string]$InputString)
[uint64]$totalMask = 0
# 使用正则表达式替换所有非数字和非连字符的分隔符为空格,然后按空格分割
$tokens = $InputString -replace '[^\d\-,]', ' ' -split ' ' | Where-Object { $_ }
foreach ($token in $tokens) {
if ($token -match '^\d+$') { # 如果是单个数字
$cpu = [int]$token
$totalMask = $totalMask -bor (1L -shl $cpu)
} elseif ($token -match '^(\d+)-(\d+)$') { # 如果是范围
$startCpu = [int]$matches[1]
$endCpu = [int]$matches[2]
if ($startCpu -gt $endCpu) { continue } # 忽略无效范围
for ($i = $startCpu; $i -le $endCpu; $i++) {
$totalMask = $totalMask -bor (1L -shl $i)
}
} else {
Write-Warning "已忽略无效的亲和性片段: '$token'"
}
}
return $totalMask
}
# 应用亲和性设置
function Set-JobAffinity {
param($JobHandle, [uint64]$Mask)
$limitInfo = New-Object NativeMethods+JOBOBJECT_BASIC_LIMIT_INFORMATION
$limitInfo.LimitFlags = [NativeMethods+JOB_OBJECT_LIMIT_FLAGS]::JOB_OBJECT_LIMIT_AFFINITY
$limitInfo.Affinity = New-Object System.UIntPtr($Mask)
# 手动管理内存以传递结构体指针
$limitInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($limitInfo))
[System.Runtime.InteropServices.Marshal]::StructureToPtr($limitInfo, $limitInfoPtr, $false)
$result = [NativeMethods]::SetInformationJobObject($JobHandle, [NativeMethods+JOBOBJECT_INFO_CLASS]::JobObjectBasicLimitInformation, $limitInfoPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($limitInfo))
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($limitInfoPtr)
return $result
}
# 应用时间片权重设置
function Set-JobWeight {
param($JobHandle, [int]$Weight)
$cpuInfo = New-Object NativeMethods+JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
$cpuInfo.ControlFlags = [NativeMethods+JOB_OBJECT_LIMIT_FLAGS]::JOB_OBJECT_CPU_RATE_CONTROL_ENABLE -bor [NativeMethods+JOB_OBJECT_LIMIT_FLAGS]::JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED
$cpuInfo.Weight = $Weight
# 手动管理内存以传递结构体指针
$cpuInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($cpuInfo))
[System.Runtime.InteropServices.Marshal]::StructureToPtr($cpuInfo, $cpuInfoPtr, $false)
$result = [NativeMethods]::SetInformationJobObject($JobHandle, [NativeMethods+JOBOBJECT_INFO_CLASS]::JobObjectCpuRateControlInformation, $cpuInfoPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($cpuInfo))
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($cpuInfoPtr)
return $result
}
# --- 步骤 3: 主逻辑 ---
# 模式检测:检查是否提供了 -Affinity 或 -Weight 参数
$isOneShotMode = $PSBoundParameters.ContainsKey('Affinity') -or $PSBoundParameters.ContainsKey('Weight')
# 获取目标进程
$targetProcesses = $null
if ($isOneShotMode) {
if ($PSBoundParameters.ContainsKey('ProcessName')) {
$targetProcesses = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue
} elseif ($PSBoundParameters.ContainsKey('ProcessId')) {
$targetProcesses = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
} else {
Write-Error "在一次性模式下,必须提供 -ProcessName 或 -ProcessId 参数。"
return
}
} else { # 交互模式
$processNameInput = Read-Host -Prompt "请输入目标进程名 (不包含.exe), 或留空以输入进程ID"
if (-not [string]::IsNullOrWhiteSpace($processNameInput)) {
$targetProcesses = Get-Process -Name $processNameInput -ErrorAction SilentlyContinue
$ProcessName = $processNameInput # 保存以用于Job名
} else {
$processIdInput = Read-Host -Prompt "请输入目标进程ID"
if (-not [string]::IsNullOrWhiteSpace($processIdInput)) {
try {
$ProcessId = [int]$processIdInput
$targetProcesses = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
} catch {}
}
}
}
if (-not $targetProcesses) {
Write-Host "未找到任何目标进程。脚本将退出。" -ForegroundColor Yellow
return
}
Write-Host "已找到 $($targetProcesses.Count) 个目标进程:" -ForegroundColor Green
$targetProcesses | ForEach-Object { Write-Host " - $($_.ProcessName) (PID: $($_.Id))" }
# 为 Job Object 起一个唯一的名字
$jobId = if ($ProcessName) { "Name_$($ProcessName)" } else { "PID_$($ProcessId)" }
$jobObjectName = "Global\ProcessControllerJob_For_$($jobId)"
# 创建/打开 Job Object
$jobHandle = [NativeMethods]::CreateJobObjectW([IntPtr]::Zero, $jobObjectName)
if ($jobHandle -eq [IntPtr]::Zero) {
Write-Host "CreateJobObjectW 失败!错误码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" -ForegroundColor Red
return
}
Write-Host "Job Object '$jobObjectName' 已创建/打开。" -ForegroundColor Green
# 分配所有目标进程
foreach ($proc in $targetProcesses) {
[NativeMethods]::AssignProcessToJobObject($jobHandle, $proc.Handle)
}
Write-Host "已将所有目标进程分配到 Job Object。" -ForegroundColor Cyan
Write-Host "----------------------------------------------------"
# --- 步骤 4: 根据模式执行不同逻辑 ---
if ($isOneShotMode) {
# --- 一次性模式 ---
Write-Host "正在应用一次性设置..." -ForegroundColor Yellow
if ($PSBoundParameters.ContainsKey('Affinity')) {
$parsedMask = Parse-AffinityString -InputString $Affinity
if ($parsedMask -gt 0) {
if (Set-JobAffinity -JobHandle $jobHandle -Mask $parsedMask) {
Write-Host "成功!亲和性已设置为 0x$($parsedMask.ToString('X'))" -F Green
} else {
Write-Host "设置亲和性失败!" -F Red
}
}
}
if ($PSBoundParameters.ContainsKey('Weight')) {
if (Set-JobWeight -JobHandle $jobHandle -Weight $Weight) {
Write-Host "成功!时间片权重已设置为 $Weight" -F Green
} else {
Write-Host "设置时间片权重失败!" -F Red
}
}
Write-Host "设置已应用,脚本将退出。限制将持续有效。"
# 在一次性模式下,我们故意不关闭句柄,以使 Job Object 持续存在
# [NativeMethods]::CloseHandle($jobHandle)
} else {
# --- 交互式模式 ---
# 将主循环包裹在 try...finally 中,以确保无论如何都能执行清理代码
try {
Write-Host "已进入交互模式。按 Ctrl+C 可随时安全退出并自动解锁。" -ForegroundColor Cyan
$currentAffinityMask = $null
$currentWeight = $null
while ($true) {
# -- 亲和性设置 --
$affinityPrompt = "请输入亲和性, 或留空跳过, 或 'exit' 退出"
if ($currentAffinityMask -ne $null) { $affinityPrompt += " [当前: 0x$($currentAffinityMask.ToString('X'))]" }
$affinityInput = Read-Host -Prompt $affinityPrompt
if ($affinityInput -eq 'exit') { break }
if (-not [string]::IsNullOrWhiteSpace($affinityInput)) {
$parsedMask = Parse-AffinityString -InputString $affinityInput
if ($parsedMask -gt 0) {
if (Set-JobAffinity -JobHandle $jobHandle -Mask $parsedMask) {
$currentAffinityMask = $parsedMask
Write-Host "成功!亲和性已更新为 0x$($currentAffinityMask.ToString('X'))" -F Green
} else {
Write-Host "设置亲和性失败!" -F Red
}
} else {
Write-Warning "未从输入中解析出有效的 CPU 核心,亲和性未改变。"
}
} else {
Write-Host "亲和性设置已跳过。" -F Gray
}
# -- 时间片权重设置 --
$weightPrompt = "请输入时间片权重 (1-9), 或留空跳过, 或 'exit' 退出"
if ($currentWeight -ne $null) { $weightPrompt += " [当前: $currentWeight]" }
$weightInput = Read-Host -Prompt $weightPrompt
if ($weightInput -eq 'exit') { break }
if (-not [string]::IsNullOrWhiteSpace($weightInput)) {
if ($weightInput -match '^[1-9]$') {
if (Set-JobWeight -JobHandle $jobHandle -Weight ([int]$weightInput)) {
$currentWeight = [int]$weightInput
Write-Host "成功!时间片权重已更新为 $currentWeight" -F Green
} else {
Write-Host "设置时间片权重失败!" -F Red
}
} else {
Write-Warning "无效的输入!权重必须是 1-9 之间的单个数字,权重未改变。"
}
} else {
Write-Host "时间片权重设置已跳过。" -F Gray
}
Write-Host "----------------------------------------------------"
Write-Host "设置完毕,等待下一次修改... (按 Enter 键以再次输入)"
Read-Host | Out-Null
}
}
finally {
# --- 统一的、保证执行的解锁和清理逻辑 ---
Write-Host "" # 换行
Write-Host "正在退出... 自动移除所有限制以进行解锁..." -ForegroundColor Yellow
# 检查句柄是否有效,防止在创建失败时也尝试解锁
if ($jobHandle -and $jobHandle -ne [IntPtr]::Zero) {
# 1. 解锁亲和性
$unlockLimitInfo = New-Object NativeMethods+JOBOBJECT_BASIC_LIMIT_INFORMATION
$unlockLimitInfo.LimitFlags = 0
$unlockLimitInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($unlockLimitInfo))
[System.Runtime.InteropServices.Marshal]::StructureToPtr($unlockLimitInfo, $unlockLimitInfoPtr, $false)
[NativeMethods]::SetInformationJobObject($jobHandle, [NativeMethods+JOBOBJECT_INFO_CLASS]::JobObjectBasicLimitInformation, $unlockLimitInfoPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($unlockLimitInfo)) | Out-Null
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($unlockLimitInfoPtr)
# 2. 解锁时间片权重
$unlockCpuInfo = New-Object NativeMethods+JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
$unlockCpuInfo.ControlFlags = 0
$unlockCpuInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($unlockCpuInfo))
[System.Runtime.InteropServices.Marshal]::StructureToPtr($unlockCpuInfo, $unlockCpuInfoPtr, $false)
[NativeMethods]::SetInformationJobObject($jobHandle, [NativeMethods+JOBOBJECT_INFO_CLASS]::JobObjectCpuRateControlInformation, $unlockCpuInfoPtr, [System.Runtime.InteropServices.Marshal]::SizeOf($unlockCpuInfo)) | Out-Null
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($unlockCpuInfoPtr)
Write-Host "解锁成功!" -ForegroundColor Green
# 最终清理句柄
[NativeMethods]::CloseHandle($jobHandle)
}
Write-Host "控制器已终止。"
}
}
Quote from: Fanny Moen on June 09, 2025, 05:40:54 AMIt's fascinating to see how tools like Process Lasso help us optimize our computer's performance to make everything run smoother – from browsing the web to working to gaming. And speaking of smooth performance, it's impossible not to mention the refreshing feeling when you turn on a Basketball Stars game, all the controls are silky smooth, no lag, just you, the ball, and the ultimate scoring!Of course, using tools to optimize performance is absolutely necessary in programs that require high programming requirements.
Quote from: Lawlcakes on June 27, 2025, 10:14:04 AMHello there,Check if Process Lasso is running as admin. Also, try disabling Core Parking in BIOS (look for "CPU Park Control" on MSI boards). Sometimes Windows Game Mode remnants interfere—verify it's fully off in both OS and BIOS.
New to AMD and the x3D architecture. Did my research as best I could. Parking works on my 9950x3d and game mode parking works as well. I've turned game mode off now. Through investigation, I learned that Process Lasso could yield the best results for gaming. So I have the trial version now, tried setting up CPU affinity rules or CPU set rules for a particular game (and its launcher), but they aren't showing up in the Process Lasso Log. I read on another thread that when the rule changes aren't showing up in the log, something else is going on in the system.
Could someone please help direct me to what could be causing the issue? The system is brand new, X670E tomahawk gaming. Could there be a bios setting? I already created a BitDefender exception so it's most likely not AV related.
Quote from: DanielGadson on June 27, 2025, 03:16:22 AMI've recently installed the free version of Process Lasso to further optimize my 4090 / 5800x3D system for VR gaming and have been very pleased so far with the results. I used a shared profile I found online that deprioritizes background system tasks, and I've manually added folders where I keep my games to toggle Performance mode on and favor even-core sets for my CPU. I've gotten a noticeable uptick in performance from using these settings on the free version, but I also see people suggesting to set high I/O priority for games in PL but that's not available on the free version. Have folks found it's worth the ~$30 or so sale price for unrestricted Process Lasso to get the I/O Priority functionality to further optimize for gaming?Can someone help me?