如何实现动态异型窗口?就像Office助手那样。
我知道静态异型窗口可用SetWindowRgn实现,但这个函数使Windows把区域句柄据为己有,既无法使用,又无法关闭,多次调用后资源很快枯竭。请教大虾!

解决方案 »

  1.   

    Win32 API支持创建不规则窗口,现在很多软件使用这个技术,如:QuickTime,Windows Media Player 7等。 使用API创建不规则窗口主要有两种方式,即矢量形状叠加(布尔运算)和位图区域两种。无论哪种方式,都可以实现上面两图的效果。在编写这样的窗口代码前,我们必须清楚要创建的窗口的形状是否复杂,如图片B的窗口很复杂,为极不规则的窗口,很难使用矢量形状叠加的方式创建,那么可以使用区域,指定位图的透明色实现;而图片A是圆角矩形,使用简单的矢量形状叠加即可。之所以这样,是为了运行效率考虑,对于普通的不规则窗口,矢量叠加方式速度比较快。当然,位图区域方式可以创建任何形状的窗口,如果你不需要考虑运行效率(实际两种方式速度差异很小),那么不必考虑矢量形状叠加的办法,直接看位图区域的例子即可。 A.矢量叠加方式创建不规则窗口 例1:简单的窗口区域设定,使用圆角矩形1、HRGN hRGN=CreateRoundRectRgn(0,0,200,200,20,20);//创建圆角矩形区域2、SetWindowRgn(hRGN,TRUE);//将创建的区域设定为窗口区域
    例2:多个区域叠加(布尔运算)1、HRGN hRGN1=CreateRoundRectRgn(0,0,200,200,20,20);//创建圆角矩形区域2、HRGN hRGN2=CreateRectRgn(10,10,20,20);//创建矩形区域3、HRGN hRGN; CombineRgn(hRGN,hRGN1,hRGN2,RGN_DIFF);   //使用区域1减区域2,生成新区域hRGN(可选的参数有加,减,交等,详见MSDN)4、SetWindowRgn(hRGN,TRUE);//将创建的区域设定为窗口区域 B.位图区域方式创建不规则窗口  点击此处下载BitmapToRegion函数代码rgn.zip:1.95 KB (2,002 字节) 1、HBITMAP hBmp=(HBITMAP)LoadImage(应用程序句柄, 位图文件名, IMAGE_BITMAP, 宽度, 高度, LR_LOADFROMFILE);   //载入一个位图,该位图中的某个特定颜色将被作为透明区域处理2、HRGN hRGN=BitmapToRegion(hBmp,RGB(255,0,0),0);//调用位图转换为区域的函数,透明色为红色,第三个参数为公差,为0即只将红色设定为透明色。如果公差为1,意味着与给定透明色的红绿蓝三色数值的差距在1范围内的颜色也将被作为透明色处理。3、SetWindowRgn(hRGN,TRUE);//将创建的区域设定为窗口区域
    说明:关于BitmapToRegion函数,它的功能就是将位图转换为区域,原理来自API: ExtCreateRegion,该函数使用RGNDATA结构中的数据创建RGN,本函数就是分析位图的各个像素点数,根据它们填充RGNDATA结构,然后创建RGN的。
      

  2.   

    void CTransDlg::SetupRegion(CDC *pDC /*对话框窗口DC*/,                          UINT BackBitmapID /*背景位图资源ID*/,                         UINT MaskBitmapID /*区域处理位图资源ID*/,                         COLORREF TransColor /*透明颜色值*/){      CDC                     memDC;      CBitmap         cBitmap;      CBitmap*              pOldMemBmp = NULL;      COLORREF          cl;      CRect                   cRect;      UINT                    x, y;      CRgn                    wndRgn, rgnTemp;       //取得窗口大小      GetWindowRect(&cRect);       //背景位图资源ID       m_BackBitmapID = BackBitmapID       //装载位图      cBitmap.LoadBitmap(MaskBitmapID);       memDC.CreateCompatibleDC(pDC);      pOldMemBmp = memDC.SelectObject(&cBitmap);      //首先创建默认的完整区域为完整的窗口区域       wndRgn.CreateRectRgn(0, 0, cRect.Width(), cRect.Height());              //下面的两层循环为检查背景位图象素颜色,进行透明区域处理;              //当象素颜色为指定的透明值时,即将该点从区域中剪裁掉。              //其中用到的几个成员变量m_MaskLeftOff、m_MaskTopOff、//m_MaskRightOff、m_MaskBottomOff、m_FrameWidth//和m_CaptionHeight,其作用后面再作说明,此时可全部当作0来处理。      for(x= m_FrameWidth+m_MaskLeftOff;           x<=cRect.Width() - m_FrameWidth-m_MaskRightOff; x++){for(y = m_CaptionHeight+m_MaskTopOff; 
               y<=cRect.Height() - m_FrameWidth-m_MaskBottomOff; y++){                     //取得坐标处象素的颜色值                    cl = memDC.GetPixel(x - m_FrameWidth-m_MaskLeftOff, y - m_CaptionHeight-m_MaskTopOff);                    if(col == TransColor)                    {                            //象素颜色为指定的透明色,创建透明“微区域”                           rgnTemp.CreateRectRgn(x, y, x+1, y+1);                            //“扣像”,从完整的区域中“扣除”透明的“微区域”                           wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_XOR);                            //删除刚创建的透明“微区域”,释放系统资源                           rgnTemp.DeleteObject();                           }             }      }      if (pOldMemBmp) memDC.SelectObject(pOldMemBmp);              //用设定窗口为指定的区域      SetWindowRgn((HRGN)wndRgn, TRUE);}重置系统默认的背景擦除操作,即添加WM_ERASEBKGND消息处理过程,这一步可以借助ClassWizard来简化操作。BOOL CTransDlg::OnEraseBkgnd(CDC* pDC) {       // TODO: Add your message handler code here and/or call default       CRect            rect;       CDC             memDC;       CBitmap  cBitmap;       CBitmap*       pOldMemBmp = NULL;       GetWindowRect(&rect);       //装载背景位图       cBitmap.LoadBitmap(m_BackBitmapID);       memDC.CreateCompatibleDC(pDC);       pOldMemBmp = memDC.SelectObject(&cBitmap);              //将背景位图复制到窗口客户区       pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);       if (pOldMemBmp) memDC.SelectObject( pOldMemBmp );       //删除系统却省的OnEraseBkgnd功能       //return CDialog::OnEraseBkgnd(pDC);       return TRUE;}接下来是在WM_PAINT的消息处理函数OnPaint()中添加代码。由于当背景位图比较大时,进行区域处理比较耗时,所以只在启动时进行一次处理。一种方法是OnInitDialog()处理,但这样会在从启动程序到窗口出现有相当的延迟,易引起程序尚未启动的误解。再一种方法就是在OnPaint()处理,但为了避免重复处理,可以加上一个判断标志。以下是OnPaint()的代码,正体为AppWizard生成,粗体为自己添加内容。void CTransDlg::OnPaint() {       if (IsIconic())       {              ……       }       else       {              if(m_nFirstRun){ //首次运行标志           //修改鼠标光标为等待方式                     BeginWaitCursor();                     //设置背景区域                     SetupRegion(GetWindowDC(),
                               IDB_BACKBMP, 
                               IDB_BACKBMP,
                               0x00FFFFFF /*白色*/);           //恢复鼠标光标为正常模式                     EndWaitCursor();                     m_nFirstRun = 0;              }              CDialog::OnPaint();       }}
      

  3.   

    你去软件区下载例子吧。徐景周的kissme代码
    #if !defined(AFX_TRANSPARENTWND_H__6508F000_5685_11D3_9001_CBBD225E6BC4__INCLUDED_)
    #define AFX_TRANSPARENTWND_H__6508F000_5685_11D3_9001_CBBD225E6BC4__INCLUDED_#if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // TransparentWnd.h : header file
    ///////////////////////////////////////////////////////////////////////////////
    // TransparentWnd windowclass TransparentWnd : public CWnd
    {
    // Construction
    public:
    TransparentWnd(); void CreateTransparent(LPCTSTR pTitle, RECT &rect);
    void SetupRegion(CDC *pDC);
    void DoChange(void); CBitmap m_bmpDraw;
        int m_iAniSeq;// Attributes
    public:// Operations
    public:// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(TransparentWnd)
    //}}AFX_VIRTUAL// Implementation
    public:
    virtual ~TransparentWnd(); // Generated message map functions
    protected:
    //{{AFX_MSG(TransparentWnd)
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnTimer(UINT nIDEvent);
    afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
    afx_msg void OnHelp();
    afx_msg void OnExit();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    };///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_TRANSPARENTWND_H__6508F000_5685_11D3_9001_CBBD225E6BC4__INCLUDED_)
      

  4.   

    // TransparentWnd.cpp : implementation file
    //
    // Modified by jingzhou xu. 2000.12#include "stdafx.h"
    #include "KissMe.h"
    #include "TransparentWnd.h"
    #include "BCMenu.h"  //添加位图菜单
    #include "KissMeDlg.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endifBCMenu popmenu;  //定义位图菜单变量
    /////////////////////////////////////////////////////////////////////////////
    // TransparentWndTransparentWnd::TransparentWnd()
    {
    m_iAniSeq=0; //图像变化初始值
    }TransparentWnd::~TransparentWnd()
    {
    }BEGIN_MESSAGE_MAP(TransparentWnd, CWnd)
    //{{AFX_MSG_MAP(TransparentWnd)
    ON_WM_LBUTTONDOWN()
    ON_WM_CREATE()
    ON_WM_ERASEBKGND()
    ON_WM_TIMER()
    ON_WM_RBUTTONDOWN()
    ON_COMMAND(IDR_HELP, OnHelp)
    ON_COMMAND(IDR_EXIT, OnExit)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()//********************************************************************************
    //* CreateTransparent()
    //*
    //* Creates the main application window transparent
    //********************************************************************************
    void TransparentWnd::CreateTransparent(LPCTSTR pTitle, RECT &rect)
    {
    // 创建一个隐藏窗口
    CreateEx(   0,  
    AfxRegisterWndClass(0,AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
    pTitle,
    WS_POPUP ,
    rect,
    NULL,
    NULL,
        NULL);
    DoChange();
    }//********************************************************************************
    //* SetupRegion()
    //*
    //* Set the Window Region for transparancy outside the mask region
    //********************************************************************************
    void TransparentWnd::SetupRegion(CDC *pDC)
    {
    CDC memDC;
    CBitmap &cBitmap=m_bmpDraw;
    CBitmap* pOldMemBmp = NULL;
    COLORREF col,colMask;
    CRect cRect;
    int x, y;
    CRgn wndRgn, rgnTemp; GetWindowRect(&cRect);
    CPoint ptOrg=cRect.TopLeft(); BITMAP bmInfo;
    cBitmap.GetObject(sizeof(bmInfo),&bmInfo);
    CRect rcNewWnd=CRect(ptOrg,CSize(bmInfo.bmWidth,bmInfo.bmHeight)); memDC.CreateCompatibleDC(pDC);
    pOldMemBmp = memDC.SelectObject(&cBitmap);
    colMask=memDC.GetPixel(0,0); wndRgn.CreateRectRgn(0, 0, rcNewWnd.Width(), rcNewWnd.Height());
    for(x=0; x<=rcNewWnd.Width(); x++)
    {
    for(y=0; y<=rcNewWnd.Height(); y++)
    {
    col = memDC.GetPixel(x, y);
    if(col == colMask)
    {
    rgnTemp.CreateRectRgn(x, y, x+1, y+1);
    wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_XOR);
    rgnTemp.DeleteObject();
    }
    }
    }
    if (pOldMemBmp) memDC.SelectObject(pOldMemBmp);
    SetWindowRgn((HRGN)wndRgn, TRUE);
    MoveWindow(rcNewWnd);
    }void TransparentWnd::DoChange(void)
    {
      char szBmp[20];
      
       //不断替换图像
       sprintf(szBmp,"IDB_KISSME%d",m_iAniSeq%6+1); m_bmpDraw.DeleteObject();
    m_bmpDraw.LoadBitmap(szBmp);
    CWindowDC dc(this);
    SetupRegion(&dc);
    Invalidate();
    }void TransparentWnd::OnLButtonDown(UINT nFlags, CPoint point) 
    { CWnd::OnLButtonDown(nFlags, point); //实现无标题拖动
    PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y)); 
    }int TransparentWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
    if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

        SetTimer(1,300,NULL);  //开始时的图像显示时间

    SetWindowPos(&wndTopMost,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE); //窗体总在总前面
    return 0;
    }BOOL TransparentWnd::OnEraseBkgnd(CDC* pDC) 
    {
        CRect rect;
    GetWindowRect(&rect); CDC memDC;
    CBitmap &cBitmap=m_bmpDraw;;
    CBitmap* pOldMemBmp = NULL;
    CFont* pOldMemFont=NULL; memDC.CreateCompatibleDC(pDC);
    pOldMemBmp = memDC.SelectObject(&cBitmap);
    pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); if (pOldMemBmp) memDC.SelectObject( pOldMemBmp ); return TRUE;
    // return CWnd::OnEraseBkgnd(pDC);
    }void TransparentWnd::OnTimer(UINT nIDEvent) 
    {
       switch(nIDEvent)
    {
    case(1)://变换图像
    DoChange();
    break;
    default:
    break;
    }
    m_iAniSeq++; 

    CWnd::OnTimer(nIDEvent);
    }void TransparentWnd::OnRButtonDown(UINT nFlags, CPoint point) 
    {

    CWnd::OnRButtonDown(nFlags, point);   //添加右键快捷图标菜单(用于下拉式菜单)
      popmenu.LoadMenu(IDR_MENU);
     
      popmenu.ModifyODMenu(NULL, IDR_HELP,IDB_HELP);
      popmenu.ModifyODMenu(NULL,IDR_EXIT,IDB_EXIT);
      
      ClientToScreen(&point);
      BCMenu *psub = (BCMenu *)popmenu.GetSubMenu(0); 
      psub->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);
      popmenu.DestroyMenu();
    }void TransparentWnd::OnHelp() 
    {
    CKissMeDlg dlg;  //此对话框只显示一次
      if( !FindWindow(NULL,"帮助"))
        dlg.DoModal();
    }void TransparentWnd::OnExit() 
    {
      DestroyWindow();
    }
      

  5.   

    主程序// KissMe.h : main header file for the KISSME application
    //#if !defined(AFX_KISSME_H__8748CF84_5673_11D3_9001_DBE596190CF8__INCLUDED_)
    #define AFX_KISSME_H__8748CF84_5673_11D3_9001_DBE596190CF8__INCLUDED_#if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000#ifndef __AFXWIN_H__
    #error include 'stdafx.h' before including this file for PCH
    #endif#include "resource.h" // main symbols/////////////////////////////////////////////////////////////////////////////
    // CKissMe:
    // See KissMe.cpp for the implementation of this class
    //class CKissMe : public CWinApp
    {
    public:
    CKissMe();// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CKissMe)
    public:
    virtual BOOL InitInstance();
    //}}AFX_VIRTUAL// Implementation //{{AFX_MSG(CKissMe)
    // NOTE - the ClassWizard will add and remove member functions here.
    //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    };
    ///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_KISSME_H__8748CF84_5673_11D3_9001_DBE596190CF8__INCLUDED_)
      

  6.   

    // KissMe.cpp : Defines the class behaviors for the application.
    //
    // 软件名称:KissMe
    // 作者:徐景周
    // 功能:透明动态位图显示
     
    #include "stdafx.h"
    #include "KissMe.h"
    #include "KissMeDlg.h"
    #include "TransparentWnd.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif/////////////////////////////////////////////////////////////////////////////
    // CKissMeBEGIN_MESSAGE_MAP(CKissMe, CWinApp)
    //{{AFX_MSG_MAP(CKissMe)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG
    ON_COMMAND(ID_HELP, CWinApp::OnHelp)
    END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
    // CKissMe constructionCKissMe::CKissMe()
    {
    // TODO: add construction code here,
    // Place all significant initialization in InitInstance
    }/////////////////////////////////////////////////////////////////////////////
    // The one and only CKissMe objectCKissMe theApp;/////////////////////////////////////////////////////////////////////////////
    // CKissMe initializationBOOL CKissMe::InitInstance()
    {
    srand(time(NULL));
        
    //一次只运行一个程序实例,如果已运行则退出
    if( FindWindow(NULL,"KissMe,Darling!")) exit(0); TransparentWnd* pFrame = new TransparentWnd;
    m_pMainWnd = pFrame;
        
    // create and load the frame with its resources
    CRect rect(0, 0, 100, 100);
    pFrame->CreateTransparent("KissMe,Darling!", rect);//IDB_MASK, IDB_BACK);
    pFrame->CenterWindow();  //初始显示窗体位置
    pFrame->ShowWindow(SW_SHOW);

    return TRUE;
    }