7

In my code (strict C, not C++), I use vsnprintf this way:

char* buf = NULL;
size_t sz;
sz = vsnprintf( buf, 0, format, args); // Ask vsnprintf how big a buffer we need

buf = (char*) malloc(sz + 1);
vsnprintf( buf, sz, format, args); // Now actually fill the buffer
/* Use buf in a dialog box... then: */
free(buf);

But MS Visual C++ (MSVS10) compiler warns:

warning C4996: 'vsnprintf': This function or variable may be unsafe. Consider using vsnprintf_s instead. 

However, vsnprintf_s does not have the nifty feature that when you pass NULL for the buffer it will describe how much data it would have printed. Instead, it is documented to return -1.

I feel I'm using vsnprintf in a safe manner by determining the necessary size, and that the recommended replacement, vsnprintf_s isn't the same at all.

Am I missing a better / smarter way to use vsnprintf_s??

abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 1
    You're in C++-land, stop using sprintf entirely. What you want for building strings dynamically is probably a std::stringstream – crowder Jun 27 '13 at 19:48
  • 4
    ["First of all, print the documentation about "safe/unsafe" functions from MSDN and burn it!"](http://stackoverflow.com/questions/2169016/mac-solution-for-safe-alternatives-to-unsafe-c-c-standard-library-function/2169107#2169107) - `vsnprintf()` is **not** deprecated, don't believe VS, it's ***crap.*** –  Jun 27 '13 at 19:49
  • @crowder: fair enough. My project actually is pure-C, but I happen to be using a C++ compiler at the moment. Changing the tags, and clarifying the question. – abelenky Jun 27 '13 at 19:50
  • 1
    vsnprintf can be misused very easily, it's not unreasonable to be wary of it. – crowder Jun 27 '13 at 19:50
  • According to the [documentation for VS2010](http://msdn.microsoft.com/en-us/library/1kt27hek(v=vs.100).aspx), `vsnprintf` invokes the invalid parameter handler if either the buffer or format arguments are `NULL`, or if count is less than or equal to zero (See discussion in [this question](http://stackoverflow.com/questions/8488671/unix-to-windows-alternative-to-vsnprintf-to-determine-length?rq=1)). I think there's a reasonable possibility that `vsnprintf_s` will have the behavior you want contrary to documentation - test it. – Casey Jun 27 '13 at 20:05
  • 1
    Unfortunately, the vsnprintf_s documentation is pretty inaccurate. It states that if the buffer is null or the count is 0, it will return -1 and set errno, but that's not exactly true. If you pass null for the buffer, 0 for the sizeOfBuffer, and 0 for the count, it will return 0 (provided you didn't pass null as the format string), and it will not set errno. Also, if you pass a real buffer and a real size but a count of 0, it will return -1 but not set errno. The documentation also states that the invalid parameter handler will be invoked if buffer is null or if count is 0. Also not true. 1/2 – Millie Smith Sep 04 '15 at 04:04
  • 1
    Neither of the workflows I described above will invoke the invalid parameter handler. The documentation states "if count <= 0". However, count is a size_t, which is typedefed as unsigned by the standard. Maybe they're looking to the future. *shrugs*. Also of note is that the MSDN documentation for vsnprintf (not _s) states that "If buffer or format is NULL, or if count is less than or equal to zero, these functions invoke the invalid parameter handler". Not true. You can pass it a null buffer and it will give you the required length (minus the null terminator) as the return value. 2/2 – Millie Smith Sep 04 '15 at 04:14
  • `vsnprintf` is not deprecated – M.M Sep 26 '17 at 20:56

4 Answers4

4

Turns out this question is pretty much an exact duplicate of:

Calculating the size of an sprintf() buffer

Summary of the answer:

Use _vscprintf to figure out how big the buffer should be, then use vsnprintf_s to actually fill it.

Community
  • 1
  • 1
abelenky
  • 63,815
  • 23
  • 109
  • 159
2

VC has finally implemented the standard vsnprintf. See the ever unreliable MSDN.

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • 2
    It seems any criticism of the big M is sanctioned by a downvote... +1 – chqrlie Sep 26 '17 at 21:36
  • I never said `vsnprintf` was not implemented. It was always implemented, but also marked as deprecated, and given a `C4996` warning. – abelenky Sep 26 '17 at 21:45
  • 1
    File a bug report with microsoft. `vsnprintf` is a standard function, and not deprecated by any actual authority. – Shahbaz Sep 27 '17 at 14:51
  • It was not me, but I think that downvote may have been earned by that "See the ever unreliable MSDN." remark. I've mostly skipped the last 10 years or so, but in the preceding decade (or so), MSDN used to be "the ever reliable" documentation, actually, if memory serves. Not error-free, but one of the better ones. – Sz. Aug 06 '19 at 21:25
  • @Sz. See the comments on the question, some inaccuracies are pointed out. MSDN is also full of MS nonesense written as if it's standard. The fact that they think they have a "more secure" version of it (yet until a few years ago their `vsnprintf` didn't even comply to C99) is another example. MSDN may have improved (it was garbage last I saw it), but it's still full of things that are non-standard, with little indication of what's standard and what's not (hence being unreliable). – Shahbaz Aug 09 '19 at 04:42
  • That `vsnprintf` saga is getting kinda old, and has been beaten to death millions of times. It was a problem (an overblown one, too), but is that all that's supposed to make MSDN "ever unreliable", or are there other serious examples, too? Note: MSDN is sometimes worse than some other (even ancient) servings of the MS docs, and may be annoyingly terse here and there, or lack crossrefs, and their legacy terminology (e.g. the "Unicode" stuff) can be ridiculously confusing occasionally, but I'm not sure your arguments are convincing. I'm kinda more convinced of your bias, if anything. – Sz. Aug 09 '19 at 17:58
  • @S.z, `vsnprintf` is the context of this question. I don't generally deal with WIN32, so I have little interaction with MSDN. Every time I did though, I was presented with some highly useless overview of things that never solved my problem. Once was to figure out why WIN32 Fonts that are created bold don't stay bold (on font copy? I don't remember), one was [this](https://stackoverflow.com/q/43899457/912144). Recently was to figure out an issue with [GetPixelFormat](https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getpixelformat). I could never rely on MSDN, hence unreliable – Shahbaz Aug 12 '19 at 04:59
0

I wouldn't say this is a duplicate question, as @abelenky suggests. If you want to use vsnzprintf_s in place of an old vsnprintf you need to pass _TRUNCATE (which expands to ((size_t)-1)) as the count argument (3rd parameter).

https://msdn.microsoft.com/en-us/library/d3xd30zz.aspx

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. If execution continues after the invalid parameter handler, these functions set buffer to an empty string, set errno to ERANGE, and return -1.

kornman00
  • 808
  • 10
  • 27
0

To get the buffer size you can do this:

size_t size = _vscprintf(format, argptr);

A good overview is available here vsnprintf vs vsnprintf_s . Essentially vsnprintf_s returns E_INVAL error if the buffer or format paramters are null pointers. If not null, vsnprintf_s writes up to the buffer size, truncating data that exceeds the size.

JayS
  • 2,057
  • 24
  • 16