前一陣突然想起了9月份電面某公司實習時的二面題,大概就是說怎么用Win32 API實現一個透明的窗口,估計當時我的腦殘答案肯定讓面試官哭笑不得吧。所以本人決定好好研究下這個問題。經過一下午的摸索,基本掌握了使用Win32 API創建各種匪夷所思的窗口的基本思路。
(以下文字基於本人的個人理解,由於本人技術和經驗原因不保證正確性,希望各位不吝指正)
首先我們需要了解一些基礎知識。
1、Layered Windows。這是Windows2000開始引入的新概念,重新定義了窗口的Hit Testing方法,以前都是把窗口按rectangle的方式裁剪,而把窗口加上WS_EX_LAYERED的Style后就可以根據窗口的形狀和像素值進行Hit Testing[1],這樣我們的不規則窗口就變成了真正意義上的獨立窗口,而不是傳統的被一個不可見的矩形窗口所包含。
Layered Windows支持兩種繪制方式,一種是采用UpdateLayeredWindow函數,優點是是一勞永逸,不需要在窗口函數中響應各種重繪事件,缺點嘛大概就是這高科技玩意讓人一時半會接受不了= =另一種方式就是先用SetLayeredWindowAttributes函數設置關於窗口透明度的信息,然后用傳統方式,在窗口函數中響應各種重繪事件。然而我們其實似乎並不需要關注WM_PAINT,只要在WM_CREATE中初始化一下窗口的全局背景(顏色和SetLayeredWindowAttributes所定義的相同),然后在WM_ERASEBKGND中更新一些顏色與SetLayeredWindowAttributes定義的不同的細節區域之處便可。
2、SetWindowRgn函數。這個函數用來定義窗口的區域,我們的不規則形狀由此而來。這個函數和它的朋友們十分強大,不僅可以定義獨立的基本形狀的區域,還可以通過運算來組合已有區域從而產生新的區域。下面的實例就通過CombineRgn函數的幫助來產生了一個孔方兄形狀的窗口。
好了,基本知識我們已經掌握了,下面來看看我做的示例程序的運行效果:
怎么樣,還算比較cool吧。下面是完整代碼:
LRESULT _stdcall WinProc (HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
static HDC hDC = GetWindowDC (hWnd ) ;
static HRGN hRgn = CreateRectRgn ( 120 , 70 , 280 , 230 ) ;
switch (uMsg )
{
case WM_ERASEBKGND :
{
DefWindowProc (hWnd , uMsg , wParam , lParam ) ;
FillRgn (hDC , hRgn , CreateSolidBrush (RGB ( 255 , 165 , 0 ) ) ) ; // Orange
SelectObject (hDC , hRgn ) ;
return 0 ;
}
case WM_CREATE :
{
HRGN hRgn1 = CreateEllipticRgn ( 0 , 0 , 400 , 300 ) ;
HRGN hRgn2 = CreateEllipticRgn ( 150 , 100 , 250 , 200 ) ;
CombineRgn (hRgn1 , hRgn1 , hRgn2 , RGN_XOR ) ;
SetWindowRgn (hWnd , hRgn1 , TRUE ) ;
DeleteObject (hRgn1 ) ;
DeleteObject (hRgn2 ) ;
break ;
}
case WM_LBUTTONDOWN :
{
SendMessage (hWnd , WM_NCLBUTTONDOWN , HTCAPTION , 0 ) ;
break ;
}
case WM_DESTROY :
{
DeleteObject (hRgn ) ;
ReleaseDC (hWnd , hDC ) ;
PostQuitMessage ( 0 ) ;
break ;
}
}
return DefWindowProc (hWnd , uMsg , wParam , lParam ) ;;
}
int _stdcall WinMain (HINSTANCE hInstance , HINSTANCE , LPSTR , BOOL )
{
WNDCLASS wc = { 0 } ;
wc. lpszClassName = L "wndclass" ;
wc. hbrBackground = CreateSolidBrush (RGB ( 255 , 99 , 71 ) ) ;
wc. hIcon = LoadIcon (NULL , IDI_APPLICATION ) ;
wc. hCursor = LoadCursor (NULL , IDC_ARROW ) ;
wc. lpfnWndProc = WinProc ;
RegisterClass ( &wc ) ;
HWND hWnd = CreateWindowExW (WS_EX_LAYERED , L "wndclass" , L "Window" , WS_POPUP |WS_VISIBLE , CW_USEDEFAULT , CW_USEDEFAULT , 400 , 300 , 0 , 0 , hInstance , 0 ) ;
if (hWnd == NULL )
return 1 ;
SetLayeredWindowAttributes (hWnd , NULL , 178 , LWA_ALPHA ) ; // Tomato
MSG msg = { 0 } ;
while (GetMessage ( &msg , 0 , 0 , 0 ) )
{
DispatchMessage ( &msg ) ;
}
return 0 ;
}
參考資料:
[1] MSDN:Layered Windows
[2] WindowsAPI_001:創建一個不規則的窗口的方法(用到Region系列API)
» 轉載請注明來源及鏈接: 未來代碼研究所