1

I'm very confused with GetThemeStream function

HRESULT GetThemeStream(
  _In_  HTHEME    hTheme,
  _In_  int       iPartId,
  _In_  int       iStateId,
  _In_  int       iPropId,
  _Out_ VOID      **ppvStream,
  _Out_ DWORD     *pcbStream,
  _In_  HINSTANCE hInst
);

How to use this function? Which parameter I should pass to the ppvStream?

Update:

I'm trying to use it with delphi, declaration from UxTheme:

function GetThemeStream(hTheme: HTHEME; iPartId: Integer; iStateId: Integer;
  iPropId: Integer; var ppvStream: Pointer; var pcbStream: DWORD;
  hInst: HINST): HResult; stdcall;

var
  h: HTHEME;
  Res: HResult;
  PBuf, PPBuf: Pointer;
  BufSize: Cardinal;


h := OpenThemeData(Handle, 'DWMWINDOW');
if h = 0 then Exit;
PBuf := nil;
PPBuf := @PBuf;
Res := GetThemeStream(h, WP_FRAMELEFT, CBS_HOT, TMT_DISKSTREAM, PPBuf, BufSize, hInstance);
if Res <> S_OK then Exit;

Here I'm have BufSize = 75005, Res = S_OK, but nothing in PBuf (nil), possible I'm incorrect send parameter?

gabr
  • 26,580
  • 9
  • 75
  • 141
Alex Egorov
  • 907
  • 7
  • 26
  • As [documented](https://msdn.microsoft.com/en-us/library/windows/desktop/bb759768.aspx): *"Address of a pointer that receives the stream."* It's an *out* parameter, and upon successful return, the pointer points to the data. – IInspectable Dec 11 '15 at 11:50
  • Yes, I can read documentation, but what this is should be? Binary array or stream data? Any example of usage? – Alex Egorov Dec 11 '15 at 12:04
  • Since the API call returns the size in bytes, you can use a byte pointer (e.g. `unsigned char* pStream = nullptr; GetThemeStream( ..., (VOID**)&pStream, ...) ;` – IInspectable Dec 11 '15 at 12:09
  • Thank you for your sample, based on this I'm trying use in delphi, but nothing received in PPBuf – Alex Egorov Dec 11 '15 at 12:51
  • @AlexEgorov Did you try using the `@PBuf` directly, without using `PPBuf`: `..., TMT_DISKSTREAM, @PBuf, ...` – Marko Popovic Dec 11 '15 at 15:38
  • No, with this declaration @Pbuf usage impossible, got compiler error: E2033 Types of actual and formal var parameters must be identical – Alex Egorov Dec 11 '15 at 17:02

1 Answers1

2

Correct usage of this function, thanks to Andreas Verhoeven, author of the program Vista Style Builder:

var
  hTh: HTHEME;
  hLib: HMODULE;
  DllName: string;
  Png: TPngImage;
  MS: TMemoryStream;
  BufSize: Cardinal;
  PBuf: Pointer;

hTh := OpenThemeData(...);
// DllName is path to the theme file
hLib := LoadLibraryEx(PChar(DllName), 0, LOAD_LIBRARY_AS_DATAFILE);
// Read Theme Png stream
GetThemeStream(hTh, 0, 0, TMT_DISKSTREAM, PBuf, BufSize, hLib);
// Copy data to memory stream
MS := TMemoryStream.Create;
MS.WriteBuffer(PByteArray(PBuf)^[0], BufSize);
MS.Position := 0;
// Copy memory stream to Png
Png := TPngImage.Create;
Png.LoadFromStream(MS);

Update with additional example:

var
  ThemeRectLeft,
  ThemeRectLeftInactive,
  ThemeRectMiddle,
  ThemeRectMiddleInactive,
  ThemeRectCloseRight,
  ThemeRectCloseRightInactive,
  ThemeRectCloseSingle,
  ThemeRectCloseSingleInactive,
  ThemeRectMinIco,
  ThemeRectMaxIco,
  ThemeRectRestoreIco,
  ThemeRectHelpIco,
  ThemeRectCloseIco: TRect;

var
  THEME_MIDDLE_IMAGES         : Integer =  3;
  THEME_MIDDLE_IMAGES2        : Integer =  4; // Inactive Window
  THEME_LEFT_IMAGES           : Integer =  5;
  THEME_LEFT_IMAGES2          : Integer =  6; // Inactive Window
  THEME_CLOSE_RIGHT_IMAGES    : Integer =  7;
  THEME_CLOSE_RIGHT_IMAGES2   : Integer =  8; // Inactive Window
  THEME_CLOSE_SINGLE_IMAGES   : Integer =  9;
  THEME_CLOSE_SINGLE_IMAGES2  : Integer = 10; // Inactive Window
  THEME_CLOSE_HOT_FRAME       : Integer = 11; // Win7, Vista
  THEME_HOT_FRAME             : Integer = 16; // Win7, Vista
  THEME_CLOSE_ICONS           : Integer = 12;
  THEME_HELP_ICONS            : Integer = 16; // Win7, Vista: 17
  THEME_MAX_ICONS             : Integer = 20; // Win7, Vista: 21
  THEME_MIN_ICONS             : Integer = 24; // Win7, Vista: 25
  THEME_RESTORE_ICONS         : Integer = 28; // Win7, Vista: 29
  THEME_CLOSE_SMALL_IMAGES    : Integer = 37; // Win7, Vista: 45
  THEME_CLOSE_SMALL_IMAGES2   : Integer = 38; // Win7, Vista: 46  Inactive Window
  THEME_CLOSE_SMALL_HOT_FRAME : Integer = 47; // Win7, Vista
  THEME_CLOSE_SMALL_ICONS     : Integer = 39; // Win7, Vista: 48

function LoadThemePng(var APng: TPngImage; const APathToSave: string): Boolean;
const
  ThemeRegPath = 'SOFTWARE\Microsoft\Windows\CurrentVersion\ThemeManager';
var
  hTh: HTHEME;
  hLib: HMODULE;
  DllName, Path: string;
  MS: TMemoryStream;
  BufSize: Cardinal;
  PBuf: Pointer;
  I: Integer;
  Rt: TRect;
  imgPng: TPngImage;
begin
  Result := False;
  hTh := OpenThemeData(0, 'DWMWINDOW');
  if hTh <> 0 then
  try
    // Get Library path
    SetLength(DllName, 1024);
    SHRegGetPath(HKEY_CURRENT_USER, PChar(ThemeRegPath), 'DllName', PChar(DllName), 0);
    // Open Library
    hLib := LoadLibraryEx(PChar(DllName), 0, LOAD_LIBRARY_AS_DATAFILE);
    if hLib > 0 then
    try
      // Read Theme Png stream
      if GetThemeStream(hTh, 0, 0, TMT_DISKSTREAM, PBuf, BufSize, hLib) = S_OK then begin
        MS := TMemoryStream.Create;
        try
          MS.WriteBuffer(PByteArray(PBuf)^[0], BufSize);
          MS.Position := 0;
          APng.LoadFromStream(MS);
          Result := True;
        finally
          MS.Free;
        end;
      end;
    finally
      FreeLibrary(hLib);
    end;
    GetThemeRect(hTh, THEME_LEFT_IMAGES, 0, TMT_ATLASRECT, ThemeRectLeft);
    GetThemeRect(hTh, THEME_LEFT_IMAGES2, 0, TMT_ATLASRECT, ThemeRectLeftInactive);
    GetThemeRect(hTh, THEME_MIDDLE_IMAGES, 0, TMT_ATLASRECT, ThemeRectMiddle);
    GetThemeRect(hTh, THEME_MIDDLE_IMAGES2, 0, TMT_ATLASRECT, ThemeRectMiddleInactive);
    GetThemeRect(hTh, THEME_CLOSE_RIGHT_IMAGES,  0, TMT_ATLASRECT, ThemeRectCloseRight);
    GetThemeRect(hTh, THEME_CLOSE_RIGHT_IMAGES2,  0, TMT_ATLASRECT, ThemeRectCloseRightInactive);
    GetThemeRect(hTh, THEME_CLOSE_SINGLE_IMAGES,  0, TMT_ATLASRECT, ThemeRectCloseSingle);
    GetThemeRect(hTh, THEME_CLOSE_SINGLE_IMAGES2,  0, TMT_ATLASRECT, ThemeRectCloseSingleInactive);

    if (Win32MajorVersion = 6) and (Win32MinorVersion in [0, 1]) then begin
      GetThemeRect(hTh, THEME_MIN_ICONS + 1,     0, TMT_ATLASRECT, ThemeRectMinIco);
      GetThemeRect(hTh, THEME_MAX_ICONS + 1,     0, TMT_ATLASRECT, ThemeRectMaxIco);
      GetThemeRect(hTh, THEME_RESTORE_ICONS + 1, 0, TMT_ATLASRECT, ThemeRectRestoreIco);
      GetThemeRect(hTh, THEME_HELP_ICONS + 1,    0, TMT_ATLASRECT, ThemeRectHelpIco);
    end
    else begin
      GetThemeRect(hTh, THEME_MIN_ICONS,     0, TMT_ATLASRECT, ThemeRectMinIco);
      GetThemeRect(hTh, THEME_MAX_ICONS,     0, TMT_ATLASRECT, ThemeRectMaxIco);
      GetThemeRect(hTh, THEME_RESTORE_ICONS, 0, TMT_ATLASRECT, ThemeRectRestoreIco);
      GetThemeRect(hTh, THEME_HELP_ICONS,    0, TMT_ATLASRECT, ThemeRectHelpIco);
    end;

    GetThemeRect(hTh, THEME_CLOSE_ICONS,   0, TMT_ATLASRECT, ThemeRectCloseIco);

    if APathToSave <> '' then begin
      Path := IncludeTrailingPathDelimiter(APathToSave);
      APng.SaveToFile(Path + 'Theme_Full.png');
      for I := 1 to 99 do begin
        imgPng := TPngImage.Create;
        try
          if GetThemeRect(hTh, I, 0, TMT_ATLASRECT, Rt) = S_OK then begin
            CopyPng(APng, imgPng, Rt);
            imgPng.SaveToFile(Path + 'Theme_Img_' + IntToStr(I) + '.png');
          end;
        finally
          imgPng.Free;
        end;
      end;
    end;
  finally
    CloseThemeData(hTh);
  end;
end;
Alex Egorov
  • 907
  • 7
  • 26
  • According to the GetThemeStream function i can use the function without theme file. But for me this always fails. Do you have more samples? – bazz-dee Apr 20 '17 at 12:07
  • Well. At least it returns S_OK and the buffer returned seems to start with a png header, but cannot open it when writing to a file. But i'll check tomorrow. Thanks for now – bazz-dee Apr 20 '17 at 13:41
  • Please note, for Windows XP this is not PNG format, for all newer versions is PNG – Alex Egorov Apr 20 '17 at 16:14
  • Well i just need this for Win8/Win10. So no problem – bazz-dee Apr 21 '17 at 05:53
  • Is there any option to load a `BUTTON` parts using this method? I asked a question here: https://stackoverflow.com/questions/47488359/how-to-save-windows-theme-part-using-getthemestream – zig Nov 26 '17 at 06:53
  • Sure, you can load BUTTON parts, you should can find this parts in Path folder using (imgPng.SaveToFile(Path + 'Theme_Img_' + IntToStr(I) + '.png')) code – Alex Egorov Dec 07 '17 at 13:44
  • @AlexEgorov where did the values for numbers such as `THEME_CLOSE_ICONS` come from? – Mark Ingram Nov 28 '18 at 21:23
  • 1
    @MarkIngram this is my own investigations – Alex Egorov Nov 28 '18 at 21:37
  • @AlexEgorov Thanks - that's what I was afraid of ;-) – Mark Ingram Nov 29 '18 at 10:30