前一阵突然想起了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)