C++ Win32屏幕保护程序主显示器不是最左侧的多个监视器

C++ Win32屏幕保护程序主显示器不是最左侧的多个监视器,c++,winapi,screensaver,C++,Winapi,Screensaver,我制作了一个屏幕保护程序,它可以简单地从右向左滚动用户定义的文本,如果文本超出左边界,就会自动跳回右侧 它可以完美地与多个监视器协同工作,除非有一个例外:如果“主显示器”位于右侧(即监视器#2为主显示器),则我不会获得滚动文本,但监视器会被代码遮住。如果主显示为#1,则没有问题 我已经仔细研究了几个小时的代码,无法确定问题是在什么阶段出现的;我可以确认文本位于正确的位置(我插入了验证其当前位置的日志代码),但这就好像一个API调用只是将其删除一样。我已经阅读了他们的文档,看起来都不错 我通过以下

我制作了一个屏幕保护程序,它可以简单地从右向左滚动用户定义的文本,如果文本超出左边界,就会自动跳回右侧

它可以完美地与多个监视器协同工作,除非有一个例外:如果“主显示器”位于右侧(即监视器#2为主显示器),则我不会获得滚动文本,但监视器会被代码遮住。如果主显示为#1,则没有问题

我已经仔细研究了几个小时的代码,无法确定问题是在什么阶段出现的;我可以确认文本位于正确的位置(我插入了验证其当前位置的日志代码),但这就好像一个API调用只是将其删除一样。我已经阅读了他们的文档,看起来都不错

我通过以下方式在WM_create中创建自定义DC:

if (( hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL)) == NULL )
为了防止闪烁,我创建了要更新的兼容对象:

void
TickerScreensaver::Paint_Prep(HDC hDC)
{
    _devcon_mem = CreateCompatibleDC(hDC);
    _devcon_orig = hDC;
    _bmp_mem = CreateCompatibleBitmap(hDC, _width, _height);
}
在WM_PAINT中绘制时(在开始绘制等之后),执行位块传输到实际设备上下文:

void
TickerScreensaver::Paint(HDC hDC, RECT rect)
{
    _bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);

    FillRect(_devcon_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));

    if ( _gdiplus_token != NULL )
    {
        Graphics    graphics(_devcon_mem);
        SolidBrush  brush(cfg.display.font_colour); 
        FontFamily  font_family(cfg.display.font_family.c_str());
        Font        font(&font_family, cfg.display.font_size, FontStyleRegular, UnitPixel);
        PointF      point_f((f32)cfg.display.text_pos.x, (f32)cfg.display.text_pos.y);
        RectF       layout_rect(0, 0, 0, 0);
        RectF       bound_rect;

        graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);

        graphics.MeasureString(cfg.display.text.c_str(), cfg.display.text.length(), &font, layout_rect, &bound_rect);
        cfg.display.offset.x = (DWORD)(0 - bound_rect.Width);
        cfg.display.offset.y = (DWORD)(bound_rect.Height / 2);

        graphics.DrawString(cfg.display.text.c_str(), cfg.display.text.length(), &font, point_f, &brush);
    }

    BitBlt(hDC, 0, 0, _width, _height, _devcon_mem, 0, 0, SRCCOPY);

    SelectObject(_devcon_mem, _bmp_orig);
}
我这样计算尺寸:

void
TickerScreensaver::GetFullscreenRect(HDC hDC, RECT *rect)
{
    RECT    s = { 0, 0, 0, 0 };

    if ( EnumDisplayMonitors(hDC, NULL, EnumMonitorCallback, (LPARAM)&s) )
    {
        CopyRect(rect, &s);

        s.left < 0 ?
            _width = s.right + (0 + -s.left) :
            _width = s.right;

        s.top < 0 ?
            _height = s.bottom + (0 + -s.top) :
            _height = s.bottom;
    }
}
void
TickerScreensaver::GetFullscreenRect(HDC-HDC,RECT*RECT)
{
RECT s={0,0,0,0};
if(EnumDisplayMonitors(hDC、NULL、EnumMonitorCallback、(LPRAM)和s))
{
CopyRect(rect,&s);
s、 左<0?
_宽度=s.右+(0+-s.左):
_宽度=右s;
s、 顶部<0?
_高度=s.bottom+(0+-s.top):
_高度=s.底部;
}
}

请注意,计算的宽度、高度等均为100%准确;仅当图形代码位于右侧(将原点设置为{0,0},监视器#1为负值)时,它在主显示器上似乎不起作用。它也可以在tri显示器上复制,主屏幕位于中间。

事实证明它很好而且简单-在
Paint()
中,我们应该使用真实宽度和高度的rect,而不是检索到的包含负值的rect(实际从API函数检索到的rect):


一个小缺点:你似乎认为显示器的编号是从左到右分配的,但事实并非如此。我的正常设置是主显示器为#1且在右侧。我能告诉你的是,Windows按照检测监视器的顺序来分配监视器编号,仅此而已。非常正确,但我的测试一直在我的工作机器上进行,这些编号确实是“从左到右”分配您的
GetFullscreenRect
函数似乎过于复杂。为什么不能用
SM\u CXVIRTUALSCREEN
SM\u cvirtualscreen
调用
GetSystemMetrics
来获得相同的结果?
RECT r = { 0, 0, _width, _height }; 

_bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);

FillRect(_devcon_mem, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));