The Road to Delphi

Delphi – Free Pascal – Oxygene

Changing the glass composition color (DWM) using delphi

6 Comments

Before to read this post you must be aware which the material exposed  make uses of undocumented windows functions, so you must know the risks involved in using them.   If you are not comfortably with that please  skip this post.

Some days ago  reading this article,  I found a very nice application called Aura (Written in C#), which  calculates average color of  the desktop background image or the active window icon and sets it as Aero Glass color.

Check this demo video of Aura

After of that, I check the Desktop Window Manager (DWM) reference to find the functions which allow change the color of the  glass in Windows Vista/7,  but I can’t found any documented function to do this. Finally digging in the source code of the Aura application I found two undocumented functions called DwmGetColorizationParameters and DwmSetColorizationParameter which makes this task. Then I check the parameters, the call conventions and finally I took the dwmapi.dll file and after of analyze using IDA and the Microsoft public symbol server, I found a lot of undocumented functions.

At this point originally I was going to include the details of the disassembled code, but to avoid legal problems I just going to include the dump of the exported functions

This is the dump of the exported functions of this library (dwmapi.dll) , as you can see exist a lot of undocumented functions.

dwmapi.1; Index 100;undocumented
dwmapi.2; Index 101;undocumented
DwmEnableComposition; Index 102;
dwmapi.4; Index 103;undocumented
dwmapi.5; Index 104;undocumented
dwmapi.6; Index 105;undocumented
dwmapi.7; Index 106;undocumented
dwmapi.8; Index 107;undocumented
dwmapi.9; Index 108;undocumented
dwmapi.10; Index 109;undocumented
dwmapi.11; Index 110;undocumented
DwmAttachMilContent; Index 111;
dwmapi.13; Index 112;undocumented
dwmapi.14; Index 113;undocumented
dwmapi.15; Index 114;undocumented
dwmapi.16; Index 115;undocumented
DwmDefWindowProc; Index 116;
DwmDetachMilContent; Index 117;
dwmapi.19; Index 118;undocumented
dwmapi.20; Index 119;undocumented
dwmapi.21; Index 120;undocumented
dwmapi.22; Index 121;undocumented
DwmEnableBlurBehindWindow; Index 122;
DwmEnableMMCSS; Index 123;
dwmapi.25; Index 124;undocumented
dwmapi.26; Index 125;undocumented
dwmapi.27; Index 126;undocumented
dwmapi.28; Index 127;undocumented
dwmapi.29; Index 128;undocumented
dwmapi.30; Index 129;undocumented
dwmapi.31; Index 130;undocumented
dwmapi.32; Index 131;undocumented
dwmapi.33; Index 132;undocumented
dwmapi.34; Index 133;undocumented
dwmapi.35; Index 134;undocumented
DwmExtendFrameIntoClientArea; Index 135;
DwmFlush; Index 136;
DwmGetColorizationColor; Index 137;
DwmGetCompositionTimingInfo; Index 138;
DwmGetGraphicsStreamClient; Index 139;
DwmGetGraphicsStreamTransformHint; Index 140;
DwmGetTransportAttributes; Index 141;
DwmGetWindowAttribute; Index 142;
DwmInvalidateIconicBitmaps; Index 143;
DwmIsCompositionEnabled; Index 144;
DwmModifyPreviousDxFrameDuration; Index 145;
DwmQueryThumbnailSourceSize; Index 146;
DwmRegisterThumbnail; Index 147;
DwmSetDxFrameDuration; Index 148;
DwmSetIconicLivePreviewBitmap; Index 149;
DwmSetIconicThumbnail; Index 150;
DwmSetPresentParameters; Index 151;
DwmSetWindowAttribute; Index 152;
DwmUnregisterThumbnail; Index 153;
DwmUpdateThumbnailProperties; Index 154;

Now using the Microsoft public symbol server we can obtain the undocumented functions names.

dwmapi.1; Index 100;_DwmpDxGetWindowSharedSurface
dwmapi.2; Index 101;_DwmpDxUpdateWindowSharedSurface
DwmEnableComposition; Index 102;
dwmapi.4; Index 103;_DwmpRestartComposition
dwmapi.5; Index 104;_DwmpSetColorizationColor
dwmapi.6; Index 105;_DwmpStartOrStopFlip3D
dwmapi.7; Index 106;_DwmpIsCompositionCapable
dwmapi.8; Index 107;_DwmpGetGlobalState
dwmapi.9; Index 108;_DwmpEnableRedirection
dwmapi.10; Index 109;_DwmGetGraphicsStreamTransformHint
dwmapi.11; Index 110;_DwmpCloseGraphicsStream
DwmAttachMilContent; Index 111;
dwmapi.13; Index 112;_DwmpSetGraphicsStreamTransformHint
dwmapi.14; Index 113;_DwmpActivateLivePreview
dwmapi.15; Index 114;_DwmpQueryThumbnailType
dwmapi.16; Index 115;_DwmpStartupViaUserInit
DwmDefWindowProc; Index 116;
DwmDetachMilContent; Index 117;
dwmapi.19; Index 118;_DwmpGetAssessment
dwmapi.20; Index 119;_DwmpGetAssessmentUsage
dwmapi.21; Index 120;_DwmpSetAssessmentUsage
dwmapi.22; Index 121;_DwmpIsSessionDWM
DwmEnableBlurBehindWindow; Index 122;
DwmEnableMMCSS; Index 123;
dwmapi.25; Index 124;_DwmpRegisterThumbnail
dwmapi.26; Index 125;_DwmpDxBindSwapChain
dwmapi.27; Index 126;_DwmpDxUnbindSwapChain
dwmapi.28; Index 127;_DwmGetColorizationParameters
dwmapi.29; Index 128;_DwmpDxgiIsThreadDesktopComposited
dwmapi.30; Index 129;_DwmpDxgiDisableRedirection
dwmapi.31; Index 130;_DwmpDxgiEnableRedirection
dwmapi.32; Index 131;_DwmSetColorizationParameters
dwmapi.33; Index 132;_DwmpGetCompositionTimingInfoEx
dwmapi.34; Index 133;_DwmpDxUpdateWindowRedirectionBltSurface
dwmapi.35; Index 134;_DwmpDxSetContentHostingInformation
DwmExtendFrameIntoClientArea; Index 135;
DwmFlush; Index 136;
DwmGetColorizationColor; Index 137;
DwmGetCompositionTimingInfo; Index 138;
DwmGetGraphicsStreamClient; Index 139;
DwmGetGraphicsStreamTransformHint; Index 140;
DwmGetTransportAttributes; Index 141;
DwmGetWindowAttribute; Index 142;
DwmInvalidateIconicBitmaps; Index 143;
DwmIsCompositionEnabled; Index 144;
DwmModifyPreviousDxFrameDuration; Index 145;
DwmQueryThumbnailSourceSize; Index 146;
DwmRegisterThumbnail; Index 147;
DwmSetDxFrameDuration; Index 148;
DwmSetIconicLivePreviewBitmap; Index 149;
DwmSetIconicThumbnail; Index 150;
DwmSetPresentParameters; Index 151;
DwmSetWindowAttribute; Index 152;
DwmUnregisterThumbnail; Index 153;
DwmUpdateThumbnailProperties; Index 154;

In this article only we use the DwmGetColorizationParameters and DwmSetColorizationParameter functions,  the declaration of these in Delphi is

type
tagCOLORIZATIONPARAMS = record
clrColor        : COLORREF;  //ColorizationColor
clrAftGlow      : COLORREF;  //ColorizationAfterglow
nIntensity      : UINT;      //ColorizationColorBalance -> 0-100
clrAftGlowBal   : UINT;      //ColorizationAfterglowBalance
clrBlurBal      : UINT;      //ColorizationBlurBalance
clrGlassReflInt : UINT;      //ColorizationGlassReflectionIntensity
fOpaque         : BOOL;
end;

COLORIZATIONPARAMS=tagCOLORIZATIONPARAMS;
TColorizationParams=COLORIZATIONPARAMS;
PColorizationParams=^TColorizationParams;

TDwmGetColorizationParameters = procedure(out parameters :TColorizationParams); stdcall;
TDwmSetColorizationParameters = procedure(parameters :PColorizationParams;unknown:BOOL); stdcall;

To check the current values of the tagCOLORIZATIONPARAMS  structure  used by the DwmGetColorizationParameters and DwmSetColorizationParameters  functions, you can see the windows registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM

Now in order to change the colorization color of the glass from Delphi you must call these functions in this way

Procedure SetCompositionColor(AColor:TColor);
var
  Params : TColorizationParams;
begin
   //Convert the TColor to a valid color RGB -> BGR
   AColor:=RGB(GetBValue(AColor),GetGValue(AColor),GetRValue(AColor));
   ZeroMemory(@Params,SizeOf(Params));
   //Get the current values
   DwmGetColorizationParameters(Params);
   //Set the New Color
   Params.clrColor  :=AColor;
   //Call the function to set the new color
   DwmSetColorizationParameters(@Params,Bool(0));
end;

Check this delphi console application which change the color of the glass

program DwmDelphiDemo;

{$APPTYPE CONSOLE}
//Author  : Rodrigo Ruz V.
//2011-05-05

uses
  Graphics,
  Windows,
  SysUtils;

type
 tagCOLORIZATIONPARAMS = record
	clrColor        : COLORREF;  //ColorizationColor
        clrAftGlow      : COLORREF;  //ColorizationAfterglow
        nIntensity      : UINT;      //ColorizationColorBalance -> 0-100
	clrAftGlowBal   : UINT;      //ColorizationAfterglowBalance
	clrBlurBal      : UINT;      //ColorizationBlurBalance
	clrGlassReflInt : UINT;      //ColorizationGlassReflectionIntensity
	fOpaque         : BOOL;
end;

 COLORIZATIONPARAMS=tagCOLORIZATIONPARAMS;
 TColorizationParams=COLORIZATIONPARAMS;
 PColorizationParams=^TColorizationParams;

 TDwmGetColorizationParameters = procedure(out parameters :TColorizationParams); stdcall;
 TDwmSetColorizationParameters = procedure(parameters :PColorizationParams;unknown:BOOL); stdcall;
 TDwmIsCompositionEnabled      = function(out pfEnabled : BOOL): HRESULT; stdcall;

var
 DwmGetColorizationParameters : TDwmGetColorizationParameters;
 DwmSetColorizationParameters : TDwmSetColorizationParameters;
 DwmIsCompositionEnabled      : TDwmIsCompositionEnabled;
 hdwmapi                      : Cardinal;

function  IsAeroEnabled: Boolean;
var
  pfEnabled : BOOL;
begin
 Result:=False;
 if Assigned(DwmIsCompositionEnabled) and (DwmIsCompositionEnabled(pfEnabled)=S_OK) then
  Result:=pfEnabled;
end;

Procedure SetCompositionColor(AColor:TColor);
var
  Params : TColorizationParams;
begin
   //convert the TColor to a valid color RGB -> BGR
   AColor:=RGB(GetBValue(AColor),GetGValue(AColor),GetRValue(AColor));
   ZeroMemory(@Params,SizeOf(Params));
   //Get the current values
   DwmGetColorizationParameters(Params);
   //Set the New Color
   Params.clrColor  :=AColor;
   //Call the function to set the new color
   DwmSetColorizationParameters(@Params,Bool(0));
   //get the colorization parameters and show the details
   DwmGetColorizationParameters(Params);
   Writeln(format('Intensity %d - Colorization Color %.8x - Colorization Afterglow Color 2 %.8x',[Params.nIntensity,Params.clrColor,Params.clrAftGlow]));
end;

//load the functions to use
function Init_Dwm: Boolean;
begin
 Result:=False;
  hdwmapi := LoadLibrary('dwmapi.dll');
  if (hdwmapi <> 0) then
  begin
    @DwmIsCompositionEnabled      := GetProcAddress(hdwmapi, 'DwmIsCompositionEnabled');
    //load the DwmGetColorizationParameters  function from the 127 index
    @DwmGetColorizationParameters := GetProcAddress(hdwmapi, LPCSTR(127));
    //load the DwmSetColorizationParameters function from the 131 index
    @DwmSetColorizationParameters := GetProcAddress(hdwmapi, LPCSTR(131));
    Result:=(Assigned(DwmGetColorizationParameters)) and (Assigned(DwmSetColorizationParameters)) and IsAeroEnabled;
  end;
end;

procedure Done_Dwm;
begin
  if (hdwmapi <> 0) then
   FreeLibrary(hdwmapi);
end;

const
  MaxColors =10;
  Colors    : Array [0..MaxColors-1] of TColor =
  (clRed,clBlack,clBlue,clGreen,clYellow,clAqua,clFuchsia,clLime,clPurple,clDkGray);
var
   Params     : TColorizationParams;
   DwmActive  : Boolean;
   i          : Integer;
begin
  try
    DwmActive:=Init_Dwm;
    try
      if DwmActive then
      begin
        //Get the current settings
        DwmGetColorizationParameters(Params);
        Writeln('Current Values');
        Writeln(format('Intensity %d - Colorization Color  %.8x - Colorization Afterglow Color %.8x %d %d %d',[Params.nIntensity,Params.clrColor,Params.clrAftGlow,Params.clrAftGlowBal,Params.clrBlurBal,Params.clrGlassReflInt]));
        try

          for i:= low(Colors) to high(Colors) do
          begin
            SetCompositionColor(Colors[i]);
            Writeln('Press enter to continue');
            Readln;
          end;

        finally
        //Restore the original settings
            DwmSetColorizationParameters(@Params,Bool(0));
        end;
      end
      else
      Writeln('Glass is not active');
    finally
      Done_Dwm;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

and this is the result

Finally just for fun I wrote a similar application to Aura using delphi (just in 240 lines of code ;P and without Net framework) and this is the result.

You can check the source code of this application on Github.

Author: Rodrigo

Just another Delphi guy.

6 thoughts on “Changing the glass composition color (DWM) using delphi

  1. Thanks for this beautiful article but here are my thoughts:

    I have been using following code to calculate average color in image loaded in ImageEn and then setting the background color of ImageEn itself. You can achieve this in ImageEn by capturing the background and calculating average color:

    function DynBackCol(Dominante: Boolean): TColor;
    var
      c: TRGB;
      r, g, b: Cardinal;
      p, o, t, x, y: integer;
    begin
      Result := 80 or (80 shl 8) or (80 shl 16);
      Exit;
    
      if Dominante then
      begin
        ieMain.Proc.GetDominantColor(c);
        Result := c.r or (c.g shl 8) or (c.b shl 16);
      end
      else
      begin
        r := 0;
        g := 0;
        b := 0;
        p := integer(ieMain.IEBitmap.Scanline[0]);
        o := integer(ieMain.IEBitmap.Scanline[1]) - p;
        for y := 0 to ieMain.IEBitmap.Height - 1 do
        begin
          for x := 0 to ieMain.IEBitmap.Width - 1 do
          begin
            r := r + PRGB(p + x * 3).r;
            g := g + PRGB(p + x * 3).g;
            b := b + PRGB(p + x * 3).b;
          end;
          p := p + o;
        end;
    
        t := ieMain.IEBitmap.Width * ieMain.IEBitmap.Height;
        if r>0 then
          r := Trunc(r / t);
        if g>0 then
          g := Trunc(g / t);
        if b>0 then
          b := Trunc(b / t);
    
        Result := r or (g shl 8) or (b shl 16);
      end;
    end;
    

    Just my thoughts.

    • Yogi Yan, thanks for you comments and code, the application use a very simple a algorithm to calculate the most used color (not the average color like Aura). but i will check your code to see the results. ;)

  2. Very interesting article Rodrigo, thank you!!

  3. Excellent! Now add some “cross-fading” color effect like Aura! :)

  4. Ohhh Noooo undocumented features!!! *FEAR*
    (If you too read The Old New Thing you should know why. :)

    Jokes aside. Excellent job Rodrigo.
    Good use for IDA too. :D

Leave a comment