?

Log in

Previous Entry | Next Entry

Долгое время не получалось заставить свой клон таскбара рисоваться корректно под Windows 7 в теме Aero. Он получался каким-то бледным, что особенно хорошо было заметно при установке кислотных цветов для рамок окон.

В результате продолжительной отладки и ковыряний в недрах Explorer.exe удалось наткнуться на замечательную недокументированную функцию SetWindowCompositionAttribute (описание параметров можно посмотреть тут), позволяющую сделать красиво.

Фактически, SetWindowCompositionAttribute является расширением функции DwmSetWindowAttribute и позволяет установить дополнительные атрибуты окна, не доступные через DwmSetWindowAttribute.

Полный диапазон допустимых атрибутов, с которыми умеет работать SetWindowCompositionAttribute, я не выяснял. Мне был нужен только атрибут с числовым значением 16, которое можно было бы назвать, скажем, WCA_CLIENTRENDERING_POLICY (почему именно так, станет понятно из снимков окна, приведенных ниже).
Это атрибут имеет смысл менять у окна, для которого была вызвана функция DwmEnableBlurBehindWindow с взведенным флагом fEnable.

Атрибут DWMWA_NCRENDERING_POLICY влияет на то, как будет рисоваться неклиентская часть окна, тогда как установка WCA_CLIENTRENDERING_POLICY позволяет сделать принудительную отрисовку Blur-эффекта независимо от того, активно окно или нет.

Перейду сразу к снимкам окна, они более понятны, чем объяснения :)
(в качестве цвета рамок был установлен контрастный красный цвет).
Картинки кликабельные.

Атрибут WCA_CLIENTRENDERING_POLICY выключен (состояние по умолчанию):
Неактивное окно
Активное окно
DWMWA_NCRENDERING_POLICY = 0
DWMWA_NCRENDERING_POLICY = 1


А теперь то же самое, но при включенном атрибуте WCA_CLIENTRENDERING_POLICY:
Неактивное окно
Активное окно
DWMWA_NCRENDERING_POLICY = 0
DWMWA_NCRENDERING_POLICY = 1


Очевидно, что при включенном WCA_CLIENTRENDERING_POLICY клиентская часть отрисовывается с Blur-эффектом, и на эту отрисовку не влияет состояние неактивности окна, а также значение атрибута DWMWA_NCRENDERING_POLICY.

Кстати говоря, у системного таскбара оба этих атрибута установлены в 1, что и позволяет таскбару всегда рисоваться ярким.

Метки:

Comments

( 9 комментариев — Оставить комментарий )
aserteam
7 мар, 2015 11:58 (UTC)
Спасибо !
Однако,
Windows 7x64, Delphi XE2, в 32-битном приложении работает в 64-битном нет.

Описал по аналогии с DwmSetWindowAttribute:

TDwmWinCompAttrData = packed record
dwAttribute: DWORD;
pvAttribute: Pointer;
cbAttribute: DWORD;
end;
PDwmWinCompAttrData = ^TDwmWinCompAttrData;

TDwmSetWindowCompositionAttribute = function(hwnd: HWND; pAttrData: PDwmWinCompAttrData): BOOL; stdcall;

a_whiter
7 мар, 2015 15:03 (UTC)
Да, для совместимости с x64 нужно использовать вот такое описание:
  TWinCompAttrData = packed record
    dwAttribute: THandle;
    pvAttribute: Pointer;
    cbAttribute: DWORD;
  end;


Edited at 2015-03-07 15:04 (UTC)
aserteam
8 мар, 2015 15:41 (UTC)
Пробовал аналогичный вариант с NativeInt и по всякому. Ни в какую.
a_whiter
8 мар, 2015 15:52 (UTC)
Функция SetWindowCompositionAttribute при этом возвращает False, правильно? А что при этом сообщает GetLastError?

В том случае, если используете NativeInt или THandle, чему оказывается равен sizeof для записи TWinCompAttrData под x64?
aserteam
8 мар, 2015 16:04 (UTC)
Да.
ERROR_INVALID_HANDLE
NativeInt = 8, Pointer = 8, DWORD = 4, TWinCompAttrData = 20
a_whiter
9 мар, 2015 15:38 (UTC)
Странное дело. Сейчас под рукой нет Win7 x64, проверю еще раз завтра на тестовой системе и напишу о результатах.
aserteam
9 мар, 2015 15:56 (UTC)
Спасибо, тк моя компетентность исчерпана.
a_whiter
10 мар, 2015 03:04 (UTC)
Вот куски кода, которые только что проверил в Win7 x64. Собирал и 32х и 64х битную версию, нормально работали обе.
type
  TWinCompAttrData = packed record
    attribute: THandle;
    pData: Pointer;
    dataSize: ULONG;
  end;
var
  SetWindowCompositionAttribute: function (Wnd: HWND; const AttrData: TWinCompAttrData): BOOL; stdcall = Nil;

procedure SetWindowRenderingPolicy(Wnd: HWND; NC: Integer; CA: boolean);
var
  Policy1: DWORD;
  Policy2: BOOL;
  AttrData: TWinCompAttrData;
begin
  Policy1 := NC;
  DwmSetWindowAttribute(Wnd, DWMWA_NCRENDERING_POLICY, @Policy1, sizeof(Policy1));

  Policy2 := CA;
  with AttrData do
  begin
    attribute := 16;
    pData := @Policy2;
    dataSize := sizeof(Policy2);
  end;
  if not SetWindowCompositionAttribute(Wnd, AttrData) then
    MessageBox(0, pchar(IntToStr(GetLastError)), '', MB_OK);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  BB: TDwmBlurBehind;
begin
  with BB do
  begin
    dwFlags := DWM_BB_ENABLE;
    fEnable := true;
  end;
  DwmEnableBlurBehindWindow(Handle, bb);

  SetWindowRenderingPolicy(Handle, 0, True);
end;

initialization
  SetWindowCompositionAttribute := GetProcAddress(GetModuleHandle(user32), 'SetWindowCompositionAttribute');
end.


Edited at 2015-03-10 03:06 (UTC)
aserteam
10 мар, 2015 16:50 (UTC)
Балда. Получал адрес по ordinal, а он в 64-битах другой.
Win8x86 2281
Win8x64 2287
Спасибо !
( 9 комментариев — Оставить комментарий )