【发布时间】:2012-09-12 08:06:55
【问题描述】:
我正在尝试重新制作用 C++ 和 WinAPI 编写的 Windows 屏幕保护程序,以便在多个显示器上工作。我找到了提供基础知识的this article。但是当我在自己的代码中实现它时,我得到了一个奇怪的结果。看看这段代码:
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdcE = BeginPaint(hWnd, &ps );
EnumDisplayMonitors(hdcE, NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
}
break;
BOOL CALLBACK MyPaintEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdc1, // handle to monitor DC
LPRECT lprcMonitor, // monitor intersection rectangle
LPARAM data // data
)
{
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
if(GetMonitorInfo(hMonitor, &mi))
{
//Is it a primary monitor?
BOOL bPrimary = mi.dwFlags & MONITORINFOF_PRIMARY;
DoDrawing(bPrimary, hdc1, &mi.rcMonitor);
}
return 1;
}
void DoDrawing(BOOL bPrimaryMonitor, HDC hDC, RECT* pRcMonitor)
{
//#define DIRECT_PAINT //Comment out for double-buffering
int nMonitorW = abs(pRcMonitor->right - pRcMonitor->left);
int nMonitorH = abs(pRcMonitor->bottom - pRcMonitor->top);
HDC hMemDC = ::CreateCompatibleDC(hDC);
if(hMemDC)
{
HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDC, nMonitorW, nMonitorH);
if(hMemBmp)
{
HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hMemBmp);
COLORREF clr, clrBorder;
if(bPrimaryMonitor)
{
clr = RGB(0, 128, 0); //Green
clrBorder = RGB(255, 0, 0);
}
else
{
clr = RGB(128, 0, 0); //Red
clrBorder = RGB(0, 255, 0);
}
RECT rcRect;
#ifndef DIRECT_PAINT
//With double-buffering
rcRect.left = 0;
rcRect.top = 0;
rcRect.right = nMonitorW;
rcRect.bottom = nMonitorH;
#else
rcRect = *pRcMonitor;
#endif
HBRUSH hBrush = ::CreateSolidBrush(clr);
#ifndef DIRECT_PAINT
//With double-buffering
::FillRect(hMemDC, &rcRect, hBrush);
#else
::FillRect(hDC, &rcRect, hBrush);
#endif
#ifndef DIRECT_PAINT
//With double-buffering
::BitBlt(hDC, pRcMonitor->left, pRcMonitor->top, nMonitorW, nMonitorH, hMemDC, 0, 0, SRCCOPY);
#endif
//Debugging output
CString _s;
_s.Format(_T("%s\n")
_T("%s\n")
_T("hDC=0x%X\n")
_T("hMemDC=0x%X\n")
_T("RcMonitor: L=%d, T=%d, R=%d, B=%d")
,
bPrimaryMonitor ? _T("Primary") : _T("Secondary"),
#ifndef DIRECT_PAINT
_T("Double-buffering"),
#else
_T("Direct paint"),
#endif
hDC,
hMemDC,
pRcMonitor->left,
pRcMonitor->top,
pRcMonitor->right,
pRcMonitor->bottom);
::DrawText(hDC, _s, _s.GetLength(), pRcMonitor, DT_NOCLIP | DT_NOPREFIX);
SelectObject(hMemDC, hOldBmp);
::DeleteObject(hMemBmp);
}
::DeleteDC(hMemDC);
}
}
绘画总是在主显示器上工作。但是当我画到辅助显示器时,我只能直接画到它的 DC。当我使用双缓冲技术(将 DIRECT_PAINT 预处理器指令注释掉)时,我只会在应该是红色的辅助显示器上看到黑屏。
我在这里附上两张截图。
第一个使用直接绘画的作品:
然后是双缓冲失败的那个:
任何想法我在这里做错了什么?
【问题讨论】:
-
您是否检查过
FillRect()和/或BitBlt()是否返回错误? -
刚刚检查过,它们在屏幕保护程序生命周期的任何时候都没有返回 0。
-
大多数 GDI 函数在返回错误方面完全不可靠。几乎不值得检查。
-
想一想,
MyPaintEnumProc中的 hdc1 始终为 NULL。所以,你更失败了。 -
也检查过,hdc1 不为 NULL。但我完全同意,那些 GDI 功能完全不可靠!
标签: c++ winapi gdi screensaver doublebuffered