C#迅雷七窗体特效,使用DWM实现Aero Glass效果

来源:互联网 发布:数据挖掘的应用前景 编辑:程序博客网 时间:2024/05/09 23:53

从Windows Vista开始,Aero Glass效果被应用在了Home Premium以上的系统中(Home Basic不具有该效果)。这种效果是由DWM(Desktop Window Manager)来控制的。对于一般的程序,缺省将在窗口边框应用这种效果。但如果我们想要更多的控制,比如让客户区的一部分也呈现这种效果,那也非常的简单。不需要我们在程序里做任何复杂的算法,我们只需要调API,交给DWM去做就可以了。

一、Composition(窗口合成) and Non-client Rendering(非客户区渲染)

       非客户区通常包括窗口标题栏和窗口边框。缺省状态下,非客户区会被渲染成毛玻璃效果,这也称为Compostion。有几个函数可以控制系统和当前窗口的渲染方式。同时也有Windows消息用于接受渲染模式的改变。

       1.检测系统是否开启Aero Glass。使用 函数 DwmIsCompositionEnabled 检测系统当前是否开启了Aero Glass特效。它接受一个BOOL参数,并将当前状态存储到其中。函数原型:HRESULT DwmIsCompositionEnabled(BOOL *pfEnabled );

       2.开启/关闭Aero Glass。使用函数DwmEnableComposition 开启或关闭系统Aero Glass效果,传入DWM_EC_ENABLECOMPOSITION 开启,传入DWM_EC_DISABLECOMPOSITION 关闭。

       3.开启/关闭当前窗口的非客户区渲染。函数DwmSetWindowAttribute 用于设置窗口属性,属性DWMWA_NCRENDERING_POLICY 控制当前窗口是否使用非客户区渲染。DWMNCRP_ENABLED 开启,DWMNCRP_DISABLED 关闭。当系统的Aero Glass关闭时,设置无效。与之对应,使用函数DwmGetWindowAttribute 可以检测当前窗口属性。

       4.响应系统Aero Glass的开启或关闭。当Aero Glass被开启或关闭时,Windows会发送消息WM_DWMCOMPOSITIONCHANGED , 使用 函数 DwmIsCompositionEnabled 检测状态。

       5.响应窗口非客户区渲染的开启或关闭。当前窗口的非客户区渲染开启或关闭时,Windows会发送消息WM_DWMNCRENDERINGCHANGED ,wParam 指示当前状态。

二、Transition(窗口动画) and ColorizationColor(主题颜色)

       Transition控制是否以动画方式显示窗口的最小化和还原。通过使用函数DwmSetWindowAttribute ,设置属性DWMWA_TRANSITIONS_FORCEDISABLED ,开启或关闭窗口动画。该设置只对当前窗口有效。

       当用户通过控制面板修改主题颜色时,Windows将发送消息WM_DWMCOLORIZATIONCOLORCHANGED ,程序中通过函数DwmGetColorizationColor 取得当前主题颜色,以及是否透明。通过响应颜色的变更,可以让程序的颜色风格随主题风格而变化。

三、开启客户区域Aero Glass效果

       函数DwmEnableBlurBehindWindow 开启客户区的Aero Glass效果,第一个参数为窗口句柄,第二个参数为一个DWM_BLURBEHIND 结构。其中fEnable 设置是否开启客户区Glass效果。hRgnBlur 设置Glass效果的区域,该项设置为NULL将使整个客户区呈现Glass效果,设置为一个正确的区域后,该区域将呈现Glass效果, 而区域以外为完全透明。要呈现透明效果需要客户区原始的颜色为黑色,可以在WM_PAINT 消息中绘制客户区,下面的代码使用GDI+,在Aero Glass开启时将整个窗口绘制为黑色,Aero Glass关闭时绘制为灰色:

 

  1. case WM_PAINT:  
  2.     {  
  3.         PAINTSTRUCT ps;  
  4.         HDC hDC = BeginPaint(hWnd, &ps);  
  5.         //不要直接使用窗口句柄创建Graphics,会导致闪烁  
  6.         Graphics graph(hDC);  
  7.         //清除客户区域  
  8.         RECT rcClient;  
  9.         GetClientRect(hWnd, &rcClient);  
  10.         BOOL bCompEnabled;  
  11.         DwmIsCompositionEnabled(&bCompEnabled);  
  12.         SolidBrush br(bCompEnabled? Color::Black : Color::DarkGray);  
  13.         graph.FillRectangle(&br, Rect(rcClient.left, rcClient.top,   
  14.             rcClient.right, rcClient.bottom));  
  15.         EndPaint(hWnd, &ps);  
  16.     }  
  17.     break;  

  [DllImport("dwmapi.dll")]
  static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);

 struct DWM_BLURBEHIND
        {
            public uint dwFlags;
            public bool fEnable;
            public IntPtr hRgnBlur;
            public bool fTransitionOnMaximized;
        }

  DWM_BLURBEHIND blur=new DWM_BLURBEHIND();
            blur.dwFlags = 0x00000001 | 0x00000002;
            blur.fEnable = true;
            DwmEnableBlurBehindWindow(this.Handle, ref blur);

        GDI+的初始化和关闭仍然是必须的:

 ====================================================================

MSDN:

DWM_BLURBEHIND structure

Specifies Desktop Window Manager (DWM) blur-behind properties. Used by the DwmEnableBlurBehindWindow function.

Syntax

C++
typedef struct _DWM_BLURBEHIND {  DWORD dwFlags;  BOOL  fEnable;  HRGN  hRgnBlur;  BOOL  fTransitionOnMaximized;} DWM_BLURBEHIND, *PDWM_BLURBEHIND;

Members

dwFlags

A bitwise combination of DWM Blur Behind constant values that indicates which of the members of this structure have been set.

fEnable

TRUE to register the window handle to DWM blur behind; FALSE to unregister the window handle from DWM blur behind.

hRgnBlur

The region within the client area where the blur behind will be applied. A NULL value will apply the blur behind the entire client area.

fTransitionOnMaximized

TRUE if the window's colorization should transition to match the maximized windows; otherwise, FALSE.

=====================================================

  1. //初始化GDI+  
  2. ULONG_PTR token;  
  3. GdiplusStartupInput input;  
  4. GdiplusStartup(&token, &input, NULL);  
  5. //*********************************  
  6. //关闭GDI+  
  7. GdiplusShutdown(token);  

 

       下面代码将整个客户区设置为Glass效果:

 

  1. DWM_BLURBEHIND bb = {0};  
  2. bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;  
  3. bb.fEnable = true;  
  4. bb.hRgnBlur = NULL;  
  5. DwmEnableBlurBehindWindow(hWnd, &bb);  

 

      下面代码将客户区中心一个椭圆的区域设置为Glass效果:

 

  1. RECT rect;  
  2. GetWindowRect(hWnd, &rect);  
  3. int width = 300, height = 200;  
  4. //居中椭圆形  
  5. HRGN hRgn = CreateEllipticRgn((rect.right - rect.left)/2 - width/2,   
  6.     (rect.bottom - rect.top)/2 - height/2, (rect.right - rect.left)/2 + width/2,   
  7.     (rect.bottom - rect.top)/2 + height/2);  
  8. DWM_BLURBEHIND bb = {0};  
  9. bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;  
  10. bb.fEnable = true;  
  11. bb.hRgnBlur = hRgn;  
  12. DwmEnableBlurBehindWindow(hWnd, &bb);  

 

 

四、窗口边框向客户区扩展

      上面的方式中,非客户区和客户区之间仍然有界限。如何增大Glass效果的范围,并且消除界限呢?那就是使窗口边框向客户区扩展,利用函数DwmExtendFrameIntoClientArea 实现。函数接受一个窗口句柄和一个MARGINS 类型的参数。MARGINS指定了在上下左右4个方向上扩展的范围。如果4个值均为-1,则扩展到整个客户区。

 

  1. MARGINS margins = {50, 50, 50, 50};  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins);  

 

 

  1. MARGINS margins2 = {-1};    //将扩展到整个客户区  
  2. DwmExtendFrameIntoClientArea(hWnd, &margins2);  

 

五、在窗口上绘制图形

      PNG图片带有alpha通道,可以与Aero Glass很好的配合。利用GDI+显示PNG图片非常方便,下面的代码将一张PNG图片加载到内存中:

 

  1. Bitmap bmp  = Bitmap::FromFile(L"Ferrari.png"false);  

 

      在WM_PAINT消息处理中,将整个客户区绘制为黑色以后,利用GDI+将图片绘制到窗口客户区:

 

  1. //绘制图形  
  2. int width = bmp->GetWidth();  
  3. int height = bmp->GetHeight();  
  4. Rect rc(30, 30, width, height);  
  5. graph.DrawImage(bmp, rc, 0, 0, width, height, UnitPixel);  

 

六、文本的绘制

      当窗口大范围的透明之后,窗口上的文字的阅读成了一个问题。Windows的解决办法是为文字加上发光效果(Glowing),标题栏的文本使用的就是这种方式。我们在自己的程序中可以使用DrawThemeTextEx 函数来绘制发光的文字。该函数的原型定义如下:

 

  1. HRESULT DrawThemeTextEx(          HTHEME hTheme,  
  2.     HDC hdc,  
  3.     int iPartId,  
  4.     int iStateId,  
  5.     LPCWSTR pszText,  
  6.     int iCharCount,  
  7.     DWORD dwFlags,  
  8.     LPRECT pRect,  
  9.     const DTTOPTS *pOptions  
  10. );  

 

      hTheme是一个主题句柄,可以使用OpenThemeData 获得, OpenThemeData 函数接受一个窗口句柄,和主题类的名称。iPartId和iStateId分别代表主题类中的Part和State,所有可用的主题类、Part和state在SDK的帮助文档中可以查看到。pszText是要绘制的文本。iCharCount为文字个数,-1代表绘制全部文本。dwFlags指定文本格式。pRect为文本绘制区域。pOptions中可以设定文本的发光、阴影等效果。HDC是一个设备上下文句柄,为了实现类似于标题栏中文本的发光效果,这里不能使用由BeginPaint 得到的句柄,而是要使用CreateCompatibleDC 创建一个内存中的句柄,并且要创建一张位图,通过内存句柄将文本绘制到位图上。然后再将位图转移到窗口上。下面的函数封装了绘制发光文本的过程:

 

  1. //绘制发光文字  
  2. void DrawGlowingText(HDC hDC, LPWSTR szText, RECT &rcArea,   
  3.     DWORD dwTextFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE, int iGlowSize = 10)  
  4. {  
  5.     //获取主题句柄  
  6.     HTHEME hThm = OpenThemeData(GetDesktopWindow(), L"TextStyle");  
  7.     //创建DIB  
  8.     HDC hMemDC = CreateCompatibleDC(hDC);  
  9.     BITMAPINFO bmpinfo = {0};  
  10.     bmpinfo.bmiHeader.biSize = sizeof(bmpinfo.bmiHeader);  
  11.     bmpinfo.bmiHeader.biBitCount = 32;  
  12.     bmpinfo.bmiHeader.biCompression = BI_RGB;  
  13.     bmpinfo.bmiHeader.biPlanes = 1;  
  14.     bmpinfo.bmiHeader.biWidth = rcArea.right - rcArea.left;  
  15.     bmpinfo.bmiHeader.biHeight = -(rcArea.bottom - rcArea.top);  
  16.     HBITMAP hBmp = CreateDIBSection(hMemDC, &bmpinfo, DIB_RGB_COLORS, 0, NULL, 0);  
  17.     if (hBmp == NULL) return;  
  18.     HGDIOBJ hBmpOld = SelectObject(hMemDC, hBmp);  
  19.     //绘制选项  
  20.     DTTOPTS dttopts = {0};  
  21.     dttopts.dwSize = sizeof(DTTOPTS);  
  22.     dttopts.dwFlags = DTT_GLOWSIZE | DTT_COMPOSITED;  
  23.     dttopts.iGlowSize = iGlowSize;  //发光的范围大小  
  24.     //绘制文本  
  25.     RECT rc = {0, 0, rcArea.right - rcArea.left, rcArea.bottom - rcArea.top};  
  26.     HRESULT hr = DrawThemeTextEx(hThm, hMemDC, TEXT_LABEL, 0, szText, -1, dwTextFlags , &rc, &dttopts);  
  27.     if(FAILED(hr)) return;  
  28.     BitBlt(hDC, rcArea.left, rcArea.top, rcArea.right - rcArea.left,   
  29.         rcArea.bottom - rcArea.top, hMemDC, 0, 0, SRCCOPY | CAPTUREBLT);  
  30.     //Clear  
  31.     SelectObject(hMemDC, hBmpOld);  
  32.     DeleteObject(hBmp);  
  33.     DeleteDC(hMemDC);  
  34.     CloseThemeData(hThm);  
  35. }  

 

      在绘制了图形后,加入下面代码绘制一段文本:

 

  1. //绘制文本  
  2. RECT rcText = {10, 10, 300, 40};  
  3. DrawGlowingText(hDC, L"  一点点中文 and some english", rcText);  

 

     因为字体发光的缘故,在文本左侧留下一个空格看起来会舒服一些。效果如下:

七、缩略图关联

       DWM API中还有一个功能,即缩略图关联。它允许我们将一个窗口的缩略图显示到自己窗口的客户区。缩略图不同于截图,它是实时更新的。下面的代码将在窗口客户区显示QQ影音播放器的缩略图:

 

  1. HRESULT hr = S_OK;  
  2. HTHUMBNAIL thumbnail = NULL;  
  3. HWND hWndSrc = FindWindow(_T("QQPlayer Window"), NULL);  
  4. hr = DwmRegisterThumbnail(hWnd, hWndSrc, &thumbnail);  
  5. if (SUCCEEDED(hr))  
  6. {  
  7.     RECT rc;  
  8.     GetClientRect(hWnd, &rc);  
  9.     DWM_THUMBNAIL_PROPERTIES dskThumbProps;  
  10.     dskThumbProps.dwFlags = DWM_TNP_RECTDESTINATION | DWM_TNP_VISIBLE | DWM_TNP_OPACITY ;  
  11.     dskThumbProps.fVisible = TRUE;  
  12.     dskThumbProps.opacity = 200;  
  13.     dskThumbProps.rcDestination = rc;  
  14.     hr = DwmUpdateThumbnailProperties(thumbnail,&dskThumbProps);  
  15. }  

 

       首先通过窗口标题查找到源窗口句柄,然后使用DwmRegisterThumbnail 注册缩略图关联,注册成功后,通过DwmUpdateThumbnailProperties 更新缩略图属性,其中设定了是否可视、透明度以及目标绘制区域。得到下面的效果:

 

源代码下载 

参考资料:

-------------------------------------------------------------------------------------------------------------------------------------------------
数据库优化
数据库教程
数据库实战经验分享博客

百度云下载


百度网盘下载


无损音乐免费下载


分类: c#+Winform,c++/汇编/逆向分析/脱壳破解
0 0