seerge/g-helper · warning · Exception
Failed to get switchable graphics applications. Error code:
Error message
Failed to get switchable graphics applications. Error code:
What it means
Thrown inside AmdGpuControl.KillGPUApps when the AMD Display Library call ADL2_SwitchableGraphics_Applications_Get returns a value other than ADL_SUCCESS (0). This is a P/Invoke into atiadlxx.dll (AmdAdl2.cs:534) that enumerates applications with GPU affinity; the iListType argument is hardcoded to 2. AMD ADL returns negative integer codes (e.g. ADL_ERR=-1, ADL_ERR_NOT_INIT=-2, ADL_ERR_INVALID_PARAM=-3, ADL_ERR_NOT_SUPPORTED=-8), which are appended to the message. Note the whole block is wrapped in try/catch that calls Logger.WriteLine, so the exception is caught and logged and KillGPUApps simply degrades to not killing any apps.
Source
Thrown at app/Gpu/AMD/AmdGpuControl.cs:374
return performanceLevels;
}
public void KillGPUApps()
{
if (!IsValid) return;
nint appInfoPtr = nint.Zero;
int appCount = 0;
try
{
// Get switchable graphics applications information
var result = ADL2_SwitchableGraphics_Applications_Get(_adlContextHandle, 2, out appCount, out appInfoPtr);
if (result != 0)
{
throw new Exception("Failed to get switchable graphics applications. Error code: " + result);
}
// Convert the application data pointers to an array of structs
var appInfoArray = new ADLSGApplicationInfo[appCount];
nint currentPtr = appInfoPtr;
for (int i = 0; i < appCount; i++)
{
appInfoArray[i] = Marshal.PtrToStructure<ADLSGApplicationInfo>(currentPtr);
currentPtr = nint.Add(currentPtr, Marshal.SizeOf<ADLSGApplicationInfo>());
}
var appNames = new List<string>();
for (int i = 0; i < appCount; i++)
{
if (appInfoArray[i].iGPUAffinity == 1)
{View on GitHub (pinned to b9ba417f1b)
Solutions
- Update the AMD Adrenalin driver to a version that supports ADL2 switchable graphics on this hardware.
- Keep the existing 'if (!IsValid) return;' guard and also early-return when _iGPU is null (no integrated GPU means no switchable graphics).
- Stop throwing for a non-zero result: log the code and return, since KillGPUApps is already best-effort and wrapped in try/catch.
- Verify Adl2.Load() succeeded and _adlContextHandle is non-zero before calling (IsValid covers this but pair it with the _iGPU null check).
- Decode the returned code against known ADL constants (ADL_ERR, ADL_ERR_NOT_SUPPORTED) to log a precise reason.
Example fix
// before
var result = ADL2_SwitchableGraphics_Applications_Get(_adlContextHandle, 2, out appCount, out appInfoPtr);
if (result != 0)
throw new Exception("Failed to get switchable graphics applications. Error code: " + result);
// after
var result = ADL2_SwitchableGraphics_Applications_Get(_adlContextHandle, 2, out appCount, out appInfoPtr);
if (result != Adl2.ADL_SUCCESS)
{
Logger.WriteLine($"ADL2_SwitchableGraphics_Applications_Get failed (code {result}); skipping GPU app cleanup.");
return;
} Defensive patterns
Strategy: try-catch
Validate before calling
// The strongest pre-check available is the public IsValid property plus a switchable-GPU presence check. if (!amdGpu.IsValid) return; // ADL context not created (AmdGpuControl.cs:95) if (amdGpu._iGPU is null) return; // no integrated GPU => no switchable graphics // Note: _iGPU is private; expose a bool HasSwitchableGraphics property if you need this guard outside the class.
Try / catch
// KillGPUApps already does this — keep it, but stop throwing inside the try:
try
{
var result = ADL2_SwitchableGraphics_Applications_Get(_adlContextHandle, 2, out appCount, out appInfoPtr);
if (result != Adl2.ADL_SUCCESS)
{
Logger.WriteLine($"KillGPUApps: switchable-graphics query failed (ADL code {result}).");
return;
}
// ... process appInfoArray ...
}
catch (Exception ex)
{
Logger.WriteLine($"KillGPUApps: {ex.Message}");
}
finally
{
if (appInfoPtr != nint.Zero) Marshal.FreeCoTaskMem(appInfoPtr);
} Prevention
- Always gate AMD ADL calls with IsValid (which checks _isReady && _adlContextHandle != nint.Zero).
- Add an _iGPU null check before switchable-graphics calls: a single-GPU system legitimately has none.
- Prefer logging + early return over throw for non-zero ADL results; these are expected on unsupported drivers and the method is best-effort.
- Free the CoTaskMem pointer in a finally (already done) and never use appInfoPtr after a non-zero result.
- Keep Adl2.Load() failure paths silent and ensure AmdGpuControl is not constructed at all on non-AMD systems (the constructor returns early at line 72).
When it happens
Trigger: Calling KillGPUApps when ADL2_SwitchableGraphics_Applications_Get(_adlContextHandle, 2, out appCount, out appInfoPtr) returns non-zero. This happens when the ADL context handle is stale/uninitialized, atiadlxx.dll is the wrong version, the driver does not support switchable graphics or list type 2, no discrete+integrated GPU pairing exists, or the call is made after ADL2_Main_Control_Destroy. The guard 'if (!IsValid) return;' (line 363) only checks _isReady && _adlContextHandle != 0, so a valid-but-not-fully-functional context still reaches the throwing call.
Common situations: AMD Adrenalin driver is missing, outdated, or a version that dropped the switchable-graphics API; the system is a desktop with a single AMD GPU (no switchable graphics); atiadlxx.dll loaded but ADL2_Main_Control_Create partially failed; running on an Intel/NVIDIA-only machine where Adl2.Load succeeded by accident; the context was disposed while KillGPUApps ran. Because it is logged and swallowed, users usually only see it in the log file, not as a crash.
Related errors
AI-assisted analysis of seerge/g-helper@b9ba417f1b (2026-08-13).
Data as JSON: /api/errors/183c57ffe3641798.
Report an issue: GitHub.