最近在写一个屏幕下雪的VC程序, 取了一个VC#的类似程序做示范.
VC#的程序中原作者使用了窗体自带的DoubleBuffer属性,但是C++中只有自己实现...
原理大概就是做一个全屏的窗口,设置背景色为透明色,之后调用GDI+绘图,这样就可以在不妨碍用户操作电脑的同时实现下雪效果.
但是现在问题是我在OnPaint()中绘制雪花后窗口闪烁严重,但如果重载OnEraseBkgnd()不调用CDialog::OnEraseBkgnd()就会出现屏幕不刷新的现象,而我又不能用画刷直接刷窗口(窗口是透明的),导致无法使用双缓冲绘图.
所以问题就是要使系统的OnEraseBkgnd在我的MemDC上作图,而我创建的MemDC甚至使我使用它初始化的Graphics类的作图也失效了...
我调试了一下,发现MFC会把我调用CDialog::OnEraseBkgnd()的参数直接替换成消息队列的信息,于是我直接调用了CWnd::DefWndProc,但还是不起作用,雪花也上不去...
求各位帮忙...

解决方案 »

  1.   

    唔~这个问题已经自己解决了,不过还有一个内存泄露的问题要请大家帮忙看看.
    void CSnowDlg::OnPaint() 
    {
    CRect rcWindow;
    GetWindowRect(rcWindow);
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(GetDC()); 
    CBitmap bmpMemory;
    bmpMemory.CreateCompatibleBitmap(GetDC(), rcWindow.Width(), rcWindow.Height());
    dcMemory.SelectObject(&bmpMemory);
    CWnd::DefWindowProc(WM_ERASEBKGND, (WPARAM) dcMemory.m_hDC, 0);
    using namespace Gdiplus;
    Graphics gMemory(dcMemory.m_hDC);
    gMemory.SetSmoothingMode(SmoothingModeAntiAlias);
    EnterCriticalSection(&g_cs);
    for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
    gMemory.ResetTransform();
    gMemory.TranslateTransform(-16, -16, MatrixOrderAppend); //平移
    gMemory.ScaleTransform(iter->Scale, iter->Scale, MatrixOrderAppend); //缩放
    gMemory.RotateTransform(iter->Rotation, MatrixOrderAppend); //旋转
    gMemory.TranslateTransform(iter->X, iter->Y, MatrixOrderAppend); //平移
    gMemory.DrawImage(m_bmpSnowFlake, 0, 0); //绘制
    }
    LeaveCriticalSection(&g_cs);
    GetDC()->BitBlt(rcWindow.left, rcWindow.top, rcWindow.Width(), rcWindow.Height(), &dcMemory, rcWindow.left, rcWindow.top,SRCCOPY);
    bmpMemory.DeleteObject();
    dcMemory.DeleteDC();
    }
    UINT SnowFlakesWorker(LPVOID pParam)
    {
    unsigned long Tick = 0;
    SnowFlake s; while(true) {
    if (g_bExitWorker) {
    return 0;
    }

    Sleep(20);

    ++Tick;

    //Create new snowflakes
    //if (Tick % 3 == 0 && Random() < 0.7)
    if (Tick % 10 == 0 && Random() < 0.7) {
    s.X = Random(-50, GetSystemMetrics(SM_CXSCREEN) + 50);
    s.Y = Random(-20, -7);
    s.XVelocity = (Random() - 0.5) * 2;
    s.YVelocity = (Random() * 3) + 1;
    s.Rotation = Random(0, 359);
    s.RotationVelocity = Random(-3, 3) * 2;
    if (s.RotationVelocity == 0) {
    s.RotationVelocity = 3;
    }

    s.Scale = Random() / 2 + 0.75;
    EnterCriticalSection(&g_cs);
    g_lSnowFlakes.push_back(s);
    LeaveCriticalSection(&g_cs);
    }

    //Delete snowflakes
    EnterCriticalSection(&g_cs);
    for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
    iter->X += iter->XVelocity;
    iter->Y += iter->YVelocity;
    iter->Rotation += iter->RotationVelocity;

    iter->XVelocity += (Random() - 0.5) * 0.7;
    iter->XVelocity = MAX(iter->XVelocity, -2);
    iter->XVelocity = MIN(iter->XVelocity, +2);

    if (iter->YVelocity > GetSystemMetrics(SM_CYSCREEN) + 10) {
    iter = g_lSnowFlakes.erase(iter);
    --iter;
    }
    }
    LeaveCriticalSection(&g_cs);

    //Refresh
    ((CSnowDlg *) pParam)->Invalidate();
    }
    }
      

  2.   

    dcMemory.SelectObject(&bmpMemory);需要:
    CBitmap* pOldBmp = dcMemory.SelectObject(&bmpMemory);
    ....
    dcMemory.SelectObject(pOldBmp);
      

  3.   

    用GetDC之后都要调用ReleaseDC释放DC 而 以上代码都没有调用,并且多次调用GetDC
    另外在OnPaint中最好用CPaintDC
    比如
    void CSnowDlg::OnPaint() 
    {
        CPaintDC dc;    CRect rcWindow;
        GetWindowRect(rcWindow);
        CDC dcMemory;
        dcMemory.CreateCompatibleDC(&dc); 
      
      

  4.   


    请问最后为何要重新调用SelectObject?是被替换的CBitmap导致的内存泄露吗?另外代码已修正为如下,但内存占用还是稳定且缓慢增长...void CSnowDlg::OnPaint() 
    {
    CRect rcWindow;
    CDC *pdcWindow, dcMemory;
    CBitmap bmpMemory;
    GetWindowRect(rcWindow);
    pdcWindow = GetDC();
    dcMemory.CreateCompatibleDC(pdcWindow);
    bmpMemory.CreateCompatibleBitmap(pdcWindow, rcWindow.Width(), rcWindow.Height());
    dcMemory.SelectObject(&bmpMemory);
    CWnd::DefWindowProc(WM_ERASEBKGND, (WPARAM) dcMemory.m_hDC, 0);
    using namespace Gdiplus;
    Graphics gMemory(dcMemory.m_hDC);
    gMemory.SetSmoothingMode(SmoothingModeAntiAlias);
    EnterCriticalSection(&g_cs);
    for (std::list<SnowFlake>::iterator iter = g_lSnowFlakes.begin(); iter != g_lSnowFlakes.end(); ++iter) {
    gMemory.ResetTransform();
    gMemory.TranslateTransform(-16, -16, MatrixOrderAppend); //平移
    gMemory.ScaleTransform(iter->Scale, iter->Scale, MatrixOrderAppend); //缩放
    gMemory.RotateTransform(iter->Rotation, MatrixOrderAppend); //旋转
    gMemory.TranslateTransform(iter->X, iter->Y, MatrixOrderAppend); //平移
    gMemory.DrawImage(m_bmpSnowFlake, 0, 0); //绘制
    }
    LeaveCriticalSection(&g_cs);
    pdcWindow->BitBlt(rcWindow.left, rcWindow.top, rcWindow.Width(), rcWindow.Height(), &dcMemory, rcWindow.left, rcWindow.top,SRCCOPY);
    ReleaseDC(pdcWindow);
    bmpMemory.DeleteObject();
    dcMemory.DeleteDC();
    }