Progressbar范围和位置,如何处理C ++中的大数字?

时间:2012-12-22 06:47:36

标签: c++ winapi

尝试实现Windows Progressbar显示大数字字节下载的进度,但无法正确执行。

如果我执行以下操作,下载2.5gb,则在下载完成后不会达到完整范围。

double dlSize = getDlSize();
unsigned int pbRange = (unsigned int)( dlSize / 3000 );
SendMessage( hProgressbar, PBM_SETRANGE, 0, MAKELPARAM( 0, pbRange ) );

然后在每个下载回调中设置新位置:

double dlBytes = bytesDownloaded();
unsigned int newIncrement = (unsigned int)( dlBytes / 3000 );
SendMessage( hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0 );

这是一个非常noobish的实现,我不希望陷入xy的情况,所以我的问题是实现一个大数字的进度条的正确方法是什么,以2-5GB的字节为单位?


我尝试了下面由@msandiford和@NikBougalis提出的两种方法,通过考虑进度条的宽度和使用百分比而不是实际数字,我甚至将两者结合起来,但在所有情况下,newIncrement总是出现0,也许那是因为dlSize总是更低(在new newIncrement中出现类似于1.15743e + 007,输入它和它的0)。

我还能做些什么?

结合两种方法的新规范:

编辑2: 为代码添加了一些检查,因为我一直在为newIncrement获得0,看起来它现在正在工作,不确定如何:

GetClientRect(hProgressbar, &pbRCClient);
pbWidth = pbRCClient.right - pbRCClient.left; // (pbWidth  its a global variable)
unsigned int pbRange = pbRCClient.right - pbRCClient.left;
SendMessage( hProgressbar, PBM_SETRANGE, 0, MAKELPARAM( 0, pbRange ) );

并在更新时:

double dlSize = getDlSize();
double doubleIncrement = ( ( dlSize * pbWidth ) / totalSize );

unsigned int newIncrement;

if ( (unsigned int)doubleIncrement < 1 )
{
    blockFill += doubleIncrement;

    if ( (unsigned int)blockFill > 1 )
    {
        newIncrement = ( unsigned int )blockFill;
        SendMessage( hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0 );
        blockFill = 0;
    }
}
else
{
    newIncrement = ( unsigned int )( doubleIncrement );
    SendMessage( hProgressbar, PBM_DELTAPOS, (WPARAM)newIncrement, 0 );
    //blockFill = 0;
}

编辑3:看起来它还在提前结束。

2 个答案:

答案 0 :(得分:3)

您遇到的最大问题是进度条控件本身的限制。 PBM_SETRANGE是有限的,虽然你可以使用PBM_SETRANGE32如果你需要处理大于2GB的值,你仍然会遇到问题。

巧合的是,为什么要使用双人?使用UINT64,其最大值大约为16,384PB(如果您正在下载会溢出的东西......请跳过进度条,它只会让您和您的客户感到压抑)。整数非常适合计算像字节这样的东西。

如果您知道要下载的文件的完整大小,那么解决具有有限最大范围的进度条大小的一种方法是使进度条从0开始并以100结束。您可以使用simple rule of three

将收到的字节转换为百分比
percent = (bytes_received * 100) / max_bytes;

如果您希望获得更多“粒度”,可以将进度条的比例更改为1000并相应地调整计算;您甚至可以转到10000但在此时根据控件的宽度(或高度),您可能会遇到显示器的分辨率。

答案 1 :(得分:2)

进度条可能比进度条窗口本身的像素数更准确。基于此,您应该能够非常轻松地将目标缩放到像素数。

    RECT rcClient;
    GetClientRect(hProgressBar, &rcClient);
    unsigned int pbRange = rcClient.right - rcClient.left;

    // Need to either keep unitsPerPixel, or recalculate later
    double pixelsPerUnit = pbRange / dlSize;
    SendMessage(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, pbRange));

然后更新进度将类似于:

    double dlBytes = totalBytesDownloaded();
    unsigned int newProgress = (unsigned int)(dlBytes * pixelsPerUnit);
    SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)newProgress, 0);

如果进度窗口以某种方式调整大小,则需要重新计算pbRange,重置进度条范围并重新计算unitsPerPixel以响应WM_SIZE消息。