Five bugs identified and patched in retail Asheron's Call client: - v3b: palette refcount over-increment (3-byte NOP at two sites) - v5: RenderSurface PurgeResource no-op stub (vtable slot 2 thunk) - v11: two dangling-pointer crash guards (NULL-check + reorder) - v14: CEnvCell::Destroy ClipPlaneList leak (18-byte JMP to cleanup thunk) - v22: unpacker stale-pointer SEH guard (whole-function __try/__except) All five ship in leakfix.dll (117 KB, SHA d282f23c…) which is loaded by acclient.exe at process start via PE import table patching by tools/install_leakfix.py. Controlled 15-client fleet soak: unpatched control died at 26h with palette exhaustion; all 14 patched clients survived past that point and reached ≥5-day uptime. Residual ~15 MB/h growth traced to d3d9.dll's internal slab allocator (260KB surface backing buffers retained after Release). See REPORT.md §10 for the full investigation; conclusion is that it's unfixable from outside d3d9. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
2.7 KiB
PowerShell
72 lines
2.7 KiB
PowerShell
# keepalive_f5.ps1
|
|
# Pulses F5 to the AC client window every N seconds to defeat Coldeve's
|
|
# idle-kick. Finds the window by class "Turbine Device Class" so it's
|
|
# robust to title-string encoding quirks.
|
|
|
|
param(
|
|
[int]$IntervalSec = 60,
|
|
[string]$LogFile = "C:\Users\acbot\leakhunt\artifacts\phase1\keepalive.log"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
Add-Type @"
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
public class LhKA {
|
|
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
[DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
|
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetClassName(IntPtr hWnd, StringBuilder text, int count);
|
|
[DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
|
|
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
}
|
|
"@ -ErrorAction SilentlyContinue
|
|
|
|
function W([string]$msg) {
|
|
$t = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
|
|
"[$t] $msg" | Out-File -Append -Encoding utf8 -FilePath $LogFile
|
|
}
|
|
|
|
function Find-AcWindow {
|
|
param([int]$ProcessId)
|
|
$found = $null
|
|
$cb = [LhKA+EnumWindowsProc]{
|
|
param($h, $l)
|
|
$pid_ = 0
|
|
[void][LhKA]::GetWindowThreadProcessId($h, [ref]$pid_)
|
|
if ($pid_ -eq $ProcessId -and [LhKA]::IsWindowVisible($h)) {
|
|
$cls = New-Object System.Text.StringBuilder 256
|
|
[void][LhKA]::GetClassName($h, $cls, $cls.Capacity)
|
|
if ($cls.ToString() -eq "Turbine Device Class") {
|
|
$script:found = $h
|
|
return $false
|
|
}
|
|
}
|
|
return $true
|
|
}
|
|
[void][LhKA]::EnumWindows($cb, [IntPtr]::Zero)
|
|
return $script:found
|
|
}
|
|
|
|
$WM_KEYDOWN = 0x0100
|
|
$WM_KEYUP = 0x0101
|
|
$VK_F5 = 0x74
|
|
|
|
W "keepalive_f5 starting, pulse every $IntervalSec s"
|
|
|
|
while ($true) {
|
|
$ac = Get-Process -Name acclient -ErrorAction SilentlyContinue | Sort-Object StartTime -Descending | Select-Object -First 1
|
|
if (-not $ac) { W "acclient gone - exiting"; break }
|
|
|
|
$hwnd = Find-AcWindow -ProcessId $ac.Id
|
|
if (-not $hwnd) { W "AC window not found for PID $($ac.Id); waiting"; Start-Sleep -Seconds $IntervalSec; continue }
|
|
|
|
$ok1 = [LhKA]::PostMessage($hwnd, $WM_KEYDOWN, [IntPtr]$VK_F5, [IntPtr]0)
|
|
Start-Sleep -Milliseconds 50
|
|
$ok2 = [LhKA]::PostMessage($hwnd, $WM_KEYUP, [IntPtr]$VK_F5, [IntPtr]0)
|
|
W "F5 pulse -> hwnd=0x$($hwnd.ToInt64().ToString('X')) pid=$($ac.Id) PM=$([math]::Round($ac.PrivateMemorySize64/1MB,1))MB ok=$ok1,$ok2"
|
|
|
|
Start-Sleep -Seconds $IntervalSec
|
|
}
|