Wie kann man die aktuelle Einstellung der Hardwarebeschleunigung der Grafikkarte ermitteln bzw. ändern?
Leider gibt es dafür keine direkte API in Windows, offensichtlich macht das Kontrollfeld (in XP: Desktop -> Eigenschaften -> Einstellungen -> Erweitert -> Problembehandlung -> Hardwarebeschleunigung) des Explorers das auch direkt in der Registry (dort wird der Wert abgelegt).

Insofern muss man bei einer "nachgebauten" Lösung immer mit etwas "Raten" leben.
Folgende Unit kann die aktuelle Einstellung ermitteln, setzen und eine Änderung auch direkt wirksam machen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174:
| unit UVHWAccel;
interface
function GetPrimaryDisplayKey(const ForceUpdate: Boolean = FALSE): AnsiString;
function GetHardwareAccelerationLevel: Integer;
function SetHardwareAccelerationLevel(const NewLevel: Integer): Boolean;
function ForceUpdateDisplaySettings: Boolean;
implementation
uses Windows, Registry, SysUtils;
const DISPLAY_DEVICE_PRIMARY_DEVICE = DWORD($00000004);
REG_PATH_VIDEO = AnsiString('\HARDWARE\DEVICEMAP\VIDEO'); REG_KEY_VIDEO0 = AnsiString('\Device\Video0'); REG_PREFIX = AnsiString('\REGISTRY\MACHINE'); REG_ACCEL_VAL = AnsiString('Acceleration.Level');
ENUM_CURRENT_SETTINGS = DWORD(-1); type TDisplayDevice = packed record cb: DWORD; DeviceName: array[0..31] of AnsiChar; DeviceString: array[0..127] of AnsiChar; StateFlags: DWORD; DeviceID: array[0..127] of AnsiChar; DeviceKey: array[0..127] of AnsiChar; end;
var PrimaryDisplayKeyCached: AnsiString;
function EnumDisplayDevices(Unused: Pointer; iDevNum: DWORD; var lpDisplayDevice: TDisplayDevice; dwFlags: DWORD): BOOL; stdcall; external user32 name 'EnumDisplayDevicesA';
function GetPrimaryDisplayKey(const ForceUpdate: Boolean = FALSE): AnsiString; var DisplayDevice: TDisplayDevice; iDevNum: DWORD; Reg: TRegistry; begin if ForceUpdate then PrimaryDisplayKeyCached := ''; if (PrimaryDisplayKeyCached = '') then begin DisplayDevice.cb := SizeOf(DisplayDevice); iDevNum:= 0; while (PrimaryDisplayKeyCached = '') and EnumDisplayDevices(NIL, iDevNum, DisplayDevice, 0) do begin if (DisplayDevice.StateFlags and DISPLAY_DEVICE_PRIMARY_DEVICE) <> 0 then PrimaryDisplayKeyCached := DisplayDevice.DeviceKey; Inc(iDevNum); end; if (PrimaryDisplayKeyCached = '') then begin Reg := TRegistry.Create(KEY_READ); try try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey(REG_PATH_VIDEO, FALSE) then PrimaryDisplayKeyCached := Reg.ReadString(REG_KEY_VIDEO0); finally Reg.Free; end; except PrimaryDisplayKeyCached := ''; end; end; if (UpperCase(Copy(PrimaryDisplayKeyCached, 1, Length(REG_PREFIX))) = REG_PREFIX) then Delete(PrimaryDisplayKeyCached, 1, Length(REG_PREFIX)); end; Result := PrimaryDisplayKeyCached; end;
function GetHardwareAccelerationLevel: Integer; var Reg: TRegistry; begin Result := -1; if (PrimaryDisplayKeyCached = '') then GetPrimaryDisplayKey; if (PrimaryDisplayKeyCached <> '') then begin Reg := TRegistry.Create(KEY_READ); try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey(PrimaryDisplayKeyCached, FALSE) then begin if Reg.ValueExists(REG_ACCEL_VAL) then Result := Reg.ReadInteger(REG_ACCEL_VAL) else Result := 0; end finally Reg.Free; end; end; end;
function SetHardwareAccelerationLevel(const NewLevel: Integer): Boolean; var Reg: TRegistry; begin Result := FALSE; if (PrimaryDisplayKeyCached = '') then GetPrimaryDisplayKey; if (PrimaryDisplayKeyCached <> '') then begin Reg := TRegistry.Create; try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey(PrimaryDisplayKeyCached, FALSE) then begin if (NewLevel = 0) then begin Reg.DeleteValue(REG_ACCEL_VAL); Result := TRUE; end else if (NewLevel in [1..5]) then begin Reg.WriteInteger(REG_ACCEL_VAL, NewLevel); Result := TRUE; end; end finally Reg.Free; end; end; end;
function ForceUpdateDisplaySettings: Boolean; var DevMode: TDevMode; begin Result := EnumDisplaySettings(NIL, ENUM_CURRENT_SETTINGS, DevMode); if Result then ChangeDisplaySettings(DevMode, CDS_RESET); end;
initialization PrimaryDisplayKeyCached := '';
end. |
Anmerkungen:
- Die Identifikation des primären Display-Devices erfolgt im Wesentlichen über die Funktion EnumDisplayDevices, die mittlerweile auch den Registrierungsschlüssel des entsprechenden Gerätes liefert (seit wann, weiß ich leider nicht). Wenn das primäre Device nicht auf diesem Wege ermittelt werden kann, wird manuell unter \\HKLM\HARDWARE\DEVICEMAP\VIDEO\Device\Video0 in der Registrierung gesucht, was aber leider nicht immer funktioniert, da es von dem Treiber abhängt, wie das Device dort eingetragen wird. Es ist also prinzipiell möglich, dass die Funktionen der Unit nicht genutzt werden können, weil der Registrierungs-Schlüssel des primären Devices nicht gefunden wurde.
- Bisher getestet unter Win XP prof sp3, weitere Testergebnisse sind willkommen
Benutzung:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| uses ..., UVHWAccel;
var i: Integer; begin i := GetHardwareAccelerationLevel; if (i >= 0) then ShowMessage(IntToStr(i)) else ShowMessage('Fehler!'); end;
begin if SetHardwareAccelerationLevel(0) then ForceUpdateDisplaySettings else ShowMessage('Fehler!'); |
Im Anhang befindet sich eine Demo-Anwendung mit Quelltext.
Als kleine Spezial-Zugabe hängt auch noch ein Kommandozeilenutility (mit Quelltext) dran, so dass man die Funktionalität direkt fertig gekapselt nutzen kann.
Warum braucht man das überhaupt?
Leider kommt es unter bestimmten Umständen zu einer Fehlersituation bei Fernwartungs-Programmen (z.B. NetOp, VNC, etc.) und Java-Anwendungen: der Inhalt eines Java-Fensters wird in der Fernwartung nicht angezeigt (leer oder schwarz), da die Java-Laufzeitumgebung irgendwelche Probleme mit dem Grafiktreiber hat (die Details führen hier wirklich zu weit, da schieben sich auch die Beteiligten gegenseitig den Schwarzen Peter zu, die (vor allem Onboard-)Treiber-Hersteller sagen, dass Java-Framework ist "kaputt", die Java-Leute sagen, der Treiber ist "kaputt", *seufz*). Wenn man dann die Hardwarebeschleunigung runterdreht, so dass die DD-Funktionen von Java nicht mehr verwendet werden, ist der Inhalt der Java-Fenster wieder in der Fernwartung sichtbar.

There are 10 types of people - those who understand binary and those who don´t.