The default C-RunTime Library (msvcrt.dll) on Vista seems to have %n disabled by default - security reasons of course (well, Google showed a few pages with devs complaining that their application don't work on vista because of that, but thats the price of security I guess). As is written on MSDN (see Security Note), tag %n can be enabled back again using the _set_printf_count_output function. And that would be a good solution for my little problem, but I've quite quickly learned that for some unknown reason this function is NOT exported by the msvcrt.dll which is in my system directory (it's the default one). What's more interesting, I didn't see the function being exported in msvcp60.dll, msvcp70.dll or msvcp71.dll either. However, it was exported in msvcp90.dll (I didn't check msvcp8X.dll).
So. I want to use %n, but I cannot, because my app uses the default msvcrt. How to solve this issue? There is a couple of solutions:
1) I should switch from using msvcrt.dll to msvcp90.dll. And that would solve the problem, however it would require me to distribute this DLL with my project, because it's not on the system by default.
2) I should find the function _set_printf_count_output in msvcrt (it's there, just not exported), and use it by the address. But what if somebody else has a different msvcrt? Signatures then! What if the signature doesn't fit? Well, this isn't the best way. However, just for tests, I've decided to implement the method with static addresses:
int (*_get_printf_count_output)(void) = (int(*)(void))((char*)GetModuleHandle("msvcrt.dll") + 0x6FCD8001 - 0x6FC60000);
int (*_set_printf_count_output)(int) = (int(*)(int)) ((char*)GetModuleHandle("msvcrt.dll") + 0x6FC750B5 - 0x6FC60000);
int n = 0;
Compile and run:
21:10:32 gynvael >gcc test.c
21:10:36 gynvael >a
It works! But it still sucks. So I've got back to msvcrt analysis looking for another way. And I've found out that _set_printf_count_output is called by something that is called by the DllMain, but, it's only called (with 1 in the argument) if the PE header of the executable file has Linker Version (MajorLinkerVersion and MinorLinkerVersion in OptionalHeader) set to 6.0 (interesting condition, I didn't knew that something used the linker field in PE). Anyway, as a test has shown, this actually works.
The code below changes the linker version to 6.0 (some error checks are missing of course):
Compile, and test on the previously mentioned executable:
21:10:36 gynvael >gcc fixprintf.c -o fixprintf.exe
21:17:13 gynvael >fixprintf.exe a.exe
21:17:15 gynvael >a.exe
And that's the end of Vista story for today. I'll just add that there has been a proposal to add -malt-ISO-printf option to GCC, which would make the app use printf from MinGW instead of the msvcrt one.
PS. I wonder what other things will happen when the linker version is set to 6.0. Oshogbo told me that switching linker to 6.0 does in fact break the app at his place when %n is used. Hmm, thats interesting. I'll look how come some other time.