Pass CString to fprintf - c++

I have ran the code analyzer in visual studio on a large code base and i got about a billion of this error:
warning C6284: Object passed as parameter '3' when string is required in call to 'fprintf'
According to http://msdn.microsoft.com/en-us/library/ta308ywy.aspx "This defect might produce incorrect output or crashes." My colleague however states that we can just ignore all these errors without any problems. So one of my questions is do we need to do anything about this or can we just leave it as is?
If these errors need to be solved what is the nicest approach to solve it?
Would it work to do like this:
static_cast<const char*>(someCString)
Is there a better or more correct approach for this?
The following lines generate this warning:
CString str;
fprintf(pFile, "text %s", str);

I'm assuming that you're passing a Microsoft "CString" object to a printf()-family function where the corresponding format specifier is %s. If I'm right, then your answer is here: How can CString be passed to format string %s? (in short, your code is OK).
It seems that originally an implementation detail allowed CString to be passed directly to printf(), and later it was made part of the contract. So you're good to go as far as your program being correct, but if you want to avoid the static analysis warning, you may indeed need to use the static_cast to a char pointer. I'm not sure it's worth it here...maybe there's some other way to make these tools place nice together, since they're all from Microsoft.

Following the MSDN suggestions in C6284, you may cast the warnings away. Using C++ casts will be the most maintainable option to do this. Your example above would change to
fprintf(pFile, "text %s", static_cast<const TCHAR*>(str));
or, just another spelling of the same, to
fprintf(pFile, "text %s", static_cast<LPCTSTR>(str));
The most convincing option (100% cast-free, see Edits section) is
fprintf(pFile, "text %s", str.GetString());
Of course, following any of these change patterns will be a first porting step, and if nothing indicates a need for it, this may be harmful (not only for your team atmosphere).
Edits: (according to the comment of xMRi)
1) I added const because the argument is read-only for fprintf
2) notes to the cast-free solution CSimpleStringT::GetString: the CSimpleStringT class template is used for the definition of CStringT which again is used to typedef the class CString used in the original question
3) reworked answer to remove noise.
4) reduced the intro about the casting option

Technically speaking it is ok because the c-string is stored in such a way in CString that you can use it as stated but it is not good rely on how CString is implemented to do a shortcut. printf is a C-runtime function and knows nothing about C++ objects but here one is relying on an that the string is stored first in the CString - an implementation detail.
If I recall correctly originally CString could not be used that way and one had to cast the CString to a c-string to print it out but in later versions MS changed the implementation to allow for it to be treated as a c-string.
Another breaking issue is UNICODE, it will definitely not work if you one day decide to compile the program with UNICODE character set since even if you changed all string formatters to %ld, embedded 0s will sometimes prevent the string from being printed.
The actual problem is rather why are you using printf instead of C++ to print/write files?

Related

Handling "incompatibily" overloaded names in Cppcheck

I'm stuck with a "conflict" between with AnsiStrings sprintfmember function and Cppcheck's built-insprintf` knowledge.
In cases like this,
const char* name = "X";
int version = 1;
return AnsiString().sprintf("%s.H%02d", name, version); // <-- HERE
I'm getting this warning in the Cppcheck GUI
Id: wrongPrintfScanfArgNum
Summary: sprintf format string requires 0 parameters but 1 is given.
Message: sprintf format string requires 0 parameters but 1 is given.
which shows that Cppcheck is talking about the sprintf function, but I'm using a member function of the VCL class AnsiString with the same name.
As to get rid of this false positive, I could use
an inline suppression: // cppcheck-suppress wrongPrintfScanfArgNum
an intermediate variable: AnsiString result; result.printf(...); return result;
the sprintf function, which means handle the buffer space manually
But all these options work local, and make the code harder to read/maintain.
How can I teach Cppcheck to differentiate between overloaded names?
Edits:
I wrote override, but meant overloading, I corrected that in the current text.
added literal initialization of variables, which is important for name
Interesting.
Yes I agree this should be reported in http://trac.cppcheck.net. Looks like bugs.
I can see 2 bugs.
The AST does not show proper type information for the 'AnsiString()' even when I add a AnsiString class.
The Library should not match sprintf in that code. It is clear that some method is called.
It really is a bug.[1] Cppcheck 1.75 is smart in checking format strings, but obviously only in some cases, one of them is the second parameter of every function called printf, so the problem has nothing to do with AnsiString::sprintf but with every alternate implementation.
[1]
#7726 (False positive: format string checked for every function called 'sprintf') – Cppcheck

movefile() fails error 2 or 123

I'm updating a c++ routine to move files that was written in visual studio express 2008/2010. I'm now running VS Express 2012
Obviously there are changes to the compiler because string functions have to be upgraded to strcpy_s etc. No problem. This is a console app. I never extended my C++ knowledge past C++ to C# etc. as I need little more than to be able to write small utils to do things on the command line. Still I'm able to write somewhat complex utilities.
My issue is movefile() function always fails to move with either error 2 or 123. I'm working in C:\users\alan\downloads folder so I know I have permission. I know the file is there. Small snippet of code is:
char source=".\\test.txt"; // edited for clarity.
char dest=".\\test.txt1";
printf("\nMove\n %s\n to %s\n",source,dest); // just to see what is going on
MoveFile((LPCWSTR) source, (LPCWSTR) dest);
printf("Error %u\n",GetLastError());
output is :
Move
.\test.txt
to .\test.txt1
Error 2
All of my strings are simple char strings and I'm not exactly sure, even after reading, what LPCWSTR was type def'd for and if this is the culprit. So to get this to compile I simply typedef'd my strings. And it compiles. But still it won't move the files.
The code is more complex in developing the source & dest variables but I've reduce it to a simple "just append a 1 to the file name" situation to see if I can just simply rename it. I thought C:\xxx\yyy\zzz\test.txt was maybe wrong in some fashion but that idea fell though with the above test. I've done it with and without the .\ same issue. I'm running out of ideas other than making my own fileopen read/write binary function to replace movefile(). I'm really against that but if I have to I will.
EDIT: I pasted the printf from original code that used FullPathName, I've corrected the snippet.
The fact that you are casting your arguments to LPCWSTR suggests that you are compiling your program with UNICODE defined, which means you are calling MoveFileW and the compiler warned about an argument type mismatch.
Inserting a cast does not fix that. You are telling the compiler to stop complaining, but you haven't actually fixed the problem (the underlying data is still wrong).
Actual solutions:
Use WCHAR as MoveFileW expects (or TCHAR/LPTSTR and the _T macro).
Explicitly call MoveFileA
Compile without UNICODE defined.
Thanks Andrew Medico. I used MoveFileA and the program seems to work now.
I'm not sure I turned off unicode, but I did change one item in the properties.
I'll need to read up on the compiler about unicode/ansi settings. But for now the issue is fixed and I'm sure I've got the idea of what I need to do. "research"!!!!

sprint_f macro in Linux for cross-platform application

I'm porting an existing Windows application to Linux.
The most of the OS APIs\ Microsoft non-standard extension functions can be easily (more or less...) replaced by equivalent Linux\ GCC APIs, however, I don't know how to deal with sprintf_s which gets variable numbers of arguments.
Does anyone have an idea (If you can please put the code example as well) for that?
Thank you all in advance.
First, can you just port your code to use C++ iostreams instead (for example ostringstream)? This would completely remove all the possible issues with the sprintf line of functions, and if there are a limited number of call points is probably the best option.
If that isn't an option: The sprintf_s function is basically a helper to prevent mistakes (and external abuse to cause buffer overflows. From http://msdn.microsoft.com/en-us/library/ce3zzk1k%28VS.80%29.aspx we learn that it does two things: It checks the format string for valid formats (this doesn't mean it does type checking - it still can't do that), and it allows a max length to be specified.
The best replacement will be snprintf which does have limitations compared to sprintf_s. It won't do format string validation. And not all versions guarantee that the final string will be null terminated: You always want to also store a null into the last character of your buffer after the call to ensure that the final string is null terminated.
Add to end of your header file or beginning of source file:
#ifndef _WIN32
#define sprintf_s(dest,len,format,...) sprintf(dest,format,__VA_ARGS__)
#endif
snprintf has the same signature, but AFAIK it behaves in a slightly different way.
sprintf_s is just a "secure" version (takes buffer length as extra argument) of sprintf , cant you just use sprintf for your port ?
Why not just provide a conditionally compiled implementation of sprintf_s for Linux? This implementation could simply ignore the extra argument and call through to sprintf().

Issue with using std::copy

I am getting warning when using the std copy function.
I have a byte array that I declare.
byte *tstArray = new byte[length];
Then I have a couple other byte arrays that are declared and initialized with some hex values that i would like to use depending on some initial user input.
I have a series of if statements that I use to basically parse out the original input, and based on some string, I choose which byte array to use and in doing so copy the results to the original tstArray.
For example:
if(substr1 == "15")
{
std::cout<<"Using byte array rated 15"<<std::endl;
std::copy(ratedArray15,ratedArray15+length,tstArray);
}
The warning i get is
warning C4996: 'std::copy': Function call with parameters
that may be unsafe
- this call relies on the caller to check that the passed
values are correct.
A possible solution is to to disable this warning is by useing -D_SCL_SECURE_NO_WARNINGS, I think. Well, that is what I am researching.
But, I am not sure if this means that my code is really unsafe and I actually needed to do some checking?
C4996 means you're using a function that was marked as __declspec(deprecated). Probably using D_SCL_SECURE_NO_WARNINGS will just #ifdef out the deprecation. You could go read the header file to know for sure.
But the question is why is it deprecated? MSDN doesn't seem to say anything about it on the std::copy() page, but I may be looking at the wrong one. Typically this was done for all "unsafe string manipulation functions" during the great security push of XPSP2. Since you aren't passing the length of your destination buffer to std::copy, if you try to write too much data to it it will happily write past the end of the buffer.
To say whether or not your usage is unsafe would require us to review your entire code. Usually there is a safer version they recommend when they deprecate a function in this manner. You could just copy the strings in some other way. This article seems to go in depth. They seem to imply you should be using a std::checked_array_iterator instead of a regular OutputIterator.
Something like:
stdext::checked_array_iterator<char *> chkd_test_array(tstArray, length);
std::copy(ratedArray15, ratedArray15+length, chkd_test_array);
(If I understand your code right.)
Basically, what this warning tells you is that you have to be absolutely sure that tstArray points to an array that is large enough to hold "length" elements, as std::copy does not check that.
Well, I assume Microsoft's unilateral deprecation of the stdlib also includes passing char* to std::copy. (They've messed with a whole range of functions actually.)
I suppose parts of it has some merit (fopen() touches global ERRNO, so it's not thread-safe) but other decisions do not seem very rational. (I'd say they took a too big swathe at the whole thing. There should be levels, such as non-threadsafe, non-checkable, etc)
I'd recommend reading the MS-doc on each function if you want to know the issues about each case though, it's pretty well documented why each function has that warning, and the cause is usually different in each case.
At least it seems that VC++ 2010 RC does not emit that warning at the default warning level.

How to assign a value to a TCHAR array

I have a TCHAR array in my C++ code which I want to assign static strings to it.
I set an initial string to it via
TCHAR myVariable[260] = TEXT("initial value");
Everything works fine on this. However, when I split it in two lines as in
TCHAR myVariable[260];
myVariable = TEXT("initial value");
it bugs and gives a compiler error:
error C2440: '=': cannot convert from 'const char [14]' to 'TCHAR [260]'
shouldn't the TEXT() function do exactly what I want here? convert the given string to TCHARs? Why does it work, when putting the two lines together? What do I have to change in order to get it working?
Some other confusing thing I have encountered:
I've searched the internet for it and have seen that there are also _T() and _TEXT() and __T() and __TEXT(). What are they for? Which of them should I use in what environment?
The reason the assignment doesn't work has very little to do with TCHARs and _T. The following won't work either.
char var[260];
var = "str"; // fails
The reason is that in C and C++ you can't assign arrays directly. Instead, you have to copy the elements one by one (using, for example, strcpy, or in your case _tcscpy).
strcpy(var, "str");
Regarding the second part of your question, TEXT, _T and the others are macros, that in Unicode builds turn a string literal to a wide-string literal. In non-Unicode builds they do nothing.
See avakar's answer for the direct answer. I was going to add this as a comment, but it really is a freestanding recommendation. I will warn you up from that this will sound like a rant but it does come from using TCHAR and then working issues for a few years before trying to remove it from a rather large codebase.
Make sure that you really understand the effect of using TCHAR arrays and their friends. They are deceptively difficult to use correctly. Here is a short list of things that you have to watch out for:
sizeof(TCHAR) is conditional: Scrutinize code that contains this. If it is doing anything other than calculating the size that is being passed to malloc() or memcpy() then it is probably wrong.
TCHAR is a type alias: Since TCHAR is nothing more than a typedef, it is really easy to write things like wcscpy(tszStr, wszAry) and be none the wiser. Basically, TCHAR is either char or wchar_t so overload selections might surprise you.
wsprintf() and swprintf() are different: This is a special case of the previous but it bears special consideration since it is so easy to make a mistake here!
If you want to use TCHAR, then make sure that you compile both UNICODE and MBCS versions regularly. If you do not do this, then you are probably going to undo any advantage that you are trying to gain by using them in the first place.
Here are two recommendations that I have for how to not use TCHAR in the first place when you are writing C++ code.
Use CString if you are using MFC or are comfortable tying yourself to MSFT.
Use std::string or std::wstring in conjunction with the type-specific API - use CreateFileA() and CreateFileW() where appropriate.
Personally, I choose the latter but the character encoding and string transcoding issues are just a nightmare from time to time.