Your analysis is correct. SetWindowTheme works for window controls, but TSpeedButton and TBitBtn are non-vinyl items.
In XE, from my quick scan, it seems that most controls call Themes.ThemeControl to determine if they should be drawn. Thus, a simple solution is to replace this procedure with logic that you control. Since it does not provide any extension points, you need to connect it. Like this:
procedure PatchCode(Address: Pointer; const NewCode; Size: Integer); var OldProtect: DWORD; begin if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then begin Move(NewCode, Address^, Size); FlushInstructionCache(GetCurrentProcess, Address, Size); VirtualProtect(Address, Size, OldProtect, @OldProtect); end; end; type PInstruction = ^TInstruction; TInstruction = packed record Opcode: Byte; Offset: Integer; end; procedure RedirectProcedure(OldAddress, NewAddress: Pointer); var NewCode: TInstruction; begin NewCode.Opcode := $E9;//jump relative NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode); PatchCode(OldAddress, NewCode, SizeOf(NewCode)); end; function MyThemeControl(AControl: TControl): Boolean; begin Result := False; if AControl = nil then exit; if AControl is TSpeedButton then exit; if AControl is TBitBtn then exit; Result := (not (csDesigning in AControl.ComponentState) and ThemeServices.ThemesEnabled) or ((csDesigning in AControl.ComponentState) and (AControl.Parent <> nil) and (ThemeServices.ThemesEnabled and not UnthemedDesigner(AControl.Parent))); end; initialization RedirectProcedure(@Themes.ThemeControl, @MyThemeControl);
Be that as it may, this will not work with runtime packages, but it is easy enough to extend the code to work with packages.
David heffernan
source share