Windows从windows2000开始,提供了一种叫做Layered的窗口风格,通过使用这种窗口风格,我们就可以创建透明的窗口或者半透明的窗口,在这里我将使用一个比较简单的例子来说明如何使用这种新的窗口风格。
首先我们说明一下windows API对层窗口的支持情况。在创建窗口的时候可以直接使用CreateWindowEx来创建层窗口,或者在创建了普通的窗口之后使用SetWindowLong()来修改已有的窗口的风格. Windows层窗口的窗口风格是WS_EX_LAYERED,创建了层窗口的同时,你也就创建了透明窗口, 因为层窗口会同时设置透明窗口的风格。
在我们创建了层窗口之后,我们可以调用API SetLayeredWindowAttributes()来设置层窗口的属性,这个API函数的原型是这样的
BOOL SetLayeredWindowAttributes(
HWND hwnd, // handle to the layered window
COLORREF crKey, // specifies the color key
BYTE bAlpha, // value for the blend function
DWORD dwFlags // action
);
这个函数的主要功能就是设置层窗口的缺省的属性,第一个参数hwnd就是我们要设置的层窗口的句柄,第二个参数crKey是我们要指定的一个色彩的关键值,这个值在第4个参数设置为LWA_COLORKEY的时候有效,这个参数的主要目的是过滤掉这里指定的颜色,比如我们在这里把这个颜色设置成白色,那么当我们向窗口上画白色的时候, 所有的白色都会被滤掉,这样画白色的地方就变得透明了。第3个参数bAlpha是我们指定的用来设置blend功能的,这个参数在第4个参数设置成LWA_ALPHA的时候有效。如果设置了这个参数, 我们就可以创造一种半透明的窗口效果。
在使用COLORKEY方式的时候,我们可以得到一种透明的窗口,而且在这种情况下,所有透明的地方,都不会接受鼠标消息,鼠标消息都会被直接发送到层窗口下面的窗口上。
关于层窗口另一个比较重要的API是UpdateLayeredWindow(),这个函数的原型如下:
BOOL UpdateLayeredWindow(
HWND hwnd, // handle to layered window
HDC hdcDst, // handle to screen DC
POINT *pptDst, // new screen position
SIZE *psize, // new size of the layered window
HDC hdcSrc, // handle to surface DC
POINT *pptSrc, // layer position
COLORREF crKey, // color key
BLENDFUNCTION *pblend, // blend function
DWORD dwFlags // options
);
这个函数看起来好像参数很多,其实和BitBlt函数差不多,不过它是用在层窗口上,其中的crKey和SetLayeredWindowAttributes中的crKey是一样的,pblend是一个结构,其中的内容也是很简单,这里不作介绍了
COLORKEY和ALPHA方式同时只能有一个有效,所以dwFlags就是用来决定这个的,和SetLayeredWindowAttributes不一样的是这个参数有三个选项可以使用ULW_ALPHA,ULW_COLORKEY,ULW_OPAQUE,前两个的作用和SetLayeredWindowAttributes一样,第三个就是指定这个窗口不透明。
SetLayeredWindowAttributes和UpdateLayeredWindow的另一个不同是SetLayeredWindowAttributes是在窗口创建之后调用一次就可以了,而UpdateLayeredWindow是用来处理windows的PAINT事件的。
在本文这个例子里我创建了一个时钟程序,这个程序除了表盘上的刻度和3个指针之外其他的地方都是透明的,而且一般情况下不会影响到你正常的应用操作。这个例子使用了ATL, 另外需要支持windows2000的sdk才能编译过去。
#define WINVER 0x0500
#define _WIN32_WINNT 0x0500
#define _WIN32_IE 0x0501
#define _RICHEDIT_VER 0x0200
#include <atlbase.h>
extern CComModule _Module;
#include <atlwin.h>
#include "stdatl.h"
#include <stdio.h>
#include <math.h>
#include <time.h>
#define PI 3.1415926
#define ID_CLOCKTIMER 101
CComModule _Module;
class CClockWindow : public CWindowImpl<CClockWindow, CWindow,
CWinTraits<WS_POPUP, WS_EX_LAYERED|WS_EX_TOOLWINDOW> >
{
BEGIN_MSG_MAP( CClockWindow )
MESSAGE_HANDLER(WM_TIMER, OnTimer)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER( WM_PAINT, OnPaint )
MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
END_MSG_MAP()
LRESULT OnNcHitTest(UINT, WPARAM, LPARAM, BOOL&)
{
return HTCAPTION;
}
LRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL& )
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint( &ps );
RECT rc;
GetWindowRect(&rc);
SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC, 1000, 1000, NULL);
SetViewportOrgEx(hDC, (rc.right - rc.left) / 2, (rc.bottom - rc.top) / 2, NULL);
SetViewportExtEx(hDC, rc.right - rc.left, - (rc.bottom - rc.top), NULL);
DrawClock(hDC);
EndPaint( &ps );
return 0;
}
LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& )
{
PostQuitMessage( 0 );
return 0;
}
LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
SetTimer(ID_CLOCKTIMER, 500);
return 0;
}
LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)
{
HDC hDC = this->GetDC();
RECT rc;
GetWindowRect(&rc);
SetMapMode(hDC, MM_ISOTROPIC);
SetWindowExtEx(hDC, 1000, 1000, NULL);
SetViewportOrgEx(hDC, (rc.right - rc.left) / 2, (rc.bottom - rc.top) / 2, NULL);
SetViewportExtEx(hDC, rc.right - rc.left, - (rc.bottom - rc.top), NULL);
DrawClock(hDC);
ReleaseDC(hDC);
return 0;
}
void DrawClockSurface(HDC hDC)
{
HBRUSH hBkBrush = ::CreateSolidBrush(RGB(255, 255, 255));
HBRUSH hOldBrush = (HBRUSH)::SelectObject(hDC, hBkBrush);
::SetBkMode(hDC, TRANSPARENT);
::Ellipse(hDC, -510, -510, 510, 510);
int nRadius = 500 - 50;
int x, y;
HBRUSH hPointBrush = ::CreateSolidBrush(RGB(0, 0, 0));
::SelectObject(hDC, hPointBrush);
::DeleteObject(hBkBrush);
for (int i = 0; i < 60; i ++)
{
x = (int)(nRadius * sin((2 * i * PI) / 60));
y = (int)(nRadius * cos((2 * i * PI) / 60));
::Ellipse(hDC, x - 10, y - 10, x + 10, y + 10);
}
for (int i = 0; i < 12; i ++)
{
x = (int)(nRadius * sin((2 * i * PI) / 12));
y = (int)(nRadius * cos((2 * i * PI) / 12));
::Ellipse(hDC, x - 20, y - 20, x + 20, y + 20);
}
int nHour, nMin, nSec;
GetCurTime(nHour, nMin, nSec);
char sTime[20];
ZeroMemory(sTime, sizeof(sTime));
sprintf(sTime, "%02d:%02d:%02d", nHour, nMin, nSec);
SIZE sz;
::GetTextExtentPoint(hDC, sTime, (int)strlen(sTime), &sz);
::TextOut(hDC, -sz.cx / 2 - 1, -300, sTime, (int)strlen(sTime));
::SelectObject(hDC, hOldBrush);
::DeleteObject(hPointBrush);
}
void DrawClockPointer(HDC hDC)
{
int nHour, nMin, nSec;
GetCurTime(nHour, nMin, nSec);
int x1, y1, x2, y2;
//Draw Hour Pointer
HPEN hHourPen = ::CreatePen(PS_SOLID, 20, RGB(0, 0, 0));
HPEN hOldPen = (HPEN)::SelectObject(hDC, hHourPen);
int nRadius = 500 - 240;
x1 = (int)(nRadius * cos(PI / 2 - (2 * nHour * 5 * PI + nMin * PI / 6) / 60));
y1 = (int)(nRadius * sin(PI / 2 - (2 * nHour * 5 * PI + nMin * PI / 6) / 60));
nRadius = 40;
x2 = (int)(nRadius * cos(3 * PI / 2 - (2 * nHour * 5 * PI + nMin * PI / 6) / 60));
y2 = (int)(nRadius * sin(3 * PI / 2 - (2 * nHour * 5 * PI + nMin * PI / 6) / 60));
::MoveToEx(hDC, x1, y1, NULL);
::LineTo(hDC, x2, y2);
//Draw minute pointer
HPEN hMinPen = ::CreatePen(PS_SOLID, 12, RGB(0, 0, 0));
::SelectObject(hDC, hMinPen);
::DeleteObject(hHourPen);
nRadius = 500 - 140;
x1 = (int)(nRadius * cos(PI / 2 - (2 * nMin * PI) / 60));
y1 = (int)(nRadius * sin(PI / 2 - (2 * nMin * PI) / 60));
nRadius = 80;
x2 = (int)(nRadius * cos(3 * PI / 2 - (2 * nMin * PI) / 60));
y2 = (int)(nRadius * sin(3 * PI / 2 - (2 * nMin * PI) / 60));
::MoveToEx(hDC, x1, y1, NULL);
::LineTo(hDC, x2, y2);
//Draw Second pointer
::SelectObject(hDC, hOldPen);
::DeleteObject(hMinPen);
nRadius = 500 - 75;
x1 = (int)(nRadius * cos(PI / 2 - (2 * nSec * PI) / 60));
y1 = (int)(nRadius * sin(PI / 2 - (2 * nSec * PI) / 60));
x2 = 0;
y2 = 0;
::MoveToEx(hDC, x1, y1, NULL);
::LineTo(hDC, x2, y2);
}
void DrawClock(HDC hDC)
{
HDC hMemDC = ::CreateCompatibleDC(hDC);
RECT rc;
GetWindowRect(&rc);
SetMapMode(hMemDC, MM_ISOTROPIC);
SetWindowExtEx(hMemDC, 1000, 1000, NULL);
SetViewportOrgEx(hMemDC, (rc.right - rc.left) / 2, (rc.bottom - rc.top) / 2, NULL);
SetViewportExtEx(hMemDC, rc.right - rc.left, - (rc.bottom - rc.top), NULL);
HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hMemDC, hBitmap);
DrawClockSurface(hMemDC);
DrawClockPointer(hMemDC);
::BitBlt(hDC, -500, -500, 1000, 1000, hMemDC, -500, -500, SRCCOPY);
SelectObject(hMemDC, hOldBitmap);
::DeleteObject(hBitmap);
::DeleteDC(hMemDC);
return;
}
void GetCurTime(int& nHour, int& nMin, int& nSec)
{
struct tm* tmCurrent;
time_t t;
t = time(NULL);
tmCurrent = localtime(&t);
nHour = tmCurrent->tm_hour;
nMin = tmCurrent->tm_min;
nSec = tmCurrent->tm_sec;
return;
}
};
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
{
_Module.Init( NULL, hInstance );
CClockWindow wnd;
wnd.Create( NULL, CWindow::rcDefault, _T("ClockToy"),
WS_POPUP );
//wnd.ModifyStyleEx(0,WS_EX_TRANSPARENT /*| WS_EX_LAYERED*/);
CRgn rgn;
rgn.CreateEllipticRgn(0, 0, 180, 180);
wnd.SetWindowRgn(rgn.m_hRgn, TRUE);
wnd.SetWindowPos(HWND_TOPMOST, 0, 0, 180, 180, SWP_NOMOVE);
wnd.MoveWindow(100, 100, 180, 180, TRUE);
::SetLayeredWindowAttributes(wnd.m_hWnd, RGB(255, 255, 255), 0, LWA_COLORKEY);
wnd.CenterWindow();
wnd.ShowWindow(SW_SHOW);
wnd.UpdateWindow();
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) ){
TranslateMessage( &msg );
DispatchMessage( &msg );
}
_Module.Term();
return (int)msg.wParam;
}
contact me at younker@yeah.net