使用安全 _vsnprintf_s 获取所需的缓冲区长度

Getting required buffer length with secure _vsnprintf_s

我正在尝试更新一些 "legacy" 代码以符合 MSVC 的最新安全更新,但在从 _vsnprintf 迁移到 _vsnprintf_s 时遇到了一些问题。

特别是,我在调用 _vsnprintf 时使用空缓冲区,count/length 为零,获取结果,分配所需大小的缓冲区 (return value + 1),然后然后使用新分配的缓冲区和已知正确的大小再次调用 _vsnprintf

size_t length = _vsntprintf(nullptr, 0, mask, params);
TCHAR *final = new TCHAR [length + 1];
_vsntprintf(final, length + 1, mask, params);

这种行为是documented on MSDN

If the buffer size specified by count is not sufficiently large to contain the output specified by format and argptr, the return value of vsnprintf is the number of characters that would be written if count were sufficiently large. If the return value is greater than count - 1, the output has been truncated.

我正尝试对 _vsnprintf_s 执行相同的操作,但 its documentation does not contain the same。它说

If the storage required to store the data and a terminating null exceeds sizeOfBuffer, the invalid parameter handler is invoked, as described in Parameter Validation, unless count is _TRUNCATE, in which case as much of the string as will fit in buffer is written and -1 returned.

无论如何都要尝试以下方法:

size_t length = _vsntprintf_s(nullptr, 0, 0, mask, params);

这导致 "length" 为零。如果您改为传入 _TRUNCATE (-1) 作为计数,则以下断言将失败:

Expression: buffer != nullptr && buffer_count > 0

我认为可以覆盖 _set_invalid_parameter_handler 并以某种方式找出长度应该是多少,但必须有更简单的方法吗?

size_t length = _vscprintf(mask, va_list);
TCHAR *final = new TCHAR [length + 1];
_vsntprintf_s(final, length, _TRUNCATE, mask, va_list);

滚动你自己的 vsnprintf 不 "violate the rules" 的变体如何获得长度:

int
printf_size(const char *fmt,int count,va_list ap)
{
    char buf[2000000];
    int len;

    len = vsnprintf_s(buf,sizeof(buf),count,fmt,ap);

    return len;
}

由于返回的 [很可能] 少于 sizeof(buf) 你应该没问题。

或者,执行:

int
printf_size(const char *fmt,int count,va_list ap)
{
    char *buf;
    int siz;
    int len;

    for (siz = 2000000;  ;  siz <<= 1) {
        buf = malloc(siz);
        len = vsnprintf_s(buf,siz,count,fmt,ap);
        free(buf);
        if (len < siz)
            break;
    }

    return len;
}

或者,执行一站式服务功能:

int
sprintf_secure(char **buf,const char *fmt,int count,va_list ap)
{
    char *bp;
    int siz;
    int len;

    for (siz = 2000000;  ;  siz <<= 1) {
        bp = malloc(siz);
        len = vsnprintf_s(bp,siz,count,fmt,ap);
        if (len < siz)
            break;
    }

    bp = realloc(bp,len + 1);

    *buf = bp;

    return len;
}