Sorry, I know this is really basic, but I don't know how to search it the proper way, so here we go.
I'm trying to call MessageBoxA, and I want the message to replace '%s' with something else. Example:
MessageBoxA(0, TEXT("You have %s items"), "title", 0);
Can anyone help me? And once again, I know this is really basic, sorry.
You have to build the string yourself. In C++, this is typically done with std::ostringstream, e.g.:
#include <sstream>
...
std::ostringstream message;
message << "You have " << numItems << " items";
MessageBoxA(NULL, message.str().c_str(), "title", MB_OK);
In C, this is typically done with snprintf(3):
#include <stdio.h>
...
char buffer[256]; // Make sure this is big enough
snprintf(buffer, sizeof(buffer), "You have %d items", numItems);
MessageBoxA(NULL, buffer, "title", MB_OK);
You can write a utility function to build a std::string from a printf-style format:
#include <cstdio>
#include <cstdarg>
#include <string>
#include <vector>
std::string build_string(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
size_t len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
std::vector<char> vec(len + 1);
va_start(args, fmt);
vsnprintf(vec.data(), len + 1, fmt, args);
va_end(args);
return std::string(vec.begin(), vec.end() - 1);
}
With this function in place, you can create arbitrary strings and pass pointers to their contents as downward-arguments:
MessageBoxA(0, build_string("You have %d items", item_count).c_str(), "title", 0);
It has the advantage of simplicity (several lines of code using only stdio without dependency on iostreams), and has no arbitrary limits on the size of string.
For MessageBoxA it is:
char szBuf[120];
snprintf(szBuf,120, "You have %s items",nItemCount);
MessageBoxA(0, szBuf, "title", 0);
It is ugly, but it will fit your need.
Use boost::format.
In your example: MessageBoxA(0, (boost::format("You have %1 items") % "title").c_str(), 0);
One advantage is that you don't need to remember all those %s codes anymore, another is that you're no longer limited by the set of built-in flags.
The ( ).c_str() is needed because MessageBoxA is a C interface, not C++, and c_str() converts a C++ string to a C string.
Related
I wonder whether there is a "better way" to supply SetWindowText "text" argument than this code:
wchar_t buffer[20];
Measures10nm.CIE_L = 100.3456f;
Measures10nm.CIE_a = -9.34f;
Measures10nm.CIE_b = -56.56f;
swprintf_s(buffer, 20, L"%.2f", Measures10nm.CIE_L);
SetWindowTextW(hEditCIE_L, buffer);
swprintf_s(buffer, 20, L"%.2f", Measures10nm.CIE_a);
SetWindowTextW(hEditCIE_a, buffer);
swprintf_s(buffer, 20, L"%.2f", Measures10nm.CIE_b);
SetWindowTextW(hEditCIE_b, buffer);
I tried to experiment with a function that I could supply a float to and who would return a wchar_t, since that's the type of argument SetTextWindow() requires but I have not been "successful" at it. I'm not even sure this is possible, technically, after all the time I experimented with various coding? Ideally, what I'd like to use is a function like this :
SetTextWindow(hEdit, floatToWchar_t(Measures10nm.CIE_L));
But I have not been able to code such a function?
I experimented with something along these lines :
wchar_t floatToWchar_t(float x)
{
wchar_t buffer[20];
swprintf_s(buffer, 20, L"%f", x);
return buffer;
}
But that does not work because wchar_t is an array, I suppose. I thought about using a pointer to the array but I can't conceptualize clearly how to do it.
Any help is appreciated. Please excuse the newbie question...
We're talking C++ here so forget about character arrays and use std::wstring and C++ streams.
#include <sstream>
std::wstringstream ss; // String-based stream.
float f = 3.14; // Our float.
ss << f; // Output the float to the stream.
SetWindowTextW(hWnd, ss.str().c_str()); // Covert to a `wchar_t` zero-terminated string.
You can easily wrap this in a function.
void SetWindowFloat(HWND hWnd, float f);
If you need to modify how the float is converted to a string, take a look at iomanip.
Modify for wstring ....
std::wstring floatToWideString(float x)
{
wchar_t buffer[20];
swprintf_s(buffer, 20, L"%f", x);
return buffer;
}
Then
void SetWindowText(HWND hwnd, const std::wstring& s)
{
SetWindowText(hwnd, s.c_str());
}
SetWindowText(hwnd, floatToWideString(3.1415f));
... but with all your calls to SetWindowText to the same hwnd, you're going to overwrite what's there....it's not like it is a line buffer.
Update:
Also, if you get so inclined to use use MFC, or just to use <atlstr.h>, there is a CString class that has a (LPCTSTR) cast operator.
Something like:
CStringW floatToWideCString(float x)
{
CStringW s;
s.Format(_T("%f"), x);
return s;
}
SetWindowText(hwnd, floatToWideCString(3.14));
Currently, I'm dealing with WMI for collecting the log files from a remote machine.
I have the username of the remote machine initialized as given below.
wchar_t pszName[]=L"My username";
pszName[] is a wchar_t array. The trouble I face is that when I pass my username as a parameter of string datatype, I need to initialize wchar_t array using a string.
I cannot use a wchar_t* pointer because it gives me an error on the later part of the program. I need to initialize something like
string username = "My username";
wchar_t pszName[] = .....?.....;
#include <string>
#include <vector>
#include <windows.h>
std::wstring str_to_wstr(std::string const & str)
{
int length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0);
if (!length)
return L"utf-8 to utf-16 conversion error!";
std::vector<wchar_t> buffer(length + 1);
if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), buffer.data(), length))
return L"utf-8 to utf-16 conversion error!";
return std::wstring(buffer.data(), length);
}
// ...
std::string username{ "My username" };
std::wstring utf_16_username{ str_to_wstr(username) };
// ... use:
utf_16_username.data(); // wherever you would have used pszName
You can use the std::mbstowcs function to convert your string into wchar_t*:
std::string username = "My username"; //set your username
wchar_t pszName[] = L"My username"; //initialize pszName with a certain length wide string
std::mbstowcs(pszName, name.c_str(), std::wcslen(pszName)); //copy and convert name from username to pszName
And you need to include:
#include <string>
#include <cstdlib>
Be aware, that you have to give a maximum length for pszName, and that this value actually has to have allocated at least this length in memory! Otherwise you get runtime crashes!
With the current implementation, you can just insert a dummy name of the right length into pszName and get the right length with the std::wcslen function.
I have the following functions:
void raiseError(const char *msg, ...)
{
va_list ap;
va_start(ap, msg); // use variable arg list
int size = vsnprintf(nullptr, 0, msg, ap);
std::vector<char> s(size+1, 0);
vsnprintf(s.data(), size, msg, ap);
va_end(ap);
errorString = std::string(s.data()));
}
When I call
raiseError("File not found in <%s> : <%s>", "a", "b" );
The first vsnprintf call (which computes the final string size) returns the correct value of 27. But the final string is:
"File not found in <**A*> :"
Where the '*' characters are random across program launches.
Also, the program works in MinGW, the issue is only seen with linux gcc.
What is wrong in my code?
You can't use ap more than once. Look into using va_copy.
I have the following log function:
template<typename... Arguments>
void Log(const char* file, const int line, int level, const char* fmt, Arguments... args)
{
std::string formattedFile;
if (file)
{
boost::filesystem::path p(file);
formattedFile = p.filename().string();
}
std::string message{boost::str(boost::format("%1%:%2% [%3%] - %s") % formattedFile % line % m_uxid % fmt)};
__android_log_print(level, m_tag.c_str(), message.c_str(), args...);
}
This application is run on Android using NDK, so this is the logging system for that platform. The problem is that __android_log_print() fails to compile with:
error: format not a string literal and no format arguments [-Werror=format-security]
__android_log_print(level, m_tag.c_str(), message.c_str(), std::forward<Arguments>(args)...);
^
I'm not sure what this means. Am I not using the variadic template argument correctly?
Untrusted input into printf can be a security problem. Enforcing the format by using a string literal is one way of improving security
Turning warnings into errors will cause the build to fail so you are forced to address the warning.
GCC's warning options have this to say
-Werror:
Make all warnings into errors.
-Wformat-security:
Warn about uses of format functions that represent possible security problems.
At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments
This may be a security hole if the format string came from untrusted input and contains %n.
What is generally advised is to create a std::string in your function and pass this with a %s format string literal to your logging function
__android_log_print(level, m_tag.c_str(), "%s", message.c_str());
Where message is built from processing args..., typically using something like boost::format or a std::stringstream.
If you want to use your provided fmt string and the variadic args, you can parse the arguments using a custom printf style function which produces a std::string
std::string va_string_printf(const char* format, va_list ap)
{
char stack_buf[256];
char* buf = stack_buf;
int buf_size = sizeof(stack_buf);
std::string out_str;
while(true)
{
va_list ap1;
va_copy(ap1, ap);
int min_buf_size = vsnprintf(buf, buf_size, format, ap1) + 1;
va_end(ap1);
if (min_buf_size > buf_size)
{
if (buf != stack_buf) // allocate a bigger buffer
delete[] buf;
buf = new char[min_buf_size];
buf_size = min_buf_size;
continue;
}
out_str = buf;
break;
}
if (buf != stack_buf)
delete[] buf;
return out_str;
}
std::string string_printf(const char* format, ...)
{
va_list ap;
va_start(ap, format);
std::string str = va_string_printf(format, ap);
va_end(ap);
return str;
}
The following code does not produce the expected output. Why?
wchar_t* wchar_t_printf_return(wchar_t* formatstring, ...){
va_list argp;
va_start(argp, formatstring);
int templen = 256;
templen = vsnwprintf(NULL, 0, formatstring, argp)+3;
wchar_t *buffer = (wchar_t *) malloc ((templen+1)*sizeof(wchar_t));
memset(buffer, 0, (templen+1)*sizeof(*buffer));
int retval;
while ((retval = vsnwprintf(buffer, templen, formatstring, argp)) == -1 || (retval >= (templen-1))){
templen = templen << 1;
buffer = (wchar_t *) realloc (buffer, (templen+1)*sizeof(wchar_t));
va_end(argp);
va_start(argp, formatstring);
}
va_end(argp);
buffer[templen] = L'\0';
return buffer;
}
int main(){
int i;
char *id = "2923BE84E16CD6AE529049F1F1BBE9EB";
wchar_t *val = wchar_t_printf_return(L"'%s'", id);
printf("%ls\n", val);
}
EDIT: to state more specifically, the printf in main should wrap the id in two single quotes thereby outputting: '2923BE84E16CD6AE529049F1F1BBE9EB'. The purpose of the main here is to illustrate the bug in the function, no more. The function is supposed to be an alternate of the printf family functions which return the result in a newly allocated buffer instead of a preexisting one. This is being run in cygwin compiled natively via gcc-3 with the -mno-cygwin option (aka mingw). Sorry for the confusion!
The %s specifier changes meaning depending on whether you are using a printf or wprintf family function. When used with a wprintf family function, the %s specifier indicates a wide string, but you're passing a narrow string. You need %hs to say "This is a narrow string."
(You seemed to be aware of this because you use %ls to print a wide string with a printf-family function, but you somehow forgot about it when going the other way.)
Try using %S for a translation, not %s.