pamiętnik programisty

10 paź, 2009

Przezroczyste okienka z WinAPI

Zamieszczony przez: pejotr w: C++|Programowanie

Przezroczyste okna z winAPIWraz z pojawieniem się systemu Windows 2000, pojawiły się nowe funkcje systemowego API. Jedną z nich jest możliwość tworzenia okien o dowolnym stopniu przezroczystości. Efekt jest dość ciekawy i sporo programów ma możliwość ustawienia przezroczystości własnego okienka. Moim celem było umożliwienie sterowania przezroczystością okien centralnie z jednego programu i to nie zależnie od tego czy okno takie zostało do tego przystosowane czy też nie. Po przeczytaniu tego wpisu, każdy samodzielnie będzie mógł napisać podobną aplikację

Aby istniała możliwość ustawiania przezroczystości dla okna, musi być ono utworzone w sposób do tego przystosowany. Wystarczy skorzystać z innej funkcji tworzącej okno, nie CreateWindow, a CreateWindowEx

HWND CreateWindowEx(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

To co umożliwia później modyfikacje przezroczystości jest ukryte w ramach dodatkowego stylu okna, podawanego poprzez argument dwExStyle. Lista dopuszczalnych wartości jest długa, ale warto się z nią zapoznać. W tym konkretnym przypadku należy zainteresować się wartością WS_EX_LAYERED, utworzone wtedy okno będzie tzw. Layered Window, które obsługuje modyfikacje przezroczystości. Do ustawienia nowej wartości kanału alpha służy SetLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWROD dwFlags) – szczegóły w dokumentacji.
Pełny kod tworzący przykładowe, półprzezroczyste okno:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,	HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
  MSG msg;
  HWND hwnd;
  WNDCLASSEX wcx;

  wcx.cbSize = sizeof(wcx);
  wcx.style = CS_HREDRAW | CS_VREDRAW;
  wcx.lpfnWndProc = WndProc;
  wcx.cbClsExtra = 0;
  wcx.cbWndExtra = 0;
  wcx.hInstance = hInstance;
  wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
  wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  wcx.lpszMenuName = (LPCSTR)"MainMenu";
  wcx.lpszClassName = (LPCSTR)"MainWClass";
  wcx.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(5), IMAGE_ICON,
  GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), (UINT)LR_DEFAULTCOLOR);

  RegisterClassEx(&wcx);

  hwnd = CreateWindowEx(WS_EX_LAYERED, (LPCSTR)"MainWClass", (LPCSTR)"Sample", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, (HWND)NULL,  (HMENU)NULL, hInstance, (LPVOID) NULL);

  ShowWindow(hwnd, nCmdShow);
  SetLayeredWindowAttributes(hwnd, 0, 180, LWA_ALPHA);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, NULL,  0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch(uMsg)
  {
    case WM_CREATE:
      return 0;

    case WM_PAINT:
      return 0;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    default:
      return DefWindowProc(hwnd, uMsg, wParam, lParam);
  }
  return 0;
}

To co należy zrobić, aby kontrolować przezroczystość okien innych niż własne przedstawia się następująco:

  1. 1. Pobrać wszystkie otwarte okna typu top-level
  2. 2. Nadać oknom styl dwExStyle
  3. 3. Zmienić wartość kanału alpha

1. Pobrać wszystkie otwarte okna typu top-level
Dobrym pomysłem wydaje się pobranie uchwytu do rodzica wszystkich okien(Desktop/Pulpit) i przejście w pętli po liście jego dzieci (jak wiadomo każde okno ma kilka przydatnych wskaźników, między innymi do okna-brata). Mogłoby to wyglądać tak:

HWND hDesktop = GetDesktopWindow();
HWND hChild = GetWindow(hDesktop, GW_CHILD);
HWND hTemp;
/* ... */
while(hTemp != GetWindow(hChild, GW_HWNDLAST))
{
  /* ... */
  hTemp = GetWindow(hChild, GW_HWNDNEXT);
}

Pomysł zły ! Okazuje się że taka pętla może działać w nieskończoność. Wzmianaka na temat takiego przypadku znajduje się nawet w dokumentacji. Dlatego należy wykorzystać inny sposób, polegający na użyciu funkcji BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam):

EnumWindows(EnumWindowsProc, NULL);
/* ... */
BOOL CALLBACK EnumWindowsProc(HWND hwndChild, LPARAM lParam)
{
  char buffer[100];

  int count = GetWindowText(hwndChild, buffer, 100);
  if(count && IsWindowVisible(hwndChild))
  {
    /* ... */
  }
  return TRUE;
}

2. Nadać oknom styl dwExStyle
Wydaje się najtrudniejsze, przecież dodatkowy styl należy podać przy tworzeniu okna, a okna które widzimy zostały już przecież utworzone. Na szczęście z pomocą przychodzi funkcja LONG SetWindowLong(HWND hWnd, int iIndex, LONG dwNewLong):

SetWindowLong(hwndChild, GWL_EXSTYLE, WS_EX_LAYERED);

3. Zmienić wartość kanału alpha
Bardziej uważni czytelnicy już na pewno wiedzą jak wygląda zestaw parametrów i jak skorzystać z SetLayeredWindowAttributes. Wykorzystanie pokazane 3 listingi wyżej, przy okazji tworzenia „własnego” przezroczystego okna.

I to tyle tym temacie, wypada jeszcze stworzyć GUI, może obsługę tray’a. Program wydaje się „magiczny” a korzysta raptem z 4 funkcji API systemowego.

Tagi: ,

Brak odpowiedzi na "Przezroczyste okienka z WinAPI"

Formularz komentarza

*

O mnie:

pejotrWitam, nazywam się Piotr Doniec, w internecie występuję pod nickami 'pejotr' oraz 'doniczek'. Obecnie jestem studentem 3 roku informatyki na Politechnice Warszawskiej na wydziale Elektroniki i Technik Informacyjnych.

Kategorie