6

I'm writing an Excel plugin, and need to generate wchar_t output for Excel (although internally, we are 100% char, and in fact limit char to plain ASCII). At one point, I'm using swprintf to do the conversion:

static wchar_t buffer[ 32369 ];
buffer[0] = swprintf( buffer + 1, sizeof(buffer) - 1, L"#%s!", message );

Excel displays some sort of CJK characters, although message (type char const*) is a null terminated character string with no characters outside of printable ASCII (hex values 0x20-0x7E).

I've tried this in a small test program, dumping in hex the generated string, and it looks like VC++ is treating message as if it were a wchar_t const* (although it seems to recognized the '\0' correctly, although it is on a single byte); this results in wchar_t with values like 0x6568 (rather than the 0x0068, 0x0065 that I was expecting).

According to the C99 standard, for a "%s" specifier, swprintf should convert the characters from the char const* "as if by repeated calls to the mbrtowc function[...]". Is the behavior I am seeing is an error in the Visual C++ library, or whether there is something in the global locale that I have to change?

(FWIW: when I compile and run my small test program with g++, I get the behavior I expect. G++ is not, however, an option for our Excel plugins, at least not at present.)

3
  • Did you run std::setlocale("") at the beginning of your program?
    – Kerrek SB
    Commented Sep 10, 2013 at 9:47
  • @KerrekSB I can't. I'm writing a plugin: I don't know what Excel has done, and I suspect that I can't modify the global locale in my plugin either. Commented Sep 10, 2013 at 9:56
  • 1
    Your length sizeof(buffer)-1 is too small by a factor sizeof(wchar_t). The length is a (wide) character count, not a size in bytes.
    – MSalters
    Commented Sep 10, 2013 at 12:04

2 Answers 2

15

Note that from swprintf of MSDN:

swprintf is a wide-character version of sprintf; the pointer arguments to swprintf are wide-character strings.

and then in the example:

wchar_t buf[100];
int len = swprintf( buf, 100, L"%s", L"Hello world" );

so at least Microsoft documented this.

And then in the page of format specifiers

s String When used with printf functions, specifies a single-byte–character string; when used with wprintf functions, specifies a wide-character string. Characters are printed up to the first null character or until the precision value is reached.

And then

S String When used with printf functions, specifies a wide-character string; when used with wprintf functions, specifies a single-byte–character string. Characters are printed up to the first null character or until the precision value is reached.

So what you want is upper-case %S.

See even this similar question: visual studio swprintf is making all my %s formatters want wchar_t * instead of char * where they suggest using %ls (always consider the parameter wchar_t*) and %hs (always consider the parameter char*)

7
  • OK. Just another case of Microsoft inventing their own standards. (They've been a lot better in this regard since they hired Herb. I guess this is just one left over.) In this case, however, it doesn't matter, since I'm pretty sure we'll never have to port the Excel plugin to Unix. So I'll just conform with what Microsoft wants. Commented Sep 10, 2013 at 10:01
  • And this will teach me to use the C standard as documentation, rather than Microsoft's own pages:-). Commented Sep 10, 2013 at 10:02
  • @JamesKanze If you think one minute, it was a logical way to do it for Microsoft, because they loved the _t* functions that work both for Unicode and SBCS and are declared through the use of macros (like, in this case, _stprintf)
    – xanatos
    Commented Sep 10, 2013 at 10:06
  • 1
    I'd not complain if _stprintf did something different; it's not standard, and it wouldn't even occur to me to use it. (On the other hand, the fact that function names like LoadLibrary are actually macros is a real pain.) But swprintf has a behavior specified by the C standard. Their modifications mean that it can't easily be used in portable code. (Of course, in this case, I'm only using it because I have to conform to the Excel C ABI, so portability isn't an issue.) Commented Sep 10, 2013 at 10:11
  • What's worse is that the documentation explicitly claims "swprintf conforms to the ISO C Standard". From the context it is clear that they do not mean that at all, but they do claim it nonetheless.
    – user743382
    Commented Sep 10, 2013 at 10:39
2

When calling swprintf the specifier %s is interpreted as pointing to a wide string, i.e. a wchar_t pointer. Instead use the %S (uppercase S) format specifier, as that will correctly use the char* message you are passing.

From Microsoft's documentation on printf Type Field Characters:

  • s, String, When used with printf functions, specifies a single-byte–character string; when used with wprintf functions, specifies a wide-character string. Characters are printed up to the first null character or until the precision value is reached.
  • S, String, When used with printf functions, specifies a wide-character string; when used with wprintf functions, specifies a single-byte–character string. Characters are printed up to the first null character or until the precision value is reached.

Not the answer you're looking for? Browse other questions tagged or ask your own question.